diff --git a/.htaccess b/.htaccess
new file mode 100644
index 00000000..87b9cf81
--- /dev/null
+++ b/.htaccess
@@ -0,0 +1,21 @@
+# Turn on URL rewriting
+RewriteEngine On
+
+# Installation directory
+RewriteBase /kohana/
+
+# Protect hidden files from being viewed
+
+ Order Deny,Allow
+ Deny From All
+
+
+# Protect application and system files from being viewed
+RewriteRule ^(?:application|modules|system)\b.* index.php/$0 [L]
+
+# Allow any files or directories that exist to be displayed directly
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteCond %{REQUEST_FILENAME} !-d
+
+# Rewrite all other URLs to index.php/URL
+RewriteRule .* index.php/$0 [PT]
diff --git a/application/bootstrap.php b/application/bootstrap.php
new file mode 100644
index 00000000..a76dfc07
--- /dev/null
+++ b/application/bootstrap.php
@@ -0,0 +1,102 @@
+ '/',
+));
+
+/**
+ * Attach the file write to logging. Multiple writers are supported.
+ */
+Kohana::$log->attach(new Kohana_Log_File(APPPATH.'logs'));
+
+/**
+ * Attach a file reader to config. Multiple readers are supported.
+ */
+Kohana::$config->attach(new Kohana_Config_File);
+
+/**
+ * Enable modules. Modules are referenced by a relative or absolute path.
+ */
+Kohana::modules(array(
+ // 'auth' => MODPATH.'auth', // Basic authentication
+ // 'cache' => MODPATH.'cache', // Caching with multiple backends
+ // 'codebench' => MODPATH.'codebench', // Benchmarking tool
+ // 'database' => MODPATH.'database', // Database access
+ // 'image' => MODPATH.'image', // Image manipulation
+ // 'orm' => MODPATH.'orm', // Object Relationship Mapping
+ // 'oauth' => MODPATH.'oauth', // OAuth authentication
+ // 'pagination' => MODPATH.'pagination', // Paging of results
+ // 'unittest' => MODPATH.'unittest', // Unit testing
+ // 'userguide' => MODPATH.'userguide', // User guide and API documentation
+ ));
+
+/**
+ * Set the routes. Each route must have a minimum of a name, a URI and a set of
+ * defaults for the URI.
+ */
+Route::set('default', '((/(/)))')
+ ->defaults(array(
+ 'controller' => 'welcome',
+ 'action' => 'index',
+ ));
+
+if ( ! defined('SUPPRESS_REQUEST'))
+{
+ /**
+ * Execute the main request. A source of the URI can be passed, eg: $_SERVER['PATH_INFO'].
+ * If no source is specified, the URI will be automatically detected.
+ */
+ echo Request::instance()
+ ->execute()
+ ->send_headers()
+ ->response;
+}
diff --git a/application/classes/controller/welcome.php b/application/classes/controller/welcome.php
new file mode 100644
index 00000000..e3b27d60
--- /dev/null
+++ b/application/classes/controller/welcome.php
@@ -0,0 +1,10 @@
+request->response = 'hello, world!';
+ }
+
+} // End Welcome
diff --git a/includes/kohana/LICENSE.md b/includes/kohana/LICENSE.md
new file mode 100644
index 00000000..87af9ad5
--- /dev/null
+++ b/includes/kohana/LICENSE.md
@@ -0,0 +1,14 @@
+# Kohana License Agreement
+
+This license is a legal agreement between you and the Kohana Team for the use of Kohana Framework (the "Software"). By obtaining the Software you agree to comply with the terms and conditions of this license.
+
+Copyright (c) 2007-2010 Kohana Team
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+* Neither the name of the Kohana nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/includes/kohana/README.md b/includes/kohana/README.md
new file mode 100644
index 00000000..97e31dc3
--- /dev/null
+++ b/includes/kohana/README.md
@@ -0,0 +1,3 @@
+# Kohana PHP Framework, version 3.0 (dev)
+
+This is the current development version of [Kohana](http://kohanaframework.org/).
diff --git a/includes/kohana/install.php b/includes/kohana/install.php
new file mode 100644
index 00000000..37d15300
--- /dev/null
+++ b/includes/kohana/install.php
@@ -0,0 +1,209 @@
+
+
+
+
+
+
+
+ Kohana Installation
+
+
+
+
+
+
+
Environment Tests
+
+
+ The following tests have been run to determine if Kohana will work in your environment.
+ If any of the tests have failed, consult the documentation
+ for more information on how to correct the problem.
+
+
+
+
+
+
+
PHP Version
+ =')): ?>
+
+
+
Kohana requires PHP 5.2.3 or newer, this version is .
+
+
+
+
System Directory
+
+
+
+
The configured system directory does not exist or does not contain required files.
+
+
+
+
Application Directory
+
+
+
+
The configured application directory does not exist or does not contain required files.
Kohana can use PDO to support additional databases.
+
+
+
+
+
+
diff --git a/includes/kohana/modules/auth/classes/auth.php b/includes/kohana/modules/auth/classes/auth.php
new file mode 100644
index 00000000..a02b1e5f
--- /dev/null
+++ b/includes/kohana/modules/auth/classes/auth.php
@@ -0,0 +1,3 @@
+get('driver'))
+ {
+ $type = 'ORM';
+ }
+
+ // Set the session class name
+ $class = 'Auth_'.ucfirst($type);
+
+ // Create a new session instance
+ Auth::$_instance = new $class($config);
+ }
+
+ return Auth::$_instance;
+ }
+
+ /**
+ * Create an instance of Auth.
+ *
+ * @return Auth
+ */
+ public static function factory($config = array())
+ {
+ return new Auth($config);
+ }
+
+ protected $_session;
+
+ protected $_config;
+
+ /**
+ * Loads Session and configuration options.
+ *
+ * @return void
+ */
+ public function __construct($config = array())
+ {
+ // Clean up the salt pattern and split it into an array
+ $config['salt_pattern'] = preg_split('/,\s*/', Kohana::config('auth')->get('salt_pattern'));
+
+ // Save the config in the object
+ $this->_config = $config;
+
+ $this->_session = Session::instance();
+ }
+
+ abstract protected function _login($username, $password, $remember);
+
+ abstract public function password($username);
+
+ abstract public function check_password($password);
+
+ /**
+ * Gets the currently logged in user from the session.
+ * Returns FALSE if no user is currently logged in.
+ *
+ * @return mixed
+ */
+ public function get_user()
+ {
+ return $this->_session->get($this->_config['session_key'], FALSE);
+ }
+
+ /**
+ * Attempt to log in a user by using an ORM object and plain-text password.
+ *
+ * @param string username to log in
+ * @param string password to check against
+ * @param boolean enable autologin
+ * @return boolean
+ */
+ public function login($username, $password, $remember = FALSE)
+ {
+ if (empty($password))
+ return FALSE;
+
+ if (is_string($password))
+ {
+ // Get the salt from the stored password
+ $salt = $this->find_salt($this->password($username));
+
+ // Create a hashed password using the salt from the stored password
+ $password = $this->hash_password($password, $salt);
+ }
+
+ return $this->_login($username, $password, $remember);
+ }
+
+ /**
+ * Log out a user by removing the related session variables.
+ *
+ * @param boolean completely destroy the session
+ * @param boolean remove all tokens for user
+ * @return boolean
+ */
+ public function logout($destroy = FALSE, $logout_all = FALSE)
+ {
+ if ($destroy === TRUE)
+ {
+ // Destroy the session completely
+ $this->_session->destroy();
+ }
+ else
+ {
+ // Remove the user from the session
+ $this->_session->delete($this->_config['session_key']);
+
+ // Regenerate session_id
+ $this->_session->regenerate();
+ }
+
+ // Double check
+ return ! $this->logged_in();
+ }
+
+ /**
+ * Check if there is an active session. Optionally allows checking for a
+ * specific role.
+ *
+ * @param string role name
+ * @return mixed
+ */
+ public function logged_in($role = NULL)
+ {
+ return FALSE !== $this->get_user();
+ }
+
+ /**
+ * Creates a hashed password from a plaintext password, inserting salt
+ * based on the configured salt pattern.
+ *
+ * @param string plaintext password
+ * @return string hashed password string
+ */
+ public function hash_password($password, $salt = FALSE)
+ {
+ if ($salt === FALSE)
+ {
+ // Create a salt seed, same length as the number of offsets in the pattern
+ $salt = substr($this->hash(uniqid(NULL, TRUE)), 0, count($this->_config['salt_pattern']));
+ }
+
+ // Password hash that the salt will be inserted into
+ $hash = $this->hash($salt.$password);
+
+ // Change salt to an array
+ $salt = str_split($salt, 1);
+
+ // Returned password
+ $password = '';
+
+ // Used to calculate the length of splits
+ $last_offset = 0;
+
+ foreach ($this->_config['salt_pattern'] as $offset)
+ {
+ // Split a new part of the hash off
+ $part = substr($hash, 0, $offset - $last_offset);
+
+ // Cut the current part out of the hash
+ $hash = substr($hash, $offset - $last_offset);
+
+ // Add the part to the password, appending the salt character
+ $password .= $part.array_shift($salt);
+
+ // Set the last offset to the current offset
+ $last_offset = $offset;
+ }
+
+ // Return the password, with the remaining hash appended
+ return $password.$hash;
+ }
+
+ /**
+ * Perform a hash, using the configured method.
+ *
+ * @param string string to hash
+ * @return string
+ */
+ public function hash($str)
+ {
+ return hash($this->_config['hash_method'], $str);
+ }
+
+ /**
+ * Finds the salt from a password, based on the configured salt pattern.
+ *
+ * @param string hashed password
+ * @return string
+ */
+ public function find_salt($password)
+ {
+ $salt = '';
+
+ foreach ($this->_config['salt_pattern'] as $i => $offset)
+ {
+ // Find salt characters, take a good long look...
+ $salt .= substr($password, $offset + $i, 1);
+ }
+
+ return $salt;
+ }
+
+ protected function complete_login($user)
+ {
+ // Regenerate session_id
+ $this->_session->regenerate();
+
+ // Store username in session
+ $this->_session->set($this->_config['session_key'], $user);
+
+ return TRUE;
+ }
+
+} // End Auth
\ No newline at end of file
diff --git a/includes/kohana/modules/auth/classes/kohana/auth/file.php b/includes/kohana/modules/auth/classes/kohana/auth/file.php
new file mode 100644
index 00000000..439f17fa
--- /dev/null
+++ b/includes/kohana/modules/auth/classes/kohana/auth/file.php
@@ -0,0 +1,88 @@
+_users = Arr::get($config, 'users', array());
+ }
+
+ /**
+ * Logs a user in.
+ *
+ * @param string username
+ * @param string password
+ * @param boolean enable autologin (not supported)
+ * @return boolean
+ */
+ protected function _login($username, $password, $remember)
+ {
+ if (isset($this->_users[$username]) AND $this->_users[$username] === $password)
+ {
+ // Complete the login
+ return $this->complete_login($username);
+ }
+
+ // Login failed
+ return FALSE;
+ }
+
+ /**
+ * Forces a user to be logged in, without specifying a password.
+ *
+ * @param mixed username
+ * @return boolean
+ */
+ public function force_login($username)
+ {
+ // Complete the login
+ return $this->complete_login($username);
+ }
+
+ /**
+ * Get the stored password for a username.
+ *
+ * @param mixed username
+ * @return string
+ */
+ public function password($username)
+ {
+ return Arr::get($this->_users, $username, FALSE);
+ }
+
+ /**
+ * Compare password with original (plain text). Works for current (logged in) user
+ *
+ * @param string $password
+ * @return boolean
+ */
+ public function check_password($password)
+ {
+ $username = $this->get_user();
+
+ if ($username === FALSE)
+ {
+ return FALSE;
+ }
+
+ return ($password === $this->password($username));
+ }
+
+} // End Auth File
\ No newline at end of file
diff --git a/includes/kohana/modules/auth/classes/kohana/auth/orm.php b/includes/kohana/modules/auth/classes/kohana/auth/orm.php
new file mode 100644
index 00000000..bd3decbb
--- /dev/null
+++ b/includes/kohana/modules/auth/classes/kohana/auth/orm.php
@@ -0,0 +1,288 @@
+get_user();
+
+ if (is_object($user) AND $user instanceof Model_User AND $user->loaded())
+ {
+ // Everything is okay so far
+ $status = TRUE;
+
+ if ( ! empty($role))
+ {
+ // Multiple roles to check
+ if (is_array($role))
+ {
+ // Check each role
+ foreach ($role as $_role)
+ {
+ if ( ! is_object($_role))
+ {
+ $_role = ORM::factory('role', array('name' => $_role));
+ }
+
+ // If the user doesn't have the role
+ if ( ! $user->has('roles', $_role))
+ {
+ // Set the status false and get outta here
+ $status = FALSE;
+ break;
+ }
+ }
+ }
+ // Single role to check
+ else
+ {
+ if ( ! is_object($role))
+ {
+ // Load the role
+ $role = ORM::factory('role', array('name' => $role));
+ }
+
+ // Check that the user has the given role
+ $status = $user->has('roles', $role);
+ }
+ }
+ }
+
+ return $status;
+ }
+
+ /**
+ * Logs a user in.
+ *
+ * @param string username
+ * @param string password
+ * @param boolean enable autologin
+ * @return boolean
+ */
+ protected function _login($user, $password, $remember)
+ {
+ if ( ! is_object($user))
+ {
+ $username = $user;
+
+ // Load the user
+ $user = ORM::factory('user');
+ $user->where($user->unique_key($username), '=', $username)->find();
+ }
+
+ // If the passwords match, perform a login
+ if ($user->has('roles', ORM::factory('role', array('name' => 'login'))) AND $user->password === $password)
+ {
+ if ($remember === TRUE)
+ {
+ // Create a new autologin token
+ $token = ORM::factory('user_token');
+
+ // Set token data
+ $token->user_id = $user->id;
+ $token->expires = time() + $this->_config['lifetime'];
+ $token->save();
+
+ // Set the autologin cookie
+ Cookie::set('authautologin', $token->token, $this->_config['lifetime']);
+ }
+
+ // Finish the login
+ $this->complete_login($user);
+
+ return TRUE;
+ }
+
+ // Login failed
+ return FALSE;
+ }
+
+ /**
+ * Forces a user to be logged in, without specifying a password.
+ *
+ * @param mixed username string, or user ORM object
+ * @param boolean mark the session as forced
+ * @return boolean
+ */
+ public function force_login($user, $mark_session_as_forced = FALSE)
+ {
+ if ( ! is_object($user))
+ {
+ $username = $user;
+
+ // Load the user
+ $user = ORM::factory('user');
+ $user->where($user->unique_key($username), '=', $username)->find();
+ }
+
+ if ($mark_session_as_forced === TRUE)
+ {
+ // Mark the session as forced, to prevent users from changing account information
+ $this->_session->set('auth_forced', TRUE);
+ }
+
+ // Run the standard completion
+ $this->complete_login($user);
+ }
+
+ /**
+ * Logs a user in, based on the authautologin cookie.
+ *
+ * @return mixed
+ */
+ public function auto_login()
+ {
+ if ($token = Cookie::get('authautologin'))
+ {
+ // Load the token and user
+ $token = ORM::factory('user_token', array('token' => $token));
+
+ if ($token->loaded() AND $token->user->loaded())
+ {
+ if ($token->user_agent === sha1(Request::$user_agent))
+ {
+ // Save the token to create a new unique token
+ $token->save();
+
+ // Set the new token
+ Cookie::set('authautologin', $token->token, $token->expires - time());
+
+ // Complete the login with the found data
+ $this->complete_login($token->user);
+
+ // Automatic login was successful
+ return $token->user;
+ }
+
+ // Token is invalid
+ $token->delete();
+ }
+ }
+
+ return FALSE;
+ }
+
+ /**
+ * Gets the currently logged in user from the session (with auto_login check).
+ * Returns FALSE if no user is currently logged in.
+ *
+ * @return mixed
+ */
+ public function get_user()
+ {
+ $user = parent::get_user();
+
+ if ($user === FALSE)
+ {
+ // check for "remembered" login
+ $user = $this->auto_login();
+ }
+
+ return $user;
+ }
+
+ /**
+ * Log a user out and remove any autologin cookies.
+ *
+ * @param boolean completely destroy the session
+ * @param boolean remove all tokens for user
+ * @return boolean
+ */
+ public function logout($destroy = FALSE, $logout_all = FALSE)
+ {
+ // Set by force_login()
+ $this->_session->delete('auth_forced');
+
+ if ($token = Cookie::get('authautologin'))
+ {
+ // Delete the autologin cookie to prevent re-login
+ Cookie::delete('authautologin');
+
+ // Clear the autologin token from the database
+ $token = ORM::factory('user_token', array('token' => $token));
+
+ if ($token->loaded() AND $logout_all)
+ {
+ ORM::factory('user_token')->where('user_id', '=', $token->user_id)->delete_all();
+ }
+ elseif ($token->loaded())
+ {
+ $token->delete();
+ }
+ }
+
+ return parent::logout($destroy);
+ }
+
+ /**
+ * Get the stored password for a username.
+ *
+ * @param mixed username string, or user ORM object
+ * @return string
+ */
+ public function password($user)
+ {
+ if ( ! is_object($user))
+ {
+ $username = $user;
+
+ // Load the user
+ $user = ORM::factory('user');
+ $user->where($user->unique_key($username), '=', $username)->find();
+ }
+
+ return $user->password;
+ }
+
+ /**
+ * Complete the login for a user by incrementing the logins and setting
+ * session data: user_id, username, roles.
+ *
+ * @param object user ORM object
+ * @return void
+ */
+ protected function complete_login($user)
+ {
+ $user->complete_login();
+
+ return parent::complete_login($user);
+ }
+
+ /**
+ * Compare password with original (hashed). Works for current (logged in) user
+ *
+ * @param string $password
+ * @return boolean
+ */
+ public function check_password($password)
+ {
+ $user = $this->get_user();
+
+ if ($user === FALSE)
+ {
+ // nothing to compare
+ return FALSE;
+ }
+
+ $hash = $this->hash_password($password, $this->find_salt($user->password));
+
+ return $hash == $user->password;
+ }
+
+} // End Auth ORM
\ No newline at end of file
diff --git a/includes/kohana/modules/auth/classes/model/auth/role.php b/includes/kohana/modules/auth/classes/model/auth/role.php
new file mode 100644
index 00000000..042f1419
--- /dev/null
+++ b/includes/kohana/modules/auth/classes/model/auth/role.php
@@ -0,0 +1,27 @@
+ array('through' => 'roles_users'));
+
+ // Validation rules
+ protected $_rules = array(
+ 'name' => array(
+ 'not_empty' => NULL,
+ 'min_length' => array(4),
+ 'max_length' => array(32),
+ ),
+ 'description' => array(
+ 'max_length' => array(255),
+ ),
+ );
+
+} // End Auth Role Model
\ No newline at end of file
diff --git a/includes/kohana/modules/auth/classes/model/auth/user.php b/includes/kohana/modules/auth/classes/model/auth/user.php
new file mode 100644
index 00000000..179391ba
--- /dev/null
+++ b/includes/kohana/modules/auth/classes/model/auth/user.php
@@ -0,0 +1,244 @@
+ array('model' => 'user_token'),
+ 'roles' => array('model' => 'role', 'through' => 'roles_users'),
+ );
+
+ // Validation rules
+ protected $_rules = array(
+ 'username' => array(
+ 'not_empty' => NULL,
+ 'min_length' => array(4),
+ 'max_length' => array(32),
+ 'regex' => array('/^[-\pL\pN_.]++$/uD'),
+ ),
+ 'password' => array(
+ 'not_empty' => NULL,
+ 'min_length' => array(5),
+ 'max_length' => array(42),
+ ),
+ 'password_confirm' => array(
+ 'matches' => array('password'),
+ ),
+ 'email' => array(
+ 'not_empty' => NULL,
+ 'min_length' => array(4),
+ 'max_length' => array(127),
+ 'email' => NULL,
+ ),
+ );
+
+ // Validation callbacks
+ protected $_callbacks = array(
+ 'username' => array('username_available'),
+ 'email' => array('email_available'),
+ );
+
+ // Field labels
+ protected $_labels = array(
+ 'username' => 'username',
+ 'email' => 'email address',
+ 'password' => 'password',
+ 'password_confirm' => 'password confirmation',
+ );
+
+ // Columns to ignore
+ protected $_ignored_columns = array('password_confirm');
+
+ /**
+ * Validates login information from an array, and optionally redirects
+ * after a successful login.
+ *
+ * @param array values to check
+ * @param string URI or URL to redirect to
+ * @return boolean
+ */
+ public function login(array & $array, $redirect = FALSE)
+ {
+ $fieldname = $this->unique_key($array['username']);
+ $array = Validate::factory($array)
+ ->label('username', $this->_labels[$fieldname])
+ ->label('password', $this->_labels['password'])
+ ->filter(TRUE, 'trim')
+ ->rules('username', $this->_rules[$fieldname])
+ ->rules('password', $this->_rules['password']);
+
+ // Get the remember login option
+ $remember = isset($array['remember']);
+
+ // Login starts out invalid
+ $status = FALSE;
+
+ if ($array->check())
+ {
+ // Attempt to load the user
+ $this->where($fieldname, '=', $array['username'])->find();
+
+ if ($this->loaded() AND Auth::instance()->login($this, $array['password'], $remember))
+ {
+ if (is_string($redirect))
+ {
+ // Redirect after a successful login
+ Request::instance()->redirect($redirect);
+ }
+
+ // Login is successful
+ $status = TRUE;
+ }
+ else
+ {
+ $array->error('username', 'invalid');
+ }
+ }
+
+ return $status;
+ }
+
+ /**
+ * Validates an array for a matching password and password_confirm field,
+ * and optionally redirects after a successful save.
+ *
+ * @param array values to check
+ * @param string URI or URL to redirect to
+ * @return boolean
+ */
+ public function change_password(array & $array, $redirect = FALSE)
+ {
+ $array = Validate::factory($array)
+ ->label('password', $this->_labels['password'])
+ ->label('password_confirm', $this->_labels['password_confirm'])
+ ->filter(TRUE, 'trim')
+ ->rules('password', $this->_rules['password'])
+ ->rules('password_confirm', $this->_rules['password_confirm']);
+
+ if ($status = $array->check())
+ {
+ // Change the password
+ $this->password = $array['password'];
+
+ if ($status = $this->save() AND is_string($redirect))
+ {
+ // Redirect to the success page
+ Request::instance()->redirect($redirect);
+ }
+ }
+
+ return $status;
+ }
+
+ /**
+ * Complete the login for a user by incrementing the logins and saving login timestamp
+ *
+ * @return void
+ */
+ public function complete_login()
+ {
+ if ( ! $this->_loaded)
+ {
+ // nothing to do
+ return;
+ }
+
+ // Update the number of logins
+ $this->logins = new Database_Expression('logins + 1');
+
+ // Set the last login date
+ $this->last_login = time();
+
+ // Save the user
+ $this->save();
+ }
+
+ /**
+ * Does the reverse of unique_key_exists() by triggering error if username exists.
+ * Validation callback.
+ *
+ * @param Validate Validate object
+ * @param string field name
+ * @return void
+ */
+ public function username_available(Validate $array, $field)
+ {
+ if ($this->unique_key_exists($array[$field], 'username'))
+ {
+ $array->error($field, 'username_available', array($array[$field]));
+ }
+ }
+
+ /**
+ * Does the reverse of unique_key_exists() by triggering error if email exists.
+ * Validation callback.
+ *
+ * @param Validate Validate object
+ * @param string field name
+ * @return void
+ */
+ public function email_available(Validate $array, $field)
+ {
+ if ($this->unique_key_exists($array[$field], 'email'))
+ {
+ $array->error($field, 'email_available', array($array[$field]));
+ }
+ }
+
+ /**
+ * Tests if a unique key value exists in the database.
+ *
+ * @param mixed the value to test
+ * @param string field name
+ * @return boolean
+ */
+ public function unique_key_exists($value, $field = NULL)
+ {
+ if ($field === NULL)
+ {
+ // Automatically determine field by looking at the value
+ $field = $this->unique_key($value);
+ }
+
+ return (bool) DB::select(array('COUNT("*")', 'total_count'))
+ ->from($this->_table_name)
+ ->where($field, '=', $value)
+ ->where($this->_primary_key, '!=', $this->pk())
+ ->execute($this->_db)
+ ->get('total_count');
+ }
+
+ /**
+ * Allows a model use both email and username as unique identifiers for login
+ *
+ * @param string unique value
+ * @return string field name
+ */
+ public function unique_key($value)
+ {
+ return Validate::email($value) ? 'email' : 'username';
+ }
+
+ /**
+ * Saves the current object. Will hash password if it was changed.
+ *
+ * @return ORM
+ */
+ public function save()
+ {
+ if (array_key_exists('password', $this->_changed))
+ {
+ $this->_object['password'] = Auth::instance()->hash_password($this->_object['password']);
+ }
+
+ return parent::save();
+ }
+
+} // End Auth User Model
\ No newline at end of file
diff --git a/includes/kohana/modules/auth/classes/model/auth/user/token.php b/includes/kohana/modules/auth/classes/model/auth/user/token.php
new file mode 100644
index 00000000..e7d820c0
--- /dev/null
+++ b/includes/kohana/modules/auth/classes/model/auth/user/token.php
@@ -0,0 +1,101 @@
+ array());
+
+ // Current timestamp
+ protected $_now;
+
+ /**
+ * Handles garbage collection and deleting of expired objects.
+ *
+ * @return void
+ */
+ public function __construct($id = NULL)
+ {
+ parent::__construct($id);
+
+ // Set the now, we use this a lot
+ $this->_now = time();
+
+ if (mt_rand(1, 100) === 1)
+ {
+ // Do garbage collection
+ $this->delete_expired();
+ }
+
+ if ($this->expires < $this->_now)
+ {
+ // This object has expired
+ $this->delete();
+ }
+ }
+
+ /**
+ * Overload saving to set the created time and to create a new token
+ * when the object is saved.
+ *
+ * @return ORM
+ */
+ public function save()
+ {
+ if ($this->loaded() === FALSE)
+ {
+ // Set the created time, token, and hash of the user agent
+ $this->created = $this->_now;
+ $this->user_agent = sha1(Request::$user_agent);
+ }
+
+ while (TRUE)
+ {
+ // Generate a new token
+ $this->token = $this->create_token();
+
+ try
+ {
+ return parent::save();
+ }
+ catch (Kohana_Database_Exception $e)
+ {
+ // Collision occurred, token is not unique
+ }
+ }
+ }
+
+ /**
+ * Deletes all expired tokens.
+ *
+ * @return ORM
+ */
+ public function delete_expired()
+ {
+ // Delete all expired tokens
+ DB::delete($this->_table_name)
+ ->where('expires', '<', $this->_now)
+ ->execute($this->_db);
+
+ return $this;
+ }
+
+ /**
+ * Generate a new unique token.
+ *
+ * @return string
+ * @uses Text::random
+ */
+ protected function create_token()
+ {
+ // Create a random token
+ return Text::random('alnum', 32);
+ }
+
+} // End Auth User Token Model
\ No newline at end of file
diff --git a/includes/kohana/modules/auth/classes/model/role.php b/includes/kohana/modules/auth/classes/model/role.php
new file mode 100644
index 00000000..983fa96e
--- /dev/null
+++ b/includes/kohana/modules/auth/classes/model/role.php
@@ -0,0 +1,7 @@
+ 'ORM',
+ 'hash_method' => 'sha1',
+ 'salt_pattern' => '1, 3, 5, 9, 14, 15, 20, 21, 28, 30',
+ 'lifetime' => 1209600,
+ 'session_key' => 'auth_user',
+
+ // Username/password combinations for the Auth File driver
+ 'users' => array(
+ // 'admin' => 'b3154acf3a344170077d11bdb5fff31532f679a1919e716a02',
+ ),
+
+);
\ No newline at end of file
diff --git a/includes/kohana/modules/auth/mysql.sql b/includes/kohana/modules/auth/mysql.sql
new file mode 100644
index 00000000..1c58ac18
--- /dev/null
+++ b/includes/kohana/modules/auth/mysql.sql
@@ -0,0 +1,48 @@
+CREATE TABLE IF NOT EXISTS `roles` (
+ `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
+ `name` varchar(32) NOT NULL,
+ `description` varchar(255) NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uniq_name` (`name`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+INSERT INTO `roles` (`id`, `name`, `description`) VALUES(1, 'login', 'Login privileges, granted after account confirmation');
+INSERT INTO `roles` (`id`, `name`, `description`) VALUES(2, 'admin', 'Administrative user, has access to everything.');
+
+CREATE TABLE IF NOT EXISTS `roles_users` (
+ `user_id` int(10) UNSIGNED NOT NULL,
+ `role_id` int(10) UNSIGNED NOT NULL,
+ PRIMARY KEY (`user_id`,`role_id`),
+ KEY `fk_role_id` (`role_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE IF NOT EXISTS `users` (
+ `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
+ `email` varchar(127) NOT NULL,
+ `username` varchar(32) NOT NULL DEFAULT '',
+ `password` char(50) NOT NULL,
+ `logins` int(10) UNSIGNED NOT NULL DEFAULT '0',
+ `last_login` int(10) UNSIGNED,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uniq_username` (`username`),
+ UNIQUE KEY `uniq_email` (`email`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE IF NOT EXISTS `user_tokens` (
+ `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
+ `user_id` int(11) UNSIGNED NOT NULL,
+ `user_agent` varchar(40) NOT NULL,
+ `token` varchar(32) NOT NULL,
+ `created` int(10) UNSIGNED NOT NULL,
+ `expires` int(10) UNSIGNED NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uniq_token` (`token`),
+ KEY `fk_user_id` (`user_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+ALTER TABLE `roles_users`
+ ADD CONSTRAINT `roles_users_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
+ ADD CONSTRAINT `roles_users_ibfk_2` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE;
+
+ALTER TABLE `user_tokens`
+ ADD CONSTRAINT `user_tokens_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE;
\ No newline at end of file
diff --git a/includes/kohana/modules/auth/postgresql.sql b/includes/kohana/modules/auth/postgresql.sql
new file mode 100644
index 00000000..9e192ba6
--- /dev/null
+++ b/includes/kohana/modules/auth/postgresql.sql
@@ -0,0 +1,53 @@
+CREATE TABLE roles
+(
+ id serial,
+ "name" varchar(32) NOT NULL,
+ description text NOT NULL,
+ CONSTRAINT roles_id_pkey PRIMARY KEY (id),
+ CONSTRAINT roles_name_key UNIQUE (name)
+);
+
+CREATE TABLE roles_users
+(
+ user_id integer,
+ role_id integer
+);
+
+CREATE TABLE users
+(
+ id serial,
+ email varchar(318) NOT NULL,
+ username varchar(32) NOT NULL,
+ "password" varchar(50) NOT NULL,
+ logins integer NOT NULL DEFAULT 0,
+ last_login integer,
+ CONSTRAINT users_id_pkey PRIMARY KEY (id),
+ CONSTRAINT users_username_key UNIQUE (username),
+ CONSTRAINT users_email_key UNIQUE (email),
+ CONSTRAINT users_logins_check CHECK (logins >= 0)
+);
+
+CREATE TABLE user_tokens
+(
+ id serial,
+ user_id integer NOT NULL,
+ user_agent varchar(40) NOT NULL,
+ token character varying(32) NOT NULL,
+ created integer NOT NULL,
+ expires integer NOT NULL,
+ CONSTRAINT user_tokens_id_pkey PRIMARY KEY (id),
+ CONSTRAINT user_tokens_token_key UNIQUE (token)
+);
+
+CREATE INDEX user_id_idx ON roles_users (user_id);
+CREATE INDEX role_id_idx ON roles_users (role_id);
+
+ALTER TABLE roles_users
+ ADD CONSTRAINT user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
+ ADD CONSTRAINT role_id_fkey FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE;
+
+ALTER TABLE user_tokens
+ ADD CONSTRAINT user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
+
+INSERT INTO roles (name, description) VALUES ('login', 'Login privileges, granted after account confirmation');
+INSERT INTO roles (name, description) VALUES ('admin', 'Administrative user, has access to everything.');
\ No newline at end of file
diff --git a/includes/kohana/modules/cache/README.md b/includes/kohana/modules/cache/README.md
new file mode 100644
index 00000000..8c486cb7
--- /dev/null
+++ b/includes/kohana/modules/cache/README.md
@@ -0,0 +1,60 @@
+Kohana Cache library
+====================
+
+The cache library for Kohana 3 provides a simple interface to the most common cache solutions. Developers are free to add their own caching solutions that follow the cache design pattern defined within this module.
+
+Supported cache solutions
+-------------------------
+
+Currently this module supports the following cache methods.
+
+1. APC
+2. eAccelerator
+3. Memcache
+4. Memcached-tags (Supports tags)
+5. SQLite (Supports tags)
+6. File
+7. Xcache
+
+Planned support
+---------------
+
+In the near future, additional support for the following methods will be included.
+
+1. Memcached
+
+Introduction to caching
+-----------------------
+
+To use caching to the maximum potential, your application should be designed with caching in mind from the outset. In general, the most effective caches contain lots of small collections of data that are the result of expensive computational operations, such as searching through a large data set.
+
+There are many different caching methods available for PHP, from the very basic file based caching to opcode caching in eAccelerator and APC. Caching engines that use physical memory over disk based storage are always faster, however many do not support more advanced features such as tagging.
+
+Using Cache
+-----------
+
+To use Kohana Cache, download and extract the latest stable release of Kohana Cache from [Github](http://github.com/samsoir/kohana-cache). Place the module into your Kohana instances modules folder. Finally enable the module within the application bootstrap within the section entitled _modules_.
+
+Quick example
+-------------
+
+The following is a quick example of how to use Kohana Cache. The example is using the SQLite driver.
+
+ 'bar', 'apples' => 'pear', 'BDFL' => 'Shadowhand');
+
+ // Save the data to cache, with an id of test_id and a lifetime of 10 minutes
+ $mycache->set('test_id', $data, 600);
+
+ // Retrieve the data from cache
+ $retrieved_data = $mycache->get('test_id');
+
+ // Remove the cache item
+ $mycache->delete('test_id');
+
+ // Clear the cache of all stored items
+ $mycache->delete_all();
diff --git a/includes/kohana/modules/cache/classes/cache.php b/includes/kohana/modules/cache/classes/cache.php
new file mode 100644
index 00000000..2b43c93b
--- /dev/null
+++ b/includes/kohana/modules/cache/classes/cache.php
@@ -0,0 +1,3 @@
+ array( // Default group
+ * 'driver' => 'memcache', // using Memcache driver
+ * 'servers' => array( // Available server definitions
+ * array(
+ * 'host' => 'localhost',
+ * 'port' => 11211,
+ * 'persistent' => FALSE
+ * )
+ * ),
+ * 'compression' => FALSE, // Use compression?
+ * ),
+ * )
+ *
+ * In cases where only one cache group is required, if the group is named `default` there is
+ * no need to pass the group name when instantiating a cache instance.
+ *
+ * #### General cache group configuration settings
+ *
+ * Below are the settings available to all types of cache driver.
+ *
+ * Name | Required | Description
+ * -------------- | -------- | ---------------------------------------------------------------
+ * driver | __YES__ | (_string_) The driver type to use
+ *
+ * Details of the settings specific to each driver are available within the drivers documentation.
+ *
+ * ### System requirements
+ *
+ * * Kohana 3.0.x
+ * * PHP 5.2.4 or greater
+ *
+ * @package Kohana
+ * @category Cache
+ * @version 2.0
+ * @author Kohana Team
+ * @copyright (c) 2009-2010 Kohana Team
+ * @license http://kohanaphp.com/license
+ */
+abstract class Kohana_Cache {
+
+ const DEFAULT_EXPIRE = 3600;
+
+ /**
+ * @var string default driver to use
+ */
+ public static $default = 'file';
+
+ /**
+ * @var Kohana_Cache instances
+ */
+ public static $instances = array();
+
+ /**
+ * Creates a singleton of a Kohana Cache group. If no group is supplied
+ * the __default__ cache group is used.
+ *
+ * // Create an instance of the default group
+ * $default_group = Cache::instance();
+ *
+ * // Create an instance of a group
+ * $foo_group = Cache::instance('foo');
+ *
+ * // Access an instantiated group directly
+ * $foo_group = Cache::$instances['default'];
+ *
+ * @param string the name of the cache group to use [Optional]
+ * @return Kohana_Cache
+ * @throws Kohana_Cache_Exception
+ */
+ public static function instance($group = NULL)
+ {
+ // If there is no group supplied
+ if ($group === NULL)
+ {
+ // Use the default setting
+ $group = Cache::$default;
+ }
+
+ if (isset(Cache::$instances[$group]))
+ {
+ // Return the current group if initiated already
+ return Cache::$instances[$group];
+ }
+
+ $config = Kohana::config('cache');
+
+ if ( ! $config->offsetExists($group))
+ {
+ throw new Kohana_Cache_Exception('Failed to load Kohana Cache group: :group', array(':group' => $group));
+ }
+
+ $config = $config->get($group);
+
+ // Create a new cache type instance
+ $cache_class = 'Cache_'.ucfirst($config['driver']);
+ Cache::$instances[$group] = new $cache_class($config);
+
+ // Return the instance
+ return Cache::$instances[$group];
+ }
+
+ /**
+ * @var Kohana_Config
+ */
+ protected $_config;
+
+ /**
+ * Ensures singleton pattern is observed, loads the default expiry
+ *
+ * @param array configuration
+ */
+ protected function __construct(array $config)
+ {
+ $this->_config = $config;
+ }
+
+ /**
+ * Overload the __clone() method to prevent cloning
+ *
+ * @return void
+ * @throws Kohana_Cache_Exception
+ */
+ public function __clone()
+ {
+ throw new Kohana_Cache_Exception('Cloning of Kohana_Cache objects is forbidden');
+ }
+
+ /**
+ * Retrieve a cached value entry by id.
+ *
+ * // Retrieve cache entry from default group
+ * $data = Cache::instance()->get('foo');
+ *
+ * // Retrieve cache entry from default group and return 'bar' if miss
+ * $data = Cache::instance()->get('foo', 'bar');
+ *
+ * // Retrieve cache entry from memcache group
+ * $data = Cache::instance('memcache')->get('foo');
+ *
+ * @param string id of cache to entry
+ * @param string default value to return if cache miss
+ * @return mixed
+ * @throws Kohana_Cache_Exception
+ */
+ abstract public function get($id, $default = NULL);
+
+ /**
+ * Set a value to cache with id and lifetime
+ *
+ * $data = 'bar';
+ *
+ * // Set 'bar' to 'foo' in default group, using default expiry
+ * Cache::instance()->set('foo', $data);
+ *
+ * // Set 'bar' to 'foo' in default group for 30 seconds
+ * Cache::instance()->set('foo', $data, 30);
+ *
+ * // Set 'bar' to 'foo' in memcache group for 10 minutes
+ * if (Cache::instance('memcache')->set('foo', $data, 600))
+ * {
+ * // Cache was set successfully
+ * return
+ * }
+ *
+ * @param string id of cache entry
+ * @param string data to set to cache
+ * @param integer lifetime in seconds
+ * @return boolean
+ */
+ abstract public function set($id, $data, $lifetime = 3600);
+
+ /**
+ * Delete a cache entry based on id
+ *
+ * // Delete 'foo' entry from the default group
+ * Cache::instance()->delete('foo');
+ *
+ * // Delete 'foo' entry from the memcache group
+ * Cache::instance('memcache')->delete('foo')
+ *
+ * @param string id to remove from cache
+ * @return boolean
+ */
+ abstract public function delete($id);
+
+ /**
+ * Delete all cache entries.
+ *
+ * Beware of using this method when
+ * using shared memory cache systems, as it will wipe every
+ * entry within the system for all clients.
+ *
+ * // Delete all cache entries in the default group
+ * Cache::instance()->delete_all();
+ *
+ * // Delete all cache entries in the memcache group
+ * Cache::instance('memcache')->delete_all();
+ *
+ * @return boolean
+ */
+ abstract public function delete_all();
+
+ /**
+ * Replaces troublesome characters with underscores.
+ *
+ * // Sanitize a cache id
+ * $id = $this->_sanitize_id($id);
+ *
+ * @param string id of cache to sanitize
+ * @return string
+ */
+ protected function _sanitize_id($id)
+ {
+ // Change slashes and spaces to underscores
+ return str_replace(array('/', '\\', ' '), '_', $id);
+ }
+}
+// End Kohana_Cache
\ No newline at end of file
diff --git a/includes/kohana/modules/cache/classes/kohana/cache/apc.php b/includes/kohana/modules/cache/classes/kohana/cache/apc.php
new file mode 100644
index 00000000..a6d33c84
--- /dev/null
+++ b/includes/kohana/modules/cache/classes/kohana/cache/apc.php
@@ -0,0 +1,133 @@
+ array( // Driver group
+ * 'driver' => 'apc', // using APC driver
+ * ),
+ * )
+ *
+ * In cases where only one cache group is required, if the group is named `default` there is
+ * no need to pass the group name when instantiating a cache instance.
+ *
+ * #### General cache group configuration settings
+ *
+ * Below are the settings available to all types of cache driver.
+ *
+ * Name | Required | Description
+ * -------------- | -------- | ---------------------------------------------------------------
+ * driver | __YES__ | (_string_) The driver type to use
+ *
+ * ### System requirements
+ *
+ * * Kohana 3.0.x
+ * * PHP 5.2.4 or greater
+ * * APC PHP extension
+ *
+ * @package Kohana
+ * @category Cache
+ * @author Kohana Team
+ * @copyright (c) 2009-2010 Kohana Team
+ * @license http://kohanaphp.com/license
+ */
+class Kohana_Cache_Apc extends Cache {
+
+ /**
+ * Check for existence of the APC extension This method cannot be invoked externally. The driver must
+ * be instantiated using the `Cache::instance()` method.
+ *
+ * @param array configuration
+ * @throws Kohana_Cache_Exception
+ */
+ protected function __construct(array $config)
+ {
+ if ( ! extension_loaded('apc'))
+ {
+ throw new Kohana_Cache_Exception('PHP APC extension is not available.');
+ }
+
+ parent::__construct($config);
+ }
+
+ /**
+ * Retrieve a cached value entry by id.
+ *
+ * // Retrieve cache entry from apc group
+ * $data = Cache::instance('apc')->get('foo');
+ *
+ * // Retrieve cache entry from apc group and return 'bar' if miss
+ * $data = Cache::instance('apc')->get('foo', 'bar');
+ *
+ * @param string id of cache to entry
+ * @param string default value to return if cache miss
+ * @return mixed
+ * @throws Kohana_Cache_Exception
+ */
+ public function get($id, $default = NULL)
+ {
+ return (($data = apc_fetch($this->_sanitize_id($id))) === FALSE) ? $default : $data;
+ }
+
+ /**
+ * Set a value to cache with id and lifetime
+ *
+ * $data = 'bar';
+ *
+ * // Set 'bar' to 'foo' in apc group, using default expiry
+ * Cache::instance('apc')->set('foo', $data);
+ *
+ * // Set 'bar' to 'foo' in apc group for 30 seconds
+ * Cache::instance('apc')->set('foo', $data, 30);
+ *
+ * @param string id of cache entry
+ * @param string data to set to cache
+ * @param integer lifetime in seconds
+ * @return boolean
+ */
+ public function set($id, $data, $lifetime = NULL)
+ {
+ if ($lifetime === NULL)
+ {
+ $lifetime = Arr::get($this->_config, 'default_expire', Cache::DEFAULT_EXPIRE);
+ }
+
+ return apc_store($this->_sanitize_id($id), $data, $lifetime);
+ }
+
+ /**
+ * Delete a cache entry based on id
+ *
+ * // Delete 'foo' entry from the apc group
+ * Cache::instance('apc')->delete('foo');
+ *
+ * @param string id to remove from cache
+ * @return boolean
+ */
+ public function delete($id)
+ {
+ return apc_delete($this->_sanitize_id($id));
+ }
+
+ /**
+ * Delete all cache entries.
+ *
+ * Beware of using this method when
+ * using shared memory cache systems, as it will wipe every
+ * entry within the system for all clients.
+ *
+ * // Delete all cache entries in the apc group
+ * Cache::instance('apc')->delete_all();
+ *
+ * @return boolean
+ */
+ public function delete_all()
+ {
+ return apc_clear_cache('user');
+ }
+}
\ No newline at end of file
diff --git a/includes/kohana/modules/cache/classes/kohana/cache/eaccelerator.php b/includes/kohana/modules/cache/classes/kohana/cache/eaccelerator.php
new file mode 100644
index 00000000..7abcd775
--- /dev/null
+++ b/includes/kohana/modules/cache/classes/kohana/cache/eaccelerator.php
@@ -0,0 +1,133 @@
+ array( // Driver group
+ * 'driver' => 'eaccelerator', // using Eaccelerator driver
+ * ),
+ * )
+ *
+ * In cases where only one cache group is required, if the group is named `default` there is
+ * no need to pass the group name when instantiating a cache instance.
+ *
+ * #### General cache group configuration settings
+ *
+ * Below are the settings available to all types of cache driver.
+ *
+ * Name | Required | Description
+ * -------------- | -------- | ---------------------------------------------------------------
+ * driver | __YES__ | (_string_) The driver type to use
+ *
+ * ### System requirements
+ *
+ * * Kohana 3.0.x
+ * * PHP 5.2.4 or greater
+ * * Eaccelerator PHP extension
+ *
+ * @package Kohana
+ * @category Cache
+ * @author Kohana Team
+ * @copyright (c) 2009-2010 Kohana Team
+ * @license http://kohanaphp.com/license
+ */
+class Kohana_Cache_Eaccelerator extends Cache {
+
+ /**
+ * Check for existence of the eAccelerator extension This method cannot be invoked externally. The driver must
+ * be instantiated using the `Cache::instance()` method.
+ *
+ * @param array configuration
+ * @throws Kohana_Cache_Exception
+ */
+ protected function __construct(array $config)
+ {
+ if ( ! extension_loaded('eaccelerator'))
+ {
+ throw new Kohana_Cache_Exception('PHP eAccelerator extension is not available.');
+ }
+
+ parent::__construct($config);
+ }
+
+ /**
+ * Retrieve a cached value entry by id.
+ *
+ * // Retrieve cache entry from eaccelerator group
+ * $data = Cache::instance('eaccelerator')->get('foo');
+ *
+ * // Retrieve cache entry from eaccelerator group and return 'bar' if miss
+ * $data = Cache::instance('eaccelerator')->get('foo', 'bar');
+ *
+ * @param string id of cache to entry
+ * @param string default value to return if cache miss
+ * @return mixed
+ * @throws Kohana_Cache_Exception
+ */
+ public function get($id, $default = NULL)
+ {
+ return (($data = eaccelerator_get($this->_sanitize_id($id))) === FALSE) ? $default : $data;
+ }
+
+ /**
+ * Set a value to cache with id and lifetime
+ *
+ * $data = 'bar';
+ *
+ * // Set 'bar' to 'foo' in eaccelerator group, using default expiry
+ * Cache::instance('eaccelerator')->set('foo', $data);
+ *
+ * // Set 'bar' to 'foo' in eaccelerator group for 30 seconds
+ * Cache::instance('eaccelerator')->set('foo', $data, 30);
+ *
+ * @param string id of cache entry
+ * @param string data to set to cache
+ * @param integer lifetime in seconds
+ * @return boolean
+ */
+ public function set($id, $data, $lifetime = NULL)
+ {
+ if ($lifetime === NULL)
+ {
+ $lifetime = time() + Arr::get($this->_config, 'default_expire', Cache::DEFAULT_EXPIRE);
+ }
+
+ return eaccelerator_put($this->_sanitize_id($id), $data, $lifetime);
+ }
+
+ /**
+ * Delete a cache entry based on id
+ *
+ * // Delete 'foo' entry from the eaccelerator group
+ * Cache::instance('eaccelerator')->delete('foo');
+ *
+ * @param string id to remove from cache
+ * @return boolean
+ */
+ public function delete($id)
+ {
+ return eaccelerator_rm($this->_sanitize_id($id));
+ }
+
+ /**
+ * Delete all cache entries.
+ *
+ * Beware of using this method when
+ * using shared memory cache systems, as it will wipe every
+ * entry within the system for all clients.
+ *
+ * // Delete all cache entries in the eaccelerator group
+ * Cache::instance('eaccelerator')->delete_all();
+ *
+ * @return boolean
+ */
+ public function delete_all()
+ {
+ return eaccelerator_clean();
+ }
+}
\ No newline at end of file
diff --git a/includes/kohana/modules/cache/classes/kohana/cache/exception.php b/includes/kohana/modules/cache/classes/kohana/cache/exception.php
new file mode 100644
index 00000000..f8677e78
--- /dev/null
+++ b/includes/kohana/modules/cache/classes/kohana/cache/exception.php
@@ -0,0 +1,3 @@
+ array( // File driver group
+ * 'driver' => 'file', // using File driver
+ * 'cache_dir' => APPPATH.'cache/.kohana_cache', // Cache location
+ * ),
+ * )
+ *
+ * In cases where only one cache group is required, if the group is named `default` there is
+ * no need to pass the group name when instantiating a cache instance.
+ *
+ * #### General cache group configuration settings
+ *
+ * Below are the settings available to all types of cache driver.
+ *
+ * Name | Required | Description
+ * -------------- | -------- | ---------------------------------------------------------------
+ * driver | __YES__ | (_string_) The driver type to use
+ * cache_dir | __NO__ | (_string_) The cache directory to use for this cache instance
+ *
+ * ### System requirements
+ *
+ * * Kohana 3.0.x
+ * * PHP 5.2.4 or greater
+ *
+ * @package Kohana
+ * @category Cache
+ * @author Kohana Team
+ * @copyright (c) 2009-2010 Kohana Team
+ * @license http://kohanaphp.com/license
+ */
+class Kohana_Cache_File extends Cache implements Kohana_Cache_GarbageCollect {
+
+ // !!! NOTICE !!!
+ // THIS CONSTANT IS USED BY THE FILE CACHE CLASS
+ // INTERNALLY. USE THE CONFIGURATION FILE TO
+ // REDEFINE THE CACHE DIRECTORY.
+ const CACHE_DIR = 'cache/.kohana_cache';
+
+ /**
+ * Creates a hashed filename based on the string. This is used
+ * to create shorter unique IDs for each cache filename.
+ *
+ * // Create the cache filename
+ * $filename = Cache_File::filename($this->_sanitize_id($id));
+ *
+ * @param string string to hash into filename
+ * @return string
+ */
+ protected static function filename($string)
+ {
+ return sha1($string).'.json';
+ }
+
+ /**
+ * @var string the caching directory
+ */
+ protected $_cache_dir;
+
+ /**
+ * Constructs the file cache driver. This method cannot be invoked externally. The file cache driver must
+ * be instantiated using the `Cache::instance()` method.
+ *
+ * @param array config
+ * @throws Kohana_Cache_Exception
+ */
+ protected function __construct(array $config)
+ {
+ // Setup parent
+ parent::__construct($config);
+
+ try
+ {
+ $directory = Arr::get($this->_config, 'cache_dir', APPPATH.Cache_File::CACHE_DIR);
+ $this->_cache_dir = new RecursiveDirectoryIterator($directory);
+ }
+ catch (UnexpectedValueException $e)
+ {
+ if ( ! mkdir($directory, 0777, TRUE))
+ {
+ throw new Kohana_Cache_Exception('Failed to create the defined cache directory : :directory', array(':directory' => $directory));
+ }
+ chmod($directory, 0777);
+ $this->_cache_dir = new RecursiveDirectoryIterator($directory);
+ }
+
+ // If the defined directory is a file, get outta here
+ if ($this->_cache_dir->isFile())
+ {
+ throw new Kohana_Cache_Exception('Unable to create cache directory as a file already exists : :resource', array(':resource' => $this->_cache_dir->getRealPath()));
+ }
+
+ // Check the read status of the directory
+ if ( ! $this->_cache_dir->isReadable())
+ {
+ throw new Kohana_Cache_Exception('Unable to read from the cache directory :resource', array(':resource' => $this->_cache_dir->getRealPath()));
+ }
+
+ // Check the write status of the directory
+ if ( ! $this->_cache_dir->isWritable())
+ {
+ throw new Kohana_Cache_Exception('Unable to write to the cache directory :resource', array(':resource' => $this->_cache_dir->getRealPath()));
+ }
+ }
+
+ /**
+ * Retrieve a cached value entry by id.
+ *
+ * // Retrieve cache entry from file group
+ * $data = Cache::instance('file')->get('foo');
+ *
+ * // Retrieve cache entry from file group and return 'bar' if miss
+ * $data = Cache::instance('file')->get('foo', 'bar');
+ *
+ * @param string id of cache to entry
+ * @param string default value to return if cache miss
+ * @return mixed
+ * @throws Kohana_Cache_Exception
+ */
+ public function get($id, $default = NULL)
+ {
+ $filename = Cache_File::filename($this->_sanitize_id($id));
+ $directory = $this->_resolve_directory($filename);
+
+ // Wrap operations in try/catch to handle notices
+ try
+ {
+ // Open file
+ $file = new SplFileInfo($directory.$filename);
+
+ // If file does not exist
+ if ( ! $file->getRealPath())
+ {
+ // Return default value
+ return $default;
+ }
+ else
+ {
+ // Open the file and extract the json
+ $json = $file->openFile()->current();
+
+ // Decode the json into PHP object
+ $data = json_decode($json);
+
+ // Test the expiry
+ if ($data->expiry < time())
+ {
+ // Delete the file
+ $this->_delete_file($file, NULL, TRUE);
+
+ // Return default value
+ return $default;
+ }
+ else
+ {
+ return ($data->type === 'string') ? $data->payload : unserialize($data->payload);
+ }
+ }
+
+ }
+ catch (ErrorException $e)
+ {
+ // Handle ErrorException caused by failed unserialization
+ if ($e->getCode() === E_NOTICE)
+ {
+ throw new Kohana_Cache_Exception(__METHOD__.' failed to unserialize cached object with message : '.$e->getMessage());
+ }
+
+ // Otherwise throw the exception
+ throw $e;
+ }
+ }
+
+ /**
+ * Set a value to cache with id and lifetime
+ *
+ * $data = 'bar';
+ *
+ * // Set 'bar' to 'foo' in file group, using default expiry
+ * Cache::instance('file')->set('foo', $data);
+ *
+ * // Set 'bar' to 'foo' in file group for 30 seconds
+ * Cache::instance('file')->set('foo', $data, 30);
+ *
+ * @param string id of cache entry
+ * @param string data to set to cache
+ * @param integer lifetime in seconds
+ * @return boolean
+ */
+ public function set($id, $data, $lifetime = NULL)
+ {
+ $filename = Cache_File::filename($this->_sanitize_id($id));
+ $directory = $this->_resolve_directory($filename);
+
+ // If lifetime is NULL
+ if ($lifetime === NULL)
+ {
+ // Set to the default expiry
+ $lifetime = Arr::get($this->_config, 'default_expire', Cache::DEFAULT_EXPIRE);
+ }
+
+ // Open directory
+ $dir = new SplFileInfo($directory);
+
+ // If the directory path is not a directory
+ if ( ! $dir->isDir())
+ {
+ // Create the directory
+ if ( ! mkdir($directory, 0777, TRUE))
+ {
+ throw new Kohana_Cache_Exception(__METHOD__.' unable to create directory : :directory', array(':directory' => $directory));
+ }
+
+ // chmod to solve potential umask issues
+ chmod($directory, 0777);
+ }
+
+ // Open file to inspect
+ $resouce = new SplFileInfo($directory.$filename);
+ $file = $resouce->openFile('w');
+
+ try
+ {
+ $type = gettype($data);
+
+ // Serialize the data
+ $data = json_encode((object) array(
+ 'payload' => ($type === 'string') ? $data : serialize($data),
+ 'expiry' => time() + $lifetime,
+ 'type' => $type
+ ));
+
+ $size = strlen($data);
+ }
+ catch (ErrorException $e)
+ {
+ // If serialize through an error exception
+ if ($e->getCode() === E_NOTICE)
+ {
+ // Throw a caching error
+ throw new Kohana_Cache_Exception(__METHOD__.' failed to serialize data for caching with message : '.$e->getMessage());
+ }
+
+ // Else rethrow the error exception
+ throw $e;
+ }
+
+ try
+ {
+ $file->fwrite($data, $size);
+ return (bool) $file->fflush();
+ }
+ catch (Exception $e)
+ {
+ throw $e;
+ }
+ }
+
+ /**
+ * Delete a cache entry based on id
+ *
+ * // Delete 'foo' entry from the file group
+ * Cache::instance('file')->delete('foo');
+ *
+ * @param string id to remove from cache
+ * @return boolean
+ */
+ public function delete($id)
+ {
+ $filename = Cache_File::filename($this->_sanitize_id($id));
+ $directory = $this->_resolve_directory($filename);
+
+ return $this->_delete_file(new SplFileInfo($directory.$filename), NULL, TRUE);
+ }
+
+ /**
+ * Delete all cache entries.
+ *
+ * Beware of using this method when
+ * using shared memory cache systems, as it will wipe every
+ * entry within the system for all clients.
+ *
+ * // Delete all cache entries in the file group
+ * Cache::instance('file')->delete_all();
+ *
+ * @return boolean
+ */
+ public function delete_all()
+ {
+ return $this->_delete_file($this->_cache_dir, TRUE);
+ }
+
+ /**
+ * Garbage collection method that cleans any expired
+ * cache entries from the cache.
+ *
+ * @return void
+ */
+ public function garbage_collect()
+ {
+ $this->_delete_file($this->_cache_dir, TRUE, FALSE, TRUE);
+ return;
+ }
+
+ /**
+ * Deletes files recursively and returns FALSE on any errors
+ *
+ * // Delete a file or folder whilst retaining parent directory and ignore all errors
+ * $this->_delete_file($folder, TRUE, TRUE);
+ *
+ * @param SplFileInfo file
+ * @param boolean retain the parent directory
+ * @param boolean ignore_errors to prevent all exceptions interrupting exec
+ * @param boolean only expired files
+ * @return boolean
+ * @throws Kohana_Cache_Exception
+ */
+ protected function _delete_file(SplFileInfo $file, $retain_parent_directory = FALSE, $ignore_errors = FALSE, $only_expired = FALSE)
+ {
+ // Allow graceful error handling
+ try
+ {
+ // If is file
+ if ($file->isFile())
+ {
+ try
+ {
+ // If only expired is not set
+ if ($only_expired === FALSE)
+ {
+ // We want to delete the file
+ $delete = TRUE;
+ }
+ // Otherwise...
+ else
+ {
+ // Assess the file expiry to flag it for deletion
+ $json = $file->openFile('r')->current();
+ $data = json_decode($json);
+ $delete = $data->expiry < time();
+ }
+
+ // If the delete flag is set
+ if ($delete === TRUE)
+ {
+ // Try to delete
+ unlink($file->getRealPath());
+ }
+ }
+ catch (ErrorException $e)
+ {
+ // Catch any delete file warnings
+ if ($e->getCode() === E_WARNING)
+ {
+ throw new Kohana_Cache_Exception(__METHOD__.' failed to delete file : :file', array(':file' => $file->getRealPath()));
+ }
+ }
+ }
+ // Else, is directory
+ else if ($file->isDir())
+ {
+ // Create new DirectoryIterator
+ $files = new DirectoryIterator($file->getPathname());
+
+ // Iterate over each entry
+ while ($files->valid())
+ {
+ // Extract the entry name
+ $name = $files->getFilename();
+
+ // If the name is not a dot
+ if ($name != '.' and $name != '..')
+ {
+ // Create new file resource
+ $fp = new SplFileInfo($files->getRealPath());
+ // Delete the file
+ $this->_delete_file($fp);
+ }
+
+ // Move the file pointer on
+ $files->next();
+ }
+
+ // If set to retain parent directory, return now
+ if ($retain_parent_directory)
+ {
+ return TRUE;
+ }
+
+ try
+ {
+ // Remove the files iterator
+ // (fixes Windows PHP which has permission issues with open iterators)
+ unset($files);
+
+ // Try to remove the parent directory
+ return rmdir($file->getRealPath());
+ }
+ catch (ErrorException $e)
+ {
+ // Catch any delete directory warnings
+ if ($e->getCode() === E_WARNING)
+ {
+ throw new Kohana_Cache_Exception(__METHOD__.' failed to delete directory : :directory', array(':directory' => $file->getRealPath()));
+ }
+ }
+ }
+ }
+ // Catch all exceptions
+ catch (Exception $e)
+ {
+ // If ignore_errors is on
+ if ($ignore_errors === TRUE)
+ {
+ // Return
+ return FALSE;
+ }
+ // Throw exception
+ throw $e;
+ }
+ }
+
+ /**
+ * Resolves the cache directory real path from the filename
+ *
+ * // Get the realpath of the cache folder
+ * $realpath = $this->_resolve_directory($filename);
+ *
+ * @param string filename to resolve
+ * @return string
+ */
+ protected function _resolve_directory($filename)
+ {
+ return $this->_cache_dir->getRealPath().DIRECTORY_SEPARATOR.$filename[0].$filename[1].DIRECTORY_SEPARATOR;
+ }
+}
diff --git a/includes/kohana/modules/cache/classes/kohana/cache/garbagecollect.php b/includes/kohana/modules/cache/classes/kohana/cache/garbagecollect.php
new file mode 100644
index 00000000..228dd610
--- /dev/null
+++ b/includes/kohana/modules/cache/classes/kohana/cache/garbagecollect.php
@@ -0,0 +1,23 @@
+ array( // Default group
+ * 'driver' => 'memcache', // using Memcache driver
+ * 'servers' => array( // Available server definitions
+ * // First memcache server server
+ * array(
+ * 'host' => 'localhost',
+ * 'port' => 11211,
+ * 'persistent' => FALSE
+ * 'weight' => 1,
+ * 'timeout' => 1,
+ * 'retry_interval' => 15,
+ * 'status' => TRUE,
+ * 'failure_callback' => array('className', 'classMethod')
+ * ),
+ * // Second memcache server
+ * array(
+ * 'host' => '192.168.1.5',
+ * 'port' => 22122,
+ * 'persistent' => TRUE
+ * )
+ * ),
+ * 'compression' => FALSE, // Use compression?
+ * ),
+ * )
+ *
+ * In cases where only one cache group is required, if the group is named `default` there is
+ * no need to pass the group name when instantiating a cache instance.
+ *
+ * #### General cache group configuration settings
+ *
+ * Below are the settings available to all types of cache driver.
+ *
+ * Name | Required | Description
+ * -------------- | -------- | ---------------------------------------------------------------
+ * driver | __YES__ | (_string_) The driver type to use
+ * servers | __YES__ | (_array_) Associative array of server details, must include a __host__ key. (see _Memcache server configuration_ below)
+ * compression | __NO__ | (_boolean_) Use data compression when caching
+ *
+ * #### Memcache server configuration
+ *
+ * The following settings should be used when defining each memcache server
+ *
+ * Name | Required | Description
+ * ---------------- | -------- | ---------------------------------------------------------------
+ * host | __YES__ | (_string_) The host of the memcache server, i.e. __localhost__; or __127.0.0.1__; or __memcache.domain.tld__
+ * port | __NO__ | (_integer_) Point to the port where memcached is listening for connections. Set this parameter to 0 when using UNIX domain sockets. Default __11211__
+ * persistent | __NO__ | (_boolean_) Controls the use of a persistent connection. Default __TRUE__
+ * weight | __NO__ | (_integer_) Number of buckets to create for this server which in turn control its probability of it being selected. The probability is relative to the total weight of all servers. Default __1__
+ * timeout | __NO__ | (_integer_) Value in seconds which will be used for connecting to the daemon. Think twice before changing the default value of 1 second - you can lose all the advantages of caching if your connection is too slow. Default __1__
+ * retry_interval | __NO__ | (_integer_) Controls how often a failed server will be retried, the default value is 15 seconds. Setting this parameter to -1 disables automatic retry. Default __15__
+ * status | __NO__ | (_boolean_) Controls if the server should be flagged as online. Default __TRUE__
+ * failure_callback | __NO__ | (_[callback](http://www.php.net/manual/en/language.pseudo-types.php#language.types.callback)_) Allows the user to specify a callback function to run upon encountering an error. The callback is run before failover is attempted. The function takes two parameters, the hostname and port of the failed server. Default __NULL__
+ *
+ * ### System requirements
+ *
+ * * Kohana 3.0.x
+ * * PHP 5.2.4 or greater
+ * * Memcache (plus Memcached-tags for native tagging support)
+ * * Zlib
+ *
+ * @package Kohana
+ * @category Cache
+ * @version 2.0
+ * @author Kohana Team
+ * @copyright (c) 2009-2010 Kohana Team
+ * @license http://kohanaphp.com/license
+ */
+class Kohana_Cache_Memcache extends Cache {
+
+ // Memcache has a maximum cache lifetime of 30 days
+ const CACHE_CEILING = 2592000;
+
+ /**
+ * Memcache resource
+ *
+ * @var Memcache
+ */
+ protected $_memcache;
+
+ /**
+ * Flags to use when storing values
+ *
+ * @var string
+ */
+ protected $_flags;
+
+ /**
+ * Constructs the memcache Kohana_Cache object
+ *
+ * @param array configuration
+ * @throws Kohana_Cache_Exception
+ */
+ protected function __construct(array $config)
+ {
+ // Check for the memcache extention
+ if ( ! extension_loaded('memcache'))
+ {
+ throw new Kohana_Cache_Exception('Memcache PHP extention not loaded');
+ }
+
+ parent::__construct($config);
+
+ // Setup Memcache
+ $this->_memcache = new Memcache;
+
+ // Load servers from configuration
+ $servers = Arr::get($this->_config, 'servers', NULL);
+
+ if ( ! $servers)
+ {
+ // Throw an exception if no server found
+ throw new Kohana_Cache_Exception('No Memcache servers defined in configuration');
+ }
+
+ // Setup default server configuration
+ $config = array(
+ 'host' => 'localhost',
+ 'port' => 11211,
+ 'persistent' => FALSE,
+ 'weight' => 1,
+ 'timeout' => 1,
+ 'retry_interval' => 15,
+ 'status' => TRUE,
+ 'failure_callback' => array($this, '_failed_request'),
+ );
+
+ // Add the memcache servers to the pool
+ foreach ($servers as $server)
+ {
+ // Merge the defined config with defaults
+ $server += $config;
+
+ if ( ! $this->_memcache->addServer($server['host'], $server['port'], $server['persistent'], $server['weight'], $server['timeout'], $server['retry_interval'], $server['status'], $server['failure_callback']))
+ {
+ throw new Kohana_Cache_Exception('Memcache could not connect to host \':host\' using port \':port\'', array(':host' => $server['host'], ':port' => $server['port']));
+ }
+ }
+
+ // Setup the flags
+ $this->_flags = Arr::get($this->_config, 'compression', FALSE) ? MEMCACHE_COMPRESSED : FALSE;
+ }
+
+ /**
+ * Retrieve a cached value entry by id.
+ *
+ * // Retrieve cache entry from memcache group
+ * $data = Cache::instance('memcache')->get('foo');
+ *
+ * // Retrieve cache entry from memcache group and return 'bar' if miss
+ * $data = Cache::instance('memcache')->get('foo', 'bar');
+ *
+ * @param string id of cache to entry
+ * @param string default value to return if cache miss
+ * @return mixed
+ * @throws Kohana_Cache_Exception
+ */
+ public function get($id, $default = NULL)
+ {
+ // Get the value from Memcache
+ $value = $this->_memcache->get($this->_sanitize_id($id));
+
+ // If the value wasn't found, normalise it
+ if ($value === FALSE)
+ {
+ $value = (NULL === $default) ? NULL : $default;
+ }
+
+ // Return the value
+ return $value;
+ }
+
+ /**
+ * Set a value to cache with id and lifetime
+ *
+ * $data = 'bar';
+ *
+ * // Set 'bar' to 'foo' in memcache group for 10 minutes
+ * if (Cache::instance('memcache')->set('foo', $data, 600))
+ * {
+ * // Cache was set successfully
+ * return
+ * }
+ *
+ * @param string id of cache entry
+ * @param mixed data to set to cache
+ * @param integer lifetime in seconds, maximum value 2592000
+ * @return boolean
+ */
+ public function set($id, $data, $lifetime = 3600)
+ {
+ // If the lifetime is greater than the ceiling
+ if ($lifetime > Cache_Memcache::CACHE_CEILING)
+ {
+ // Set the lifetime to maximum cache time
+ $lifetime = Cache_Memcache::CACHE_CEILING + time();
+ }
+ // Else if the lifetime is greater than zero
+ elseif ($lifetime > 0)
+ {
+ $lifetime += time();
+ }
+ // Else
+ else
+ {
+ // Normalise the lifetime
+ $lifetime = 0;
+ }
+
+ // Set the data to memcache
+ return $this->_memcache->set($this->_sanitize_id($id), $data, $this->_flags, $lifetime);
+ }
+
+ /**
+ * Delete a cache entry based on id
+ *
+ * // Delete the 'foo' cache entry immediately
+ * Cache::instance('memcache')->delete('foo');
+ *
+ * // Delete the 'bar' cache entry after 30 seconds
+ * Cache::instance('memcache')->delete('bar', 30);
+ *
+ * @param string id of entry to delete
+ * @param integer timeout of entry, if zero item is deleted immediately, otherwise the item will delete after the specified value in seconds
+ * @return boolean
+ */
+ public function delete($id, $timeout = 0)
+ {
+ // Delete the id
+ return $this->_memcache->delete($this->_sanitize_id($id), $timeout);
+ }
+
+ /**
+ * Delete all cache entries.
+ *
+ * Beware of using this method when
+ * using shared memory cache systems, as it will wipe every
+ * entry within the system for all clients.
+ *
+ * // Delete all cache entries in the default group
+ * Cache::instance('memcache')->delete_all();
+ *
+ * @return boolean
+ */
+ public function delete_all()
+ {
+ $result = $this->_memcache->flush();
+
+ // We must sleep after flushing, or overwriting will not work!
+ // @see http://php.net/manual/en/function.memcache-flush.php#81420
+ sleep(1);
+
+ return $result;
+ }
+
+ /**
+ * Callback method for Memcache::failure_callback to use if any Memcache call
+ * on a particular server fails. This method switches off that instance of the
+ * server if the configuration setting `instant_death` is set to `TRUE`.
+ *
+ * @param string hostname
+ * @param integer port
+ * @return void|boolean
+ * @since 3.0.8
+ */
+ protected function _failed_request($hostname, $port)
+ {
+ if ( ! $this->_config['instant_death'])
+ return;
+
+ // Setup non-existent host
+ $host = FALSE;
+
+ // Get host settings from configuration
+ foreach ($this->_config['servers'] as $server)
+ {
+ if ($hostname == $server['host'] and $port == $server['port'])
+ {
+ $host = $server;
+ continue;
+ }
+ }
+
+ if ( ! $host)
+ return;
+ else
+ {
+ return $this->_memcache->setServerParams(
+ $host['host'],
+ $host['port'],
+ $host['timeout'],
+ $host['retry_interval'],
+ FALSE,
+ array($this, '_failed_request'
+ ));
+ }
+ }
+}
\ No newline at end of file
diff --git a/includes/kohana/modules/cache/classes/kohana/cache/memcachetag.php b/includes/kohana/modules/cache/classes/kohana/cache/memcachetag.php
new file mode 100644
index 00000000..cdb0db73
--- /dev/null
+++ b/includes/kohana/modules/cache/classes/kohana/cache/memcachetag.php
@@ -0,0 +1,76 @@
+_memcache, 'tag_add'))
+ {
+ throw new Kohana_Cache_Exception('Memcached-tags PHP plugin not present. Please see http://code.google.com/p/memcached-tags/ for more information');
+ }
+
+ parent::__construct($config);
+ }
+
+ /**
+ * Set a value based on an id with tags
+ *
+ * @param string id
+ * @param mixed data
+ * @param integer lifetime [Optional]
+ * @param array tags [Optional]
+ * @return boolean
+ */
+ public function set_with_tags($id, $data, $lifetime = NULL, array $tags = NULL)
+ {
+ $result = $this->set($id, $data, $lifetime);
+
+ if ($result and $tags)
+ {
+ foreach ($tags as $tag)
+ {
+ $this->_memcache->tag_add($tag, $id);
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Delete cache entries based on a tag
+ *
+ * @param string tag
+ * @return boolean
+ */
+ public function delete_tag($tag)
+ {
+ return $this->_memcache->tag_delete($tag);
+ }
+
+ /**
+ * Find cache entries based on a tag
+ *
+ * @param string tag
+ * @return void
+ * @throws Kohana_Cache_Exception
+ */
+ public function find($tag)
+ {
+ throw new Kohana_Cache_Exception('Memcached-tags does not support finding by tag');
+ }
+}
\ No newline at end of file
diff --git a/includes/kohana/modules/cache/classes/kohana/cache/sqlite.php b/includes/kohana/modules/cache/classes/kohana/cache/sqlite.php
new file mode 100644
index 00000000..32619771
--- /dev/null
+++ b/includes/kohana/modules/cache/classes/kohana/cache/sqlite.php
@@ -0,0 +1,336 @@
+_config, 'database', NULL);
+
+ if ($database === NULL)
+ {
+ throw new Kohana_Cache_Exception('Database path not available in Kohana Cache configuration');
+ }
+
+ // Load new Sqlite DB
+ $this->_db = new PDO('sqlite:'.$database);
+
+ // Test for existing DB
+ $result = $this->_db->query("SELECT * FROM sqlite_master WHERE name = 'caches' AND type = 'table'")->fetchAll();
+
+ // If there is no table, create a new one
+ if (0 == count($result))
+ {
+ $database_schema = Arr::get($this->_config, 'schema', NULL);
+
+ if ($database_schema === NULL)
+ {
+ throw new Kohana_Cache_Exception('Database schema not found in Kohana Cache configuration');
+ }
+
+ try
+ {
+ // Create the caches table
+ $this->_db->query(Arr::get($this->_config, 'schema', NULL));
+ }
+ catch (PDOException $e)
+ {
+ throw new Kohana_Cache_Exception('Failed to create new SQLite caches table with the following error : :error', array(':error' => $e->getMessage()));
+ }
+ }
+ }
+
+ /**
+ * Retrieve a value based on an id
+ *
+ * @param string id
+ * @param string default [Optional] Default value to return if id not found
+ * @return mixed
+ * @throws Kohana_Cache_Exception
+ */
+ public function get($id, $default = NULL)
+ {
+ // Prepare statement
+ $statement = $this->_db->prepare('SELECT id, expiration, cache FROM caches WHERE id = :id LIMIT 0, 1');
+
+ // Try and load the cache based on id
+ try
+ {
+ $statement->execute(array(':id' => $this->_sanitize_id($id)));
+ }
+ catch (PDOException $e)
+ {
+ throw new Kohana_Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage()));
+ }
+
+ if ( ! $result = $statement->fetch(PDO::FETCH_OBJ))
+ {
+ return $default;
+ }
+
+ // If the cache has expired
+ if ($result->expiration != 0 and $result->expiration <= time())
+ {
+ // Delete it and return default value
+ $this->delete($id);
+ return $default;
+ }
+ // Otherwise return cached object
+ else
+ {
+ // Disable notices for unserializing
+ $ER = error_reporting(~E_NOTICE);
+
+ // Return the valid cache data
+ $data = unserialize($result->cache);
+
+ // Turn notices back on
+ error_reporting($ER);
+
+ // Return the resulting data
+ return $data;
+ }
+ }
+
+ /**
+ * Set a value based on an id. Optionally add tags.
+ *
+ * @param string id
+ * @param mixed data
+ * @param integer lifetime [Optional]
+ * @return boolean
+ */
+ public function set($id, $data, $lifetime = NULL)
+ {
+ return (bool) $this->set_with_tags($id, $data, $lifetime);
+ }
+
+ /**
+ * Delete a cache entry based on id
+ *
+ * @param string id
+ * @param integer timeout [Optional]
+ * @return boolean
+ * @throws Kohana_Cache_Exception
+ */
+ public function delete($id)
+ {
+ // Prepare statement
+ $statement = $this->_db->prepare('DELETE FROM caches WHERE id = :id');
+
+ // Remove the entry
+ try
+ {
+ $statement->execute(array(':id' => $this->_sanitize_id($id)));
+ }
+ catch (PDOException $e)
+ {
+ throw new Kohana_Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage()));
+ }
+
+ return (bool) $statement->rowCount();
+ }
+
+ /**
+ * Delete all cache entries
+ *
+ * @return boolean
+ */
+ public function delete_all()
+ {
+ // Prepare statement
+ $statement = $this->_db->prepare('DELETE FROM caches');
+
+ // Remove the entry
+ try
+ {
+ $statement->execute();
+ }
+ catch (PDOException $e)
+ {
+ throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage()));
+ }
+
+ return (bool) $statement->rowCount();
+ }
+
+ /**
+ * Set a value based on an id. Optionally add tags.
+ *
+ * @param string id
+ * @param mixed data
+ * @param integer lifetime [Optional]
+ * @param array tags [Optional]
+ * @return boolean
+ * @throws Kohana_Cache_Exception
+ */
+ public function set_with_tags($id, $data, $lifetime = NULL, array $tags = NULL)
+ {
+ // Serialize the data
+ $data = serialize($data);
+
+ // Normalise tags
+ $tags = (NULL === $tags) ? NULL : '<'.implode('>,<', $tags).'>';
+
+ // Setup lifetime
+ if ($lifetime === NULL)
+ {
+ $lifetime = (0 === Arr::get('default_expire', NULL)) ? 0 : Arr::get($this->_config, 'default_expire', Cache::DEFAULT_EXPIRE) + time();
+ }
+ else
+ {
+ $lifetime = (0 === $lifetime) ? 0 : $lifetime + time();
+ }
+
+ // Prepare statement
+ // $this->exists() may throw Kohana_Cache_Exception, no need to catch/rethrow
+ $statement = $this->exists($id) ? $this->_db->prepare('UPDATE caches SET expiration = :expiration, cache = :cache, tags = :tags WHERE id = :id') : $this->_db->prepare('INSERT INTO caches (id, cache, expiration, tags) VALUES (:id, :cache, :expiration, :tags)');
+
+ // Try to insert
+ try
+ {
+ $statement->execute(array(':id' => $this->_sanitize_id($id), ':cache' => $data, ':expiration' => $lifetime, ':tags' => $tags));
+ }
+ catch (PDOException $e)
+ {
+ throw new Kohana_Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage()));
+ }
+
+ return (bool) $statement->rowCount();
+ }
+
+ /**
+ * Delete cache entries based on a tag
+ *
+ * @param string tag
+ * @param integer timeout [Optional]
+ * @return boolean
+ * @throws Kohana_Cache_Exception
+ */
+ public function delete_tag($tag)
+ {
+ // Prepare the statement
+ $statement = $this->_db->prepare('DELETE FROM caches WHERE tags LIKE :tag');
+
+ // Try to delete
+ try
+ {
+ $statement->execute(array(':tag' => "%<{$tag}>%"));
+ }
+ catch (PDOException $e)
+ {
+ throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage()));
+ }
+
+ return (bool) $statement->rowCount();
+ }
+
+ /**
+ * Find cache entries based on a tag
+ *
+ * @param string tag
+ * @return array
+ * @throws Kohana_Cache_Exception
+ */
+ public function find($tag)
+ {
+ // Prepare the statement
+ $statement = $this->_db->prepare('SELECT id, cache FROM caches WHERE tags LIKE :tag');
+
+ // Try to find
+ try
+ {
+ if ( ! $statement->execute(array(':tag' => "%<{$tag}>%")))
+ {
+ return array();
+ }
+ }
+ catch (PDOException $e)
+ {
+ throw new Kohana_Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage()));
+ }
+
+ $result = array();
+
+ while ($row = $statement->fetchObject())
+ {
+ // Disable notices for unserializing
+ $ER = error_reporting(~E_NOTICE);
+
+ $result[$row->id] = unserialize($row->cache);
+
+ // Turn notices back on
+ error_reporting($ER);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Garbage collection method that cleans any expired
+ * cache entries from the cache.
+ *
+ * @return void
+ */
+ public function garbage_collect()
+ {
+ // Create the sequel statement
+ $statement = $this->_db->prepare('DELETE FROM caches WHERE expiration < :expiration');
+
+ try
+ {
+ $statement->execute(array(':expiration' => time()));
+ }
+ catch (PDOException $e)
+ {
+ throw new Kohana_Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage()));
+ }
+ }
+
+ /**
+ * Tests whether an id exists or not
+ *
+ * @param string id
+ * @return boolean
+ * @throws Kohana_Cache_Exception
+ */
+ protected function exists($id)
+ {
+ $statement = $this->_db->prepare('SELECT id FROM caches WHERE id = :id');
+ try
+ {
+ $statement->execute(array(':id' => $this->_sanitize_id($id)));
+ }
+ catch (PDOExeption $e)
+ {
+ throw new Kohana_Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage()));
+ }
+
+ return (bool) $statement->fetchAll();
+ }
+}
\ No newline at end of file
diff --git a/includes/kohana/modules/cache/classes/kohana/cache/tagging.php b/includes/kohana/modules/cache/classes/kohana/cache/tagging.php
new file mode 100644
index 00000000..b7fe1ab7
--- /dev/null
+++ b/includes/kohana/modules/cache/classes/kohana/cache/tagging.php
@@ -0,0 +1,42 @@
+_sanitize_id($id))) === NULL) ? $default : $data;
+ }
+
+ /**
+ * Set a value based on an id. Optionally add tags.
+ *
+ * @param string id
+ * @param string data
+ * @param integer lifetime [Optional]
+ * @return boolean
+ */
+ public function set($id, $data, $lifetime = NULL)
+ {
+ if (NULL === $lifetime)
+ {
+ $lifetime = Arr::get($this->_config, 'default_expire', Cache::DEFAULT_EXPIRE);
+ }
+
+ return xcache_set($this->_sanitize_id($id), $data, $lifetime);
+ }
+
+ /**
+ * Delete a cache entry based on id
+ *
+ * @param string id
+ * @param integer timeout [Optional]
+ * @return boolean
+ */
+ public function delete($id)
+ {
+ return xcache_unset($this->_sanitize_id($id));
+ }
+
+ /**
+ * Delete all cache entries
+ * To use this method xcache.admin.enable_auth has to be Off in xcache.ini
+ *
+ * @return void
+ */
+ public function delete_all()
+ {
+ xcache_clear_cache(XC_TYPE_PHP, 0);
+ }
+}
diff --git a/includes/kohana/modules/cache/config/cache.php b/includes/kohana/modules/cache/config/cache.php
new file mode 100644
index 00000000..c509c704
--- /dev/null
+++ b/includes/kohana/modules/cache/config/cache.php
@@ -0,0 +1,71 @@
+ array
+ (
+ 'driver' => 'memcache',
+ 'default_expire' => 3600,
+ 'compression' => FALSE, // Use Zlib compression (can cause issues with integers)
+ 'servers' => array
+ (
+ array
+ (
+ 'host' => 'localhost', // Memcache Server
+ 'port' => 11211, // Memcache port number
+ 'persistent' => FALSE, // Persistent connection
+ 'weight' => 1,
+ 'timeout' => 1,
+ 'retry_interval' => 15,
+ 'status' => TRUE,
+ ),
+ ),
+ 'instant_death' => TRUE, // Take server offline immediately on first fail (no retry)
+ ),
+ 'memcachetag' => array
+ (
+ 'driver' => 'memcachetag',
+ 'default_expire' => 3600,
+ 'compression' => FALSE, // Use Zlib compression (can cause issues with integers)
+ 'servers' => array
+ (
+ array
+ (
+ 'host' => 'localhost', // Memcache Server
+ 'port' => 11211, // Memcache port number
+ 'persistent' => FALSE, // Persistent connection
+ 'weight' => 1,
+ 'timeout' => 1,
+ 'retry_interval' => 15,
+ 'status' => TRUE,
+ ),
+ ),
+ 'instant_death' => TRUE,
+ ),
+ 'apc' => array
+ (
+ 'driver' => 'apc',
+ 'default_expire' => 3600,
+ ),
+ 'sqlite' => array
+ (
+ 'driver' => 'sqlite',
+ 'default_expire' => 3600,
+ 'database' => APPPATH.'cache/kohana-cache.sql3',
+ 'schema' => 'CREATE TABLE caches(id VARCHAR(127) PRIMARY KEY, tags VARCHAR(255), expiration INTEGER, cache TEXT)',
+ ),
+ 'eaccelerator' => array
+ (
+ 'driver' => 'eaccelerator',
+ ),
+ 'xcache' => array
+ (
+ 'driver' => 'xcache',
+ 'default_expire' => 3600,
+ ),
+ 'file' => array
+ (
+ 'driver' => 'file',
+ 'cache_dir' => APPPATH.'cache',
+ 'default_expire' => 3600,
+ )
+);
\ No newline at end of file
diff --git a/includes/kohana/modules/cache/guide/cache.about.md b/includes/kohana/modules/cache/guide/cache.about.md
new file mode 100644
index 00000000..b93b11d6
--- /dev/null
+++ b/includes/kohana/modules/cache/guide/cache.about.md
@@ -0,0 +1,59 @@
+# About Kohana Cache
+
+[Kohana_Cache] provides a common interface to a variety of caching engines. [Kohana_Cache_Tagging] is
+supported where available natively to the cache system. Kohana Cache supports multiple
+instances of cache engines through a grouped singleton pattern.
+
+## Supported cache engines
+
+ * APC ([Cache_Apc])
+ * eAccelerator ([Cache_Eaccelerator])
+ * File ([Cache_File])
+ * Memcached ([Cache_Memcache])
+ * Memcached-tags ([Cache_Memcachetag])
+ * SQLite ([Cache_Sqlite])
+ * Xcache ([Cache_Xcache])
+
+## Introduction to caching
+
+Caching should be implemented with consideration. Generally, caching the result of resources
+is faster than reprocessing them. Choosing what, how and when to cache is vital. [PHP APC](http://php.net/manual/en/book.apc.php) is one of the fastest caching systems available, closely followed by [Memcached](http://memcached.org/). [SQLite](http://www.sqlite.org/) and File caching are two of the slowest cache methods, however usually faster than reprocessing
+a complex set of instructions.
+
+Caching engines that use memory are considerably faster than file based alternatives. But
+memory is limited whereas disk space is plentiful. If caching large datasets, such as large database result sets, it is best to use file caching.
+
+ [!!] Cache drivers require the relevant PHP extensions to be installed. APC, eAccelerator, Memecached and Xcache all require non-standard PHP extensions.
+
+## What the Kohana Cache module does (and does not do)
+
+This module provides a simple abstracted interface to a wide selection of popular PHP cache engines. The caching API provides the basic caching methods implemented across all solutions, memory, network or disk based. Basic key / value storing is supported by all drivers, with additional tagging and garbage collection support where implemented or required.
+
+_Kohana Cache_ does not provide HTTP style caching for clients (web browsers) and/or proxies (_Varnish_, _Squid_). There are other Kohana modules that provide this functionality.
+
+## Choosing a cache provider
+
+Getting and setting values to cache is very simple when using the _Kohana Cache_ interface. The hardest choice is choosing which cache engine to use. When choosing a caching engine, the following criteria must be considered:
+
+ 1. __Does the cache need to be distributed?__
+ This is an important consideration as it will severely limit the options available to solutions such as Memcache when a distributed solution is required.
+ 2. __Does the cache need to be fast?__
+ In almost all cases retrieving data from a cache is faster than execution. However generally memory based caching is considerably faster than disk based caching (see table below).
+ 3. __How much cache is required?__
+ Cache is not endless, and memory based caches are subject to a considerably more limited storage resource.
+
+Driver | Storage | Speed | Tags | Distributed | Automatic Garbage Collection | Notes
+---------------- | ------------ | --------- | -------- | ----------- | ---------------------------- | -----------------------
+APC | __Memory__ | Excellent | No | No | Yes | Widely available PHP opcode caching solution, improves php execution performance
+eAccelerator | __Memory__ | Excellent | No | No | Yes | Limited support and no longer developed. Included for legacy systems
+File | __Disk__ | Poor | No | No | No | Marginally faster than execution
+Memcache (tag) | __Memory__ | Good | No (yes) | Yes | Yes | Generally fast distributed solution, but has a speed hit due to variable network latency
+Sqlite | __Disk__ | Poor | Yes | No | No | Marginally faster than execution
+Xcache | __Memory__ | Excellent | Yes | No | Yes | Very fast memory solution and alternative to APC
+
+It is possible to have hybrid cache solutions that use a combination of the engines above in different contexts. This is supported with _Kohana Cache_ as well.
+
+## Minimum requirements
+
+ * Kohana 3.0.4
+ * PHP 5.2.4 or greater
\ No newline at end of file
diff --git a/includes/kohana/modules/cache/guide/cache.config.md b/includes/kohana/modules/cache/guide/cache.config.md
new file mode 100644
index 00000000..a6d428fe
--- /dev/null
+++ b/includes/kohana/modules/cache/guide/cache.config.md
@@ -0,0 +1,168 @@
+# Kohana Cache configuration
+
+Kohana Cache uses configuration groups to create cache instances. A configuration group can
+use any supported driver, with successive groups using multiple instances of the same driver type.
+
+The default cache group is loaded based on the `Cache::$default` setting. It is set to the `file` driver as standard, however this can be changed within the `/application/boostrap.php` file.
+
+ // Change the default cache driver to memcache
+ Cache::$default = 'memcache';
+
+ // Load the memcache cache driver using default setting
+ $memcache = Cache::instance();
+
+## Group settings
+
+Below are the default cache configuration groups for each supported driver. Add to- or override these settings
+within the `application/config/cache.php` file.
+
+Name | Required | Description
+-------------- | -------- | ---------------------------------------------------------------
+driver | __YES__ | (_string_) The driver type to use
+default_expire | __NO__ | (_string_) The driver type to use
+
+
+ 'file' => array
+ (
+ 'driver' => 'file',
+ 'cache_dir' => APPPATH.'cache/.kohana_cache',
+ 'default_expire' => 3600,
+ ),
+
+## Memcache & Memcached-tag settings
+
+Name | Required | Description
+-------------- | -------- | ---------------------------------------------------------------
+driver | __YES__ | (_string_) The driver type to use
+servers | __YES__ | (_array_) Associative array of server details, must include a __host__ key. (see _Memcache server configuration_ below)
+compression | __NO__ | (_boolean_) Use data compression when caching
+
+### Memcache server configuration
+
+Name | Required | Description
+---------------- | -------- | ---------------------------------------------------------------
+host | __YES__ | (_string_) The host of the memcache server, i.e. __localhost__; or __127.0.0.1__; or __memcache.domain.tld__
+port | __NO__ | (_integer_) Point to the port where memcached is listening for connections. Set this parameter to 0 when using UNIX domain sockets. Default __11211__
+persistent | __NO__ | (_boolean_) Controls the use of a persistent connection. Default __TRUE__
+weight | __NO__ | (_integer_) Number of buckets to create for this server which in turn control its probability of it being selected. The probability is relative to the total weight of all servers. Default __1__
+timeout | __NO__ | (_integer_) Value in seconds which will be used for connecting to the daemon. Think twice before changing the default value of 1 second - you can lose all the advantages of caching if your connection is too slow. Default __1__
+retry_interval | __NO__ | (_integer_) Controls how often a failed server will be retried, the default value is 15 seconds. Setting this parameter to -1 disables automatic retry. Default __15__
+status | __NO__ | (_boolean_) Controls if the server should be flagged as online. Default __TRUE__
+failure_callback | __NO__ | (_[callback](http://www.php.net/manual/en/language.pseudo-types.php#language.types.callback)_) Allows the user to specify a callback function to run upon encountering an error. The callback is run before failover is attempted. The function takes two parameters, the hostname and port of the failed server. Default __NULL__
+
+ 'memcache' => array
+ (
+ 'driver' => 'memcache',
+ 'default_expire' => 3600,
+ 'compression' => FALSE, // Use Zlib compression
+ (can cause issues with integers)
+ 'servers' => array
+ (
+ array
+ (
+ 'host' => 'localhost', // Memcache Server
+ 'port' => 11211, // Memcache port number
+ 'persistent' => FALSE, // Persistent connection
+ ),
+ ),
+ ),
+ 'memcachetag' => array
+ (
+ 'driver' => 'memcachetag',
+ 'default_expire' => 3600,
+ 'compression' => FALSE, // Use Zlib compression
+ (can cause issues with integers)
+ 'servers' => array
+ (
+ array
+ (
+ 'host' => 'localhost', // Memcache Server
+ 'port' => 11211, // Memcache port number
+ 'persistent' => FALSE, // Persistent connection
+ ),
+ ),
+ ),
+
+## APC settings
+
+ 'apc' => array
+ (
+ 'driver' => 'apc',
+ 'default_expire' => 3600,
+ ),
+
+## SQLite settings
+
+ 'sqlite' => array
+ (
+ 'driver' => 'sqlite',
+ 'default_expire' => 3600,
+ 'database' => APPPATH.'cache/kohana-cache.sql3',
+ 'schema' => 'CREATE TABLE caches(id VARCHAR(127) PRIMARY KEY,
+ tags VARCHAR(255), expiration INTEGER, cache TEXT)',
+ ),
+
+## Eaccelerator settings
+
+ 'eaccelerator' array
+ (
+ 'driver' => 'eaccelerator',
+ ),
+
+## Xcache settings
+
+ 'xcache' => array
+ (
+ 'driver' => 'xcache',
+ 'default_expire' => 3600,
+ ),
+
+## File settings
+
+ 'file' => array
+ (
+ 'driver' => 'file',
+ 'cache_dir' => 'cache/.kohana_cache',
+ 'default_expire' => 3600,
+ )
+
+## Override existing configuration group
+
+The following example demonstrates how to override an existing configuration setting, using the config file in `/application/config/cache.php`.
+
+ array
+ (
+ 'driver' => 'memcache', // Use Memcached as the default driver
+ 'default_expire' => 8000, // Overide default expiry
+ 'servers' => array
+ (
+ // Add a new server
+ array
+ (
+ 'host' => 'cache.domain.tld',
+ 'port' => 11211,
+ 'persistent' => FALSE
+ )
+ ),
+ 'compression' => FALSE
+ )
+ );
+
+## Add new configuration group
+
+The following example demonstrates how to add a new configuration setting, using the config file in `/application/config/cache.php`.
+
+ array
+ (
+ 'driver' => 'apc', // Use Memcached as the default driver
+ 'default_expire' => 1000, // Overide default expiry
+ )
+ );
\ No newline at end of file
diff --git a/includes/kohana/modules/cache/guide/cache.usage.md b/includes/kohana/modules/cache/guide/cache.usage.md
new file mode 100644
index 00000000..8a8239bc
--- /dev/null
+++ b/includes/kohana/modules/cache/guide/cache.usage.md
@@ -0,0 +1,219 @@
+# Kohana Cache usage
+
+[Kohana_Cache] provides a simple interface allowing getting, setting and deleting of cached values. Two interfaces included in _Kohana Cache_ additionally provide _tagging_ and _garbage collection_ where they are supported by the respective drivers.
+
+## Getting a new cache instance
+
+Creating a new _Kohana Cache_ instance is simple, however it must be done using the [Cache::instance] method, rather than the traditional `new` constructor.
+
+ // Create a new instance of cache using the default group
+ $cache = Cache::instance();
+
+The default group will use whatever is set to [Cache::$default] and must have a corresponding [configuration](cache.config) definition for that group.
+
+To create a cache instance using a group other than the _default_, simply provide the group name as an argument.
+
+ // Create a new instance of the memcache group
+ $memcache = Cache::instance('memcache');
+
+If there is a cache instance already instantiated then you can get it directly from the class member.
+
+ [!!] Beware that this can cause issues if you do not test for the instance before trying to access it.
+
+ // Check for the existance of the cache driver
+ if (isset(Cache::$instances['memcache']))
+ {
+ // Get the existing cache instance directly (faster)
+ $memcache = Cache::$instances['memcache'];
+ }
+ else
+ {
+ // Get the cache driver instance (slower)
+ $memcache = Cache::instance('memcache');
+ }
+
+## Setting and getting variables to and from cache
+
+The cache library supports scalar and object values, utilising object serialization where required (or not supported by the caching engine). This means that the majority or objects can be cached without any modification.
+
+ [!!] Serialisation does not work with resource handles, such as filesystem, curl or socket resources.
+
+### Setting a value to cache
+
+Setting a value to cache using the [Cache::set] method can be done in one of two ways; either using the Cache instance interface, which is good for atomic operations; or getting an instance and using that for multiple operations.
+
+The first example demonstrates how to quickly load and set a value to the default cache instance.
+
+ // Create a cachable object
+ $object = new stdClass;
+
+ // Set a property
+ $object->foo = 'bar';
+
+ // Cache the object using default group (quick interface) with default time (3600 seconds)
+ Cache::instance()->set('foo', $object);
+
+If multiple cache operations are required, it is best to assign an instance of Cache to a variable and use that as below.
+
+ // Set the object using a defined group for a defined time period (30 seconds)
+ $memcache = Cache::instance('memcache');
+ $memcache->set('foo', $object, 30);
+
+#### Setting a value with tags
+
+Certain cache drivers support setting values with tags. To set a value to cache with tags using the following interface.
+
+ // Get a cache instance that supports tags
+ $memcache = Cache::instance('memcachetag');
+
+ // Test for tagging interface
+ if ($memcache instanceof Kohana_Cache_Tagging)
+ {
+ // Set a value with some tags for 30 seconds
+ $memcache->set('foo', $object, 30, array('snafu', 'stfu', 'fubar'));
+ }
+ // Otherwise set without tags
+ else
+ {
+ // Set a value for 30 seconds
+ $memcache->set('foo', $object, 30);
+ }
+
+It is possible to implement custom tagging solutions onto existing or new cache drivers by implementing the [Kohana_Cache_Tagging] interface. Kohana_Cache only applies the interface to drivers that support tagging natively as standard.
+
+### Getting a value from cache
+
+Getting variables back from cache is achieved using the [Cache::get] method using a single key to identify the cache entry.
+
+ // Retrieve a value from cache (quickly)
+ $object = Cache::instance()->get('foo');
+
+In cases where the requested key is not available or the entry has expired, a default value will be returned (__NULL__ by default). It is possible to define the default value as the key is requested.
+
+ // If the cache key is available (with default value set to FALSE)
+ if ($object = Cache::instance()->get('foo', FALSE))
+ {
+ // Do something
+ }
+ else
+ {
+ // Do something else
+ }
+
+#### Getting values from cache using tags
+
+It is possible to retrieve values from cache grouped by tag, using the [Cache::find] method with drivers that support tagging.
+
+ [!!] The __Memcachetag__ driver does not support the `Cache::find($tag)` interface and will throw an exception.
+
+ // Get an instance of cache
+ $cache = Cache::instance('memcachetag');
+
+ // Wrap in a try/catch statement to gracefully handle memcachetag
+ try
+ {
+ // Find values based on tag
+ return $cache->find('snafu');
+ }
+ catch (Kohana_Cache_Exception $e)
+ {
+ // Handle gracefully
+ return FALSE;
+ }
+
+### Deleting values from cache
+
+Deleting variables is very similar to the getting and setting methods already described. Deleting operations are split into three categories:
+
+ - __Delete value by key__. Deletes a cached value by the associated key.
+ - __Delete all values__. Deletes all caches values stored in the cache instance.
+ - __Delete values by tag__. Deletes all values that have the supplied tag. This is only supported by Memcached-Tag and Sqlite.
+
+#### Delete value by key
+
+To delete a specific value by its associated key:
+
+ // If the cache entry for 'foo' is deleted
+ if (Cache::instance()->delete('foo'))
+ {
+ // Cache entry successfully deleted, do something
+ }
+
+By default a `TRUE` value will be returned. However a `FALSE` value will be returned in instances where the key did not exist in the cache.
+
+#### Delete all values
+
+To delete all values in a specific instance:
+
+ // If all cache items where deleted successfully
+ if (Cache::instance()->delete_all())
+ {
+ // Do something
+ }
+
+It is also possible to delete all cache items in every instance:
+
+ // For each cache instance
+ foreach (Cache::$instances as $group => $instance)
+ {
+ if ($instance->delete_all())
+ {
+ var_dump('instance : '.$group.' has been flushed!');
+ }
+ }
+
+#### Delete values by tag
+
+Some of the caching drivers support deleting by tag. This will remove all the cached values that are associated with a specific tag. Below is an example of how to robustly handle deletion by tag.
+
+ // Get cache instance
+ $cache = Cache::instance();
+
+ // Check for tagging interface
+ if ($cache instanceof Kohana_Cache_Tagging)
+ {
+ // Delete all entries by the tag 'snafu'
+ $cache->delete_tag('snafu');
+ }
+
+#### Garbage Collection
+
+Garbage Collection (GC) is the cleaning of expired cache entries. For the most part, caching engines will take care of garbage collection internally. However a few of the file based systems do not handle this task and in these circumstances it would be prudent to garbage collect at a predetermined frequency. If no garbage collection is executed, the resource storing the cache entries will eventually fill and become unusable.
+
+When not automated, garbage collection is the responsibility of the developer. It is prudent to have a GC probability value that dictates how likely the garbage collection routing will be run. An example of such a system is demonstrated below.
+
+ // Get a cache instance
+ $cache_file = Cache::instance('file');
+
+ // Set a GC probability of 10%
+ $gc = 10;
+
+ // If the GC probability is a hit
+ if (rand(0,99) <= $gc and $cache_file instanceof Kohana_Cache_GarbageCollect)
+ {
+ // Garbage Collect
+ $cache_file->garbage_collect();
+ }
+
+# Interfaces
+
+Kohana Cache comes with two interfaces that are implemented where the drivers support them:
+
+ - __[Kohana_Cache_Tagging] for tagging support on cache entries__
+ - [Cache_MemcacheTag]
+ - [Cache_Sqlite]
+ - __[Kohana_Cache_GarbageCollect] for garbage collection with drivers without native support__
+ - [Cache_File]
+ - [Cache_Sqlite]
+
+When using interface specific caching features, ensure that code checks for the required interface before using the methods supplied. The following example checks whether the garbage collection interface is available before calling the `garbage_collect` method.
+
+ // Create a cache instance
+ $cache = Cache::instance();
+
+ // Test for Garbage Collection
+ if ($cache instanceof Kohana_Cache_GarbageCollect)
+ {
+ // Collect garbage
+ $cache->garbage_collect();
+ }
\ No newline at end of file
diff --git a/includes/kohana/modules/cache/guide/menu.cache.md b/includes/kohana/modules/cache/guide/menu.cache.md
new file mode 100644
index 00000000..3f516e5c
--- /dev/null
+++ b/includes/kohana/modules/cache/guide/menu.cache.md
@@ -0,0 +1,4 @@
+1. **Cache**
+ - [About](cache.about)
+ - [Configuration](cache.config)
+ - [Usage](cache.usage)
\ No newline at end of file
diff --git a/includes/kohana/modules/cache/tests/cache/KohanaCacheTest.php b/includes/kohana/modules/cache/tests/cache/KohanaCacheTest.php
new file mode 100644
index 00000000..229e7d32
--- /dev/null
+++ b/includes/kohana/modules/cache/tests/cache/KohanaCacheTest.php
@@ -0,0 +1,91 @@
+delete_all();
+
+ self::$test_instance->set('testGet1', 'foo', 3600);
+ }
+
+ public function tearDown()
+ {
+ self::$test_instance->delete_all();
+ self::$test_instance = NULL;
+ }
+
+ /**
+ * Tests the cache static instance method
+ */
+ public function testInstance()
+ {
+ $file_instance = Cache::instance('file');
+ $file_instance2 = Cache::instance('file');
+
+ // Try and load a Cache instance
+ $this->assertType('Kohana_Cache', Cache::instance());
+ $this->assertType('Kohana_Cache_File', $file_instance);
+
+ // Test instances are only initialised once
+ $this->assertTrue(spl_object_hash($file_instance) == spl_object_hash($file_instance2));
+
+ // Test the publically accessible Cache instance store
+ $this->assertTrue(spl_object_hash(Cache::$instances['file']) == spl_object_hash($file_instance));
+
+ // Get the constructor method
+ $constructorMethod = new ReflectionMethod($file_instance, '__construct');
+
+ // Test the constructor for hidden visibility
+ $this->assertTrue($constructorMethod->isProtected(), '__construct is does not have protected visibility');
+ }
+
+ public function testGet()
+ {
+ // Try and get a non property
+ $this->assertNull(self::$test_instance->get('testGet0'));
+
+ // Try and get a non property with default return value
+ $this->assertEquals('bar', self::$test_instance->get('testGet0', 'bar'));
+
+ // Try and get a real cached property
+ $this->assertEquals('foo', self::$test_instance->get('testGet1'));
+ }
+
+ public function testSet()
+ {
+ $value = 'foobar';
+ $value2 = 'snafu';
+
+ // Set a new property
+ $this->assertTrue(self::$test_instance->set('testSet1', $value));
+
+ // Test the property exists
+ $this->assertEquals(self::$test_instance->get('testSet1'), $value);
+
+ // Test short set
+ $this->assertTrue(self::$test_instance->set('testSet2', $value2, 3));
+
+ // Test the property exists
+ $this->assertEquals(self::$test_instance->get('testSet2'), $value2);
+
+ // Allow test2 to expire
+ sleep(4);
+
+ // Test the property has expired
+ $this->assertNull(self::$test_instance->get('testSet2'));
+ }
+
+ public function testDelete()
+ {
+
+ }
+
+ public function testDeleteAll()
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/includes/kohana/modules/cache/tests/phpunit.xml b/includes/kohana/modules/cache/tests/phpunit.xml
new file mode 100644
index 00000000..5e3b9c79
--- /dev/null
+++ b/includes/kohana/modules/cache/tests/phpunit.xml
@@ -0,0 +1,16 @@
+
+
+
+
+ cache/
+
+
+
diff --git a/includes/kohana/modules/codebench/classes/bench/arrcallback.php b/includes/kohana/modules/codebench/classes/bench/arrcallback.php
new file mode 100644
index 00000000..698a6b8d
--- /dev/null
+++ b/includes/kohana/modules/codebench/classes/bench/arrcallback.php
@@ -0,0 +1,57 @@
+
+ */
+class Bench_ArrCallback extends Codebench {
+
+ public $description =
+ 'Parsing command[param,param] strings in Arr::callback():
+ http://github.com/shadowhand/kohana/commit/c3aaae849164bf92a486e29e736a265b350cb4da#L0R127';
+
+ public $loops = 10000;
+
+ public $subjects = array
+ (
+ // Valid callback strings
+ 'foo',
+ 'foo::bar',
+ 'foo[apple,orange]',
+ 'foo::bar[apple,orange]',
+ '[apple,orange]', // no command, only params
+ 'foo[[apple],[orange]]', // params with brackets inside
+
+ // Invalid callback strings
+ 'foo[apple,orange', // no closing bracket
+ );
+
+ public function bench_shadowhand($subject)
+ {
+ // The original regex we're trying to optimize
+ if (preg_match('/([^\[]*+)\[(.*)\]/', $subject, $match))
+ return $match;
+ }
+
+ public function bench_geert_regex_1($subject)
+ {
+ // Added ^ and $ around the whole pattern
+ if (preg_match('/^([^\[]*+)\[(.*)\]$/', $subject, $matches))
+ return $matches;
+ }
+
+ public function bench_geert_regex_2($subject)
+ {
+ // A rather experimental approach using \K which requires PCRE 7.2 ~ PHP 5.2.4
+ // Note: $matches[0] = params, $matches[1] = command
+ if (preg_match('/^([^\[]*+)\[\K.*(?=\]$)/', $subject, $matches))
+ return $matches;
+ }
+
+ public function bench_geert_str($subject)
+ {
+ // A native string function approach which beats all the regexes
+ if (strpos($subject, '[') !== FALSE AND substr($subject, -1) === ']')
+ return explode('[', substr($subject, 0, -1), 2);
+ }
+}
\ No newline at end of file
diff --git a/includes/kohana/modules/codebench/classes/bench/autolinkemails.php b/includes/kohana/modules/codebench/classes/bench/autolinkemails.php
new file mode 100644
index 00000000..46e7a158
--- /dev/null
+++ b/includes/kohana/modules/codebench/classes/bench/autolinkemails.php
@@ -0,0 +1,70 @@
+
+ */
+class Bench_AutoLinkEmails extends Codebench {
+
+ public $description =
+ 'Fixing #2772, and comparing some possibilities.';
+
+ public $loops = 1000;
+
+ public $subjects = array
+ (
+ '
+
voorzitter@xxxx.com
+
vicevoorzitter@xxxx.com
+
',
+ );
+
+ // The original function, with str_replace replaced by preg_replace. Looks clean.
+ public function bench_match_all_loop($subject)
+ {
+ if (preg_match_all('~\b(?|58;)(?!\.)[-+_a-z0-9.]++(?|58;)(?!\.)[-+_a-z0-9.]++(?|58;)(?!\.)[-+_a-z0-9.]++(?|58;)(?!\.)[-+_a-z0-9.]++(?
+ */
+class Bench_DateSpan extends Codebench {
+
+ public $description =
+ 'Optimization for Date::span().';
+
+ public $loops = 1000;
+
+ public $subjects = array();
+
+ public function __construct()
+ {
+ parent::__construct();
+
+ $this->subjects = array(
+ time(),
+ time() - Date::MONTH,
+ time() - Date::YEAR,
+ time() - Date::YEAR * 10,
+ );
+ }
+
+ // Original method
+ public static function bench_span_original($remote, $local = NULL, $output = 'years,months,weeks,days,hours,minutes,seconds')
+ {
+ // Array with the output formats
+ $output = preg_split('/[^a-z]+/', strtolower((string) $output));
+
+ // Invalid output
+ if (empty($output))
+ return FALSE;
+
+ // Make the output values into keys
+ extract(array_flip($output), EXTR_SKIP);
+
+ if ($local === NULL)
+ {
+ // Calculate the span from the current time
+ $local = time();
+ }
+
+ // Calculate timespan (seconds)
+ $timespan = abs($remote - $local);
+
+ if (isset($years))
+ {
+ $timespan -= Date::YEAR * ($years = (int) floor($timespan / Date::YEAR));
+ }
+
+ if (isset($months))
+ {
+ $timespan -= Date::MONTH * ($months = (int) floor($timespan / Date::MONTH));
+ }
+
+ if (isset($weeks))
+ {
+ $timespan -= Date::WEEK * ($weeks = (int) floor($timespan / Date::WEEK));
+ }
+
+ if (isset($days))
+ {
+ $timespan -= Date::DAY * ($days = (int) floor($timespan / Date::DAY));
+ }
+
+ if (isset($hours))
+ {
+ $timespan -= Date::HOUR * ($hours = (int) floor($timespan / Date::HOUR));
+ }
+
+ if (isset($minutes))
+ {
+ $timespan -= Date::MINUTE * ($minutes = (int) floor($timespan / Date::MINUTE));
+ }
+
+ // Seconds ago, 1
+ if (isset($seconds))
+ {
+ $seconds = $timespan;
+ }
+
+ // Remove the variables that cannot be accessed
+ unset($timespan, $remote, $local);
+
+ // Deny access to these variables
+ $deny = array_flip(array('deny', 'key', 'difference', 'output'));
+
+ // Return the difference
+ $difference = array();
+ foreach ($output as $key)
+ {
+ if (isset($$key) AND ! isset($deny[$key]))
+ {
+ // Add requested key to the output
+ $difference[$key] = $$key;
+ }
+ }
+
+ // Invalid output formats string
+ if (empty($difference))
+ return FALSE;
+
+ // If only one output format was asked, don't put it in an array
+ if (count($difference) === 1)
+ return current($difference);
+
+ // Return array
+ return $difference;
+ }
+
+ // Using an array for the output
+ public static function bench_span_use_array($remote, $local = NULL, $output = 'years,months,weeks,days,hours,minutes,seconds')
+ {
+ // Array with the output formats
+ $output = preg_split('/[^a-z]+/', strtolower((string) $output));
+
+ // Invalid output
+ if (empty($output))
+ return FALSE;
+
+ // Convert the list of outputs to an associative array
+ $output = array_combine($output, array_fill(0, count($output), 0));
+
+ // Make the output values into keys
+ extract(array_flip($output), EXTR_SKIP);
+
+ if ($local === NULL)
+ {
+ // Calculate the span from the current time
+ $local = time();
+ }
+
+ // Calculate timespan (seconds)
+ $timespan = abs($remote - $local);
+
+ if (isset($output['years']))
+ {
+ $timespan -= Date::YEAR * ($output['years'] = (int) floor($timespan / Date::YEAR));
+ }
+
+ if (isset($output['months']))
+ {
+ $timespan -= Date::MONTH * ($output['months'] = (int) floor($timespan / Date::MONTH));
+ }
+
+ if (isset($output['weeks']))
+ {
+ $timespan -= Date::WEEK * ($output['weeks'] = (int) floor($timespan / Date::WEEK));
+ }
+
+ if (isset($output['days']))
+ {
+ $timespan -= Date::DAY * ($output['days'] = (int) floor($timespan / Date::DAY));
+ }
+
+ if (isset($output['hours']))
+ {
+ $timespan -= Date::HOUR * ($output['hours'] = (int) floor($timespan / Date::HOUR));
+ }
+
+ if (isset($output['minutes']))
+ {
+ $timespan -= Date::MINUTE * ($output['minutes'] = (int) floor($timespan / Date::MINUTE));
+ }
+
+ // Seconds ago, 1
+ if (isset($output['seconds']))
+ {
+ $output['seconds'] = $timespan;
+ }
+
+ if (count($output) === 1)
+ {
+ // Only a single output was requested, return it
+ return array_pop($output);
+ }
+
+ // Return array
+ return $output;
+ }
+
+}
\ No newline at end of file
diff --git a/includes/kohana/modules/codebench/classes/bench/explodelimit.php b/includes/kohana/modules/codebench/classes/bench/explodelimit.php
new file mode 100644
index 00000000..4bf2acc2
--- /dev/null
+++ b/includes/kohana/modules/codebench/classes/bench/explodelimit.php
@@ -0,0 +1,34 @@
+
+ */
+class Bench_ExplodeLimit extends Codebench {
+
+ public $description =
+ 'Having a look at the effect of adding a limit to the explode function.
+ http://stackoverflow.com/questions/1308149/how-to-get-a-part-of-url-between-4th-and-5th-slashes';
+
+ public $loops = 10000;
+
+ public $subjects = array
+ (
+ 'http://example.com/articles/123a/view',
+ 'http://example.com/articles/123a/view/x/x/x/x/x',
+ 'http://example.com/articles/123a/view/x/x/x/x/x/x/x/x/x/x/x/x/x/x/x/x/x/x',
+ );
+
+ public function bench_explode_without_limit($subject)
+ {
+ $parts = explode('/', $subject);
+ return $parts[4];
+ }
+
+ public function bench_explode_with_limit($subject)
+ {
+ $parts = explode('/', $subject, 6);
+ return $parts[4];
+ }
+
+}
\ No newline at end of file
diff --git a/includes/kohana/modules/codebench/classes/bench/gruberurl.php b/includes/kohana/modules/codebench/classes/bench/gruberurl.php
new file mode 100644
index 00000000..af239750
--- /dev/null
+++ b/includes/kohana/modules/codebench/classes/bench/gruberurl.php
@@ -0,0 +1,61 @@
+
+ */
+class Bench_GruberURL extends Codebench {
+
+ public $description =
+ 'Optimization for http://daringfireball.net/2009/11/liberal_regex_for_matching_urls';
+
+ public $loops = 10000;
+
+ public $subjects = array
+ (
+ 'http://foo.com/blah_blah',
+ 'http://foo.com/blah_blah/',
+ '(Something like http://foo.com/blah_blah)',
+ 'http://foo.com/blah_blah_(wikipedia)',
+ '(Something like http://foo.com/blah_blah_(wikipedia))',
+ 'http://foo.com/blah_blah.',
+ 'http://foo.com/blah_blah/.',
+ '',
+ '',
+ 'http://foo.com/blah_blah,',
+ 'http://www.example.com/wpstyle/?p=364.',
+ 'http://✪df.ws/e7l',
+ 'rdar://1234',
+ 'rdar:/1234',
+ 'x-yojimbo-item://6303E4C1-xxxx-45A6-AB9D-3A908F59AE0E',
+ 'message://%3c330e7f8409726r6a4ba78dkf1fd71420c1bf6ff@mail.gmail.com%3e',
+ 'http://➡.ws/䨹',
+ 'www.➡.ws/䨹',
+ 'http://example.com',
+ 'Just a www.example.com link.',
+ // To test the use of possessive quatifiers:
+ 'httpppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp',
+ );
+
+ public function bench_daringfireball($subject)
+ {
+ // Original regex by John Gruber
+ preg_match('~\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))~', $subject, $matches);
+ return (empty($matches)) ? FALSE : $matches[0];
+ }
+
+ public function bench_daringfireball_v2($subject)
+ {
+ // Removed outer capturing parentheses, made another pair non-capturing
+ preg_match('~\b(?:[\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|(?:[^[:punct:]\s]|/))~', $subject, $matches);
+ return (empty($matches)) ? FALSE : $matches[0];
+ }
+
+ public function bench_daringfireball_v3($subject)
+ {
+ // Made quantifiers possessive where possible
+ preg_match('~\b(?:[\w-]++://?+|www[.])[^\s()<>]+(?:\([\w\d]++\)|(?:[^[:punct:]\s]|/))~', $subject, $matches);
+ return (empty($matches)) ? FALSE : $matches[0];
+ }
+
+}
\ No newline at end of file
diff --git a/includes/kohana/modules/codebench/classes/bench/ltrimdigits.php b/includes/kohana/modules/codebench/classes/bench/ltrimdigits.php
new file mode 100644
index 00000000..71ead49c
--- /dev/null
+++ b/includes/kohana/modules/codebench/classes/bench/ltrimdigits.php
@@ -0,0 +1,28 @@
+
+ */
+class Bench_LtrimDigits extends Codebench {
+
+ public $description = 'Chopping off leading digits: regex vs ltrim.';
+
+ public $loops = 100000;
+
+ public $subjects = array
+ (
+ '123digits',
+ 'no-digits',
+ );
+
+ public function bench_regex($subject)
+ {
+ return preg_replace('/^\d+/', '', $subject);
+ }
+
+ public function bench_ltrim($subject)
+ {
+ return ltrim($subject, '0..9');
+ }
+}
\ No newline at end of file
diff --git a/includes/kohana/modules/codebench/classes/bench/mddobaseurl.php b/includes/kohana/modules/codebench/classes/bench/mddobaseurl.php
new file mode 100644
index 00000000..1ad2a1b7
--- /dev/null
+++ b/includes/kohana/modules/codebench/classes/bench/mddobaseurl.php
@@ -0,0 +1,66 @@
+
+ */
+class Bench_MDDoBaseURL extends Codebench {
+
+ public $description =
+ 'Optimization for the doBaseURL() method of Kohana_Kodoc_Markdown
+ for the Kohana Userguide.';
+
+ public $loops = 10000;
+
+ public $subjects = array
+ (
+ // Valid matches
+ '[filesystem](about.filesystem)',
+ '[filesystem](about.filesystem "Optional title")',
+ '[same page link](#id)',
+ '[object oriented](http://wikipedia.org/wiki/Object-Oriented_Programming)',
+
+ // Invalid matches
+ '',
+ '[filesystem](about.filesystem',
+ );
+
+ public function bench_original($subject)
+ {
+ // The original regex contained a bug, which is fixed here for benchmarking purposes.
+ // At the very start of the regex, (?!!) has been replace by (?
+ */
+class Bench_MDDoImageURL extends Codebench {
+
+ public $description =
+ 'Optimization for the doImageURL() method of Kohana_Kodoc_Markdown
+ for the Kohana Userguide.';
+
+ public $loops = 10000;
+
+ public $subjects = array
+ (
+ // Valid matches
+ '',
+ '',
+ '',
+ '',
+ '![Alt text containing [square] brackets](img/install.png)',
+ '![Empty src]()',
+
+ // Invalid matches
+ ';
+
+ public function bench_original($subject)
+ {
+ return preg_replace_callback('~!\[(.+?)\]\((\S*(?:\s*".+?")?)\)~', array($this, '_add_image_url_original'), $subject);
+ }
+ protected function _add_image_url_original($matches)
+ {
+ if ($matches[2] AND strpos($matches[2], '://') === FALSE)
+ {
+ // Add the base url to the link URL
+ $matches[2] = 'http://BASE/'.$matches[2];
+ }
+
+ // Recreate the link
+ return "![{$matches[1]}]({$matches[2]})";
+ }
+
+ public function bench_optimized_callback($subject)
+ {
+ // Moved the check for "://" to the regex, simplifying the callback function
+ return preg_replace_callback('~!\[(.+?)\]\((?!\w++://)(\S*(?:\s*+".+?")?)\)~', array($this, '_add_image_url_optimized'), $subject);
+ }
+ protected function _add_image_url_optimized($matches)
+ {
+ // Add the base url to the link URL
+ $matches[2] = 'http://BASE/'.$matches[2];
+
+ // Recreate the link
+ return "![{$matches[1]}]({$matches[2]})";
+ }
+
+ public function bench_callback_gone($subject)
+ {
+ // All the optimized callback was doing now, is prepend some text to the URL.
+ // We don't need a callback for that, and that should be clearly faster.
+ return preg_replace('~(!\[.+?\]\()(?!\w++://)(\S*(?:\s*+".+?")?\))~', '$1http://BASE/$2', $subject);
+ }
+
+}
\ No newline at end of file
diff --git a/includes/kohana/modules/codebench/classes/bench/mddoincludeviews.php b/includes/kohana/modules/codebench/classes/bench/mddoincludeviews.php
new file mode 100644
index 00000000..9cac3d60
--- /dev/null
+++ b/includes/kohana/modules/codebench/classes/bench/mddoincludeviews.php
@@ -0,0 +1,50 @@
+
+ */
+class Bench_MDDoIncludeViews extends Codebench {
+
+ public $description =
+ 'Optimization for the doIncludeViews() method of Kohana_Kodoc_Markdown
+ for the Kohana Userguide.';
+
+ public $loops = 10000;
+
+ public $subjects = array
+ (
+ // Valid matches
+ '{{one}} two {{three}}',
+ '{{userguide/examples/hello_world_error}}',
+
+ // Invalid matches
+ '{}',
+ '{{}}',
+ '{{userguide/examples/hello_world_error}',
+ '{{userguide/examples/hello_world_error }}',
+ '{{userguide/examples/{{hello_world_error }}',
+ );
+
+ public function bench_original($subject)
+ {
+ preg_match_all('/{{(\S+?)}}/m', $subject, $matches, PREG_SET_ORDER);
+ return $matches;
+ }
+
+ public function bench_possessive($subject)
+ {
+ // Using a possessive character class
+ // Removed useless /m modifier
+ preg_match_all('/{{([^\s{}]++)}}/', $subject, $matches, PREG_SET_ORDER);
+ return $matches;
+ }
+
+ public function bench_lookaround($subject)
+ {
+ // Using lookaround to move $mathes[1] into $matches[0]
+ preg_match_all('/(?<={{)[^\s{}]++(?=}})/', $subject, $matches, PREG_SET_ORDER);
+ return $matches;
+ }
+
+}
\ No newline at end of file
diff --git a/includes/kohana/modules/codebench/classes/bench/stripnullbytes.php b/includes/kohana/modules/codebench/classes/bench/stripnullbytes.php
new file mode 100644
index 00000000..4d28853e
--- /dev/null
+++ b/includes/kohana/modules/codebench/classes/bench/stripnullbytes.php
@@ -0,0 +1,37 @@
+
+ */
+class Bench_StripNullBytes extends Codebench {
+
+ public $description =
+ 'String replacement comparisons related to #2676.';
+
+ public $loops = 1000;
+
+ public $subjects = array
+ (
+ "\0",
+ "\0\0\0\0\0\0\0\0\0\0",
+ "bla\0bla\0bla\0bla\0bla\0bla\0bla\0bla\0bla\0bla",
+ "blablablablablablablablablablablablablablablabla",
+ );
+
+ public function bench_str_replace($subject)
+ {
+ return str_replace("\0", '', $subject);
+ }
+
+ public function bench_strtr($subject)
+ {
+ return strtr($subject, array("\0" => ''));
+ }
+
+ public function bench_preg_replace($subject)
+ {
+ return preg_replace('~\0+~', '', $subject);
+ }
+
+}
\ No newline at end of file
diff --git a/includes/kohana/modules/codebench/classes/bench/transliterate.php b/includes/kohana/modules/codebench/classes/bench/transliterate.php
new file mode 100644
index 00000000..aff86931
--- /dev/null
+++ b/includes/kohana/modules/codebench/classes/bench/transliterate.php
@@ -0,0 +1,65 @@
+
+ */
+class Bench_Transliterate extends Codebench {
+
+ public $description =
+ 'Inspired by:
+ http://forum.kohanaframework.org/comments.php?DiscussionID=6113';
+
+ public $loops = 10;
+
+ public $subjects = array
+ (
+ // ASCII
+ 'a', 'b', 'c', 'd', '1', '2', '3',
+
+ // Non-ASCII
+ 'à', 'ô', 'ď', 'ḟ', 'ë', 'š', 'ơ',
+ 'ß', 'ă', 'ř', 'ț', 'ň', 'ā', 'ķ',
+ 'ŝ', 'ỳ', 'ņ', 'ĺ', 'ħ', 'ṗ', 'ó',
+ 'ú', 'ě', 'é', 'ç', 'ẁ', 'ċ', 'õ',
+ 'ṡ', 'ø', 'ģ', 'ŧ', 'ș', 'ė', 'ĉ',
+ 'ś', 'î', 'ű', 'ć', 'ę', 'ŵ', 'ṫ',
+ 'ū', 'č', 'ö', 'è', 'ŷ', 'ą', 'ł',
+ 'ų', 'ů', 'ş', 'ğ', 'ļ', 'ƒ', 'ž',
+ 'ẃ', 'ḃ', 'å', 'ì', 'ï', 'ḋ', 'ť',
+ 'ŗ', 'ä', 'í', 'ŕ', 'ê', 'ü', 'ò',
+ 'ē', 'ñ', 'ń', 'ĥ', 'ĝ', 'đ', 'ĵ',
+ 'ÿ', 'ũ', 'ŭ', 'ư', 'ţ', 'ý', 'ő',
+ 'â', 'ľ', 'ẅ', 'ż', 'ī', 'ã', 'ġ',
+ 'ṁ', 'ō', 'ĩ', 'ù', 'į', 'ź', 'á',
+ 'û', 'þ', 'ð', 'æ', 'µ', 'ĕ', 'ı',
+ 'À', 'Ô', 'Ď', 'Ḟ', 'Ë', 'Š', 'Ơ',
+ 'Ă', 'Ř', 'Ț', 'Ň', 'Ā', 'Ķ', 'Ĕ',
+ 'Ŝ', 'Ỳ', 'Ņ', 'Ĺ', 'Ħ', 'Ṗ', 'Ó',
+ 'Ú', 'Ě', 'É', 'Ç', 'Ẁ', 'Ċ', 'Õ',
+ 'Ṡ', 'Ø', 'Ģ', 'Ŧ', 'Ș', 'Ė', 'Ĉ',
+ 'Ś', 'Î', 'Ű', 'Ć', 'Ę', 'Ŵ', 'Ṫ',
+ 'Ū', 'Č', 'Ö', 'È', 'Ŷ', 'Ą', 'Ł',
+ 'Ų', 'Ů', 'Ş', 'Ğ', 'Ļ', 'Ƒ', 'Ž',
+ 'Ẃ', 'Ḃ', 'Å', 'Ì', 'Ï', 'Ḋ', 'Ť',
+ 'Ŗ', 'Ä', 'Í', 'Ŕ', 'Ê', 'Ü', 'Ò',
+ 'Ē', 'Ñ', 'Ń', 'Ĥ', 'Ĝ', 'Đ', 'Ĵ',
+ 'Ÿ', 'Ũ', 'Ŭ', 'Ư', 'Ţ', 'Ý', 'Ő',
+ 'Â', 'Ľ', 'Ẅ', 'Ż', 'Ī', 'Ã', 'Ġ',
+ 'Ṁ', 'Ō', 'Ĩ', 'Ù', 'Į', 'Ź', 'Á',
+ 'Û', 'Þ', 'Ð', 'Æ', 'İ',
+ );
+
+ public function bench_utf8($subject)
+ {
+ return UTF8::transliterate_to_ascii($subject);
+ }
+
+ public function bench_iconv($subject)
+ {
+ // Note: need to suppress errors on iconv because some chars trigger the following notice:
+ // "Detected an illegal character in input string"
+ return preg_replace('~[^-a-z0-9]+~i', '', @iconv('UTF-8', 'ASCII//TRANSLIT', $subject));
+ }
+
+}
\ No newline at end of file
diff --git a/includes/kohana/modules/codebench/classes/bench/urlsite.php b/includes/kohana/modules/codebench/classes/bench/urlsite.php
new file mode 100644
index 00000000..0db347d8
--- /dev/null
+++ b/includes/kohana/modules/codebench/classes/bench/urlsite.php
@@ -0,0 +1,123 @@
+
+ */
+class Bench_URLSite extends Codebench {
+
+ public $description = 'http://dev.kohanaframework.org/issues/3110';
+
+ public $loops = 1000;
+
+ public $subjects = array
+ (
+ '',
+ 'news',
+ 'news/',
+ '/news/',
+ 'news/page/5',
+ 'news/page:5',
+ 'http://example.com/',
+ 'http://example.com/hello',
+ 'http://example.com:80/',
+ 'http://user:pass@example.com/',
+ );
+
+ public function __construct()
+ {
+ foreach ($this->subjects as $subject)
+ {
+ // Automatically create URIs with query string and/or fragment part appended
+ $this->subjects[] = $subject.'?query=string';
+ $this->subjects[] = $subject.'#fragment';
+ $this->subjects[] = $subject.'?query=string#fragment';
+ }
+
+ parent::__construct();
+ }
+
+ public function bench_original($uri)
+ {
+ // Get the path from the URI
+ $path = trim(parse_url($uri, PHP_URL_PATH), '/');
+
+ if ($query = parse_url($uri, PHP_URL_QUERY))
+ {
+ $query = '?'.$query;
+ }
+
+ if ($fragment = parse_url($uri, PHP_URL_FRAGMENT))
+ {
+ $fragment = '#'.$fragment;
+ }
+
+ return $path.$query.$fragment;
+ }
+
+ public function bench_explode($uri)
+ {
+ // Chop off possible scheme, host, port, user and pass parts
+ $path = preg_replace('~^[-a-z0-9+.]++://[^/]++/?~', '', trim($uri, '/'));
+
+ $fragment = '';
+ $explode = explode('#', $path, 2);
+ if (isset($explode[1]))
+ {
+ $path = $explode[0];
+ $fragment = '#'.$explode[1];
+ }
+
+ $query = '';
+ $explode = explode('?', $path, 2);
+ if (isset($explode[1]))
+ {
+ $path = $explode[0];
+ $query = '?'.$explode[1];
+ }
+
+ return $path.$query.$fragment;
+ }
+
+ public function bench_regex($uri)
+ {
+ preg_match('~^(?:[-a-z0-9+.]++://[^/]++/?)?([^?#]++)?(\?[^#]*+)?(#.*)?~', trim($uri, '/'), $matches);
+ $path = Arr::get($matches, 1, '');
+ $query = Arr::get($matches, 2, '');
+ $fragment = Arr::get($matches, 3, '');
+
+ return $path.$query.$fragment;
+ }
+
+ public function bench_regex_without_arrget($uri)
+ {
+ preg_match('~^(?:[-a-z0-9+.]++://[^/]++/?)?([^?#]++)?(\?[^#]*+)?(#.*)?~', trim($uri, '/'), $matches);
+ $path = isset($matches[1]) ? $matches[1] : '';
+ $query = isset($matches[2]) ? $matches[2] : '';
+ $fragment = isset($matches[3]) ? $matches[3] : '';
+
+ return $path.$query.$fragment;
+ }
+
+ // And then I thought, why do all the work of extracting the query and fragment parts and then reappending them?
+ // Just leaving them alone should be fine, right? As a bonus we get a very nice speed boost.
+ public function bench_less_is_more($uri)
+ {
+ // Chop off possible scheme, host, port, user and pass parts
+ $path = preg_replace('~^[-a-z0-9+.]++://[^/]++/?~', '', trim($uri, '/'));
+
+ return $path;
+ }
+
+ public function bench_less_is_more_with_strpos_optimization($uri)
+ {
+ if (strpos($uri, '://') !== FALSE)
+ {
+ // Chop off possible scheme, host, port, user and pass parts
+ $uri = preg_replace('~^[-a-z0-9+.]++://[^/]++/?~', '', trim($uri, '/'));
+ }
+
+ return $uri;
+ }
+
+}
\ No newline at end of file
diff --git a/includes/kohana/modules/codebench/classes/bench/userfuncarray.php b/includes/kohana/modules/codebench/classes/bench/userfuncarray.php
new file mode 100644
index 00000000..f53d0c66
--- /dev/null
+++ b/includes/kohana/modules/codebench/classes/bench/userfuncarray.php
@@ -0,0 +1,58 @@
+
+ */
+class Bench_UserFuncArray extends Codebench {
+
+ public $description =
+ 'Testing the speed difference of using call_user_func_array
+ compared to counting args and doing manual calls.';
+
+ public $loops = 100000;
+
+ public $subjects = array
+ (
+ // Argument sets
+ array(),
+ array('one'),
+ array('one', 'two'),
+ array('one', 'two', 'three'),
+ );
+
+ public function bench_count_args($args)
+ {
+ $name = 'callme';
+ switch (count($args))
+ {
+ case 1:
+ $this->$name($args[0]);
+ break;
+ case 2:
+ $this->$name($args[0], $args[1]);
+ break;
+ case 3:
+ $this->$name($args[0], $args[1], $args[2]);
+ break;
+ case 4:
+ $this->$name($args[0], $args[1], $args[2], $args[3]);
+ break;
+ default:
+ call_user_func_array(array($this, $name), $args);
+ break;
+ }
+ }
+
+ public function bench_direct_call($args)
+ {
+ $name = 'callme';
+ call_user_func_array(array($this, $name), $args);
+ }
+
+ protected function callme()
+ {
+ return count(func_get_args());
+ }
+
+}
\ No newline at end of file
diff --git a/includes/kohana/modules/codebench/classes/bench/validcolor.php b/includes/kohana/modules/codebench/classes/bench/validcolor.php
new file mode 100644
index 00000000..8d046089
--- /dev/null
+++ b/includes/kohana/modules/codebench/classes/bench/validcolor.php
@@ -0,0 +1,116 @@
+
+ */
+class Bench_ValidColor extends Codebench {
+
+ public $description =
+ 'Optimization for Validate::color().
+ See: http://forum.kohanaphp.com/comments.php?DiscussionID=2192.
+
+ Note that the methods with an _invalid suffix contain flawed regexes and should be
+ completely discarded. I left them in here for educational purposes, and to remind myself
+ to think harder and test more thoroughly. It can\'t be that I only found out so late in
+ the game. For the regex explanation have a look at the forum topic mentioned earlier.';
+
+ public $loops = 10000;
+
+ public $subjects = array
+ (
+ // Valid colors
+ 'aaA',
+ '123',
+ '000000',
+ '#123456',
+ '#abcdef',
+
+ // Invalid colors
+ 'ggg',
+ '1234',
+ '#1234567',
+ "#000\n",
+ '}§è!çà%$z',
+ );
+
+ // Note that I added the D modifier to corey's regexes. We need to match exactly
+ // the same if we want the benchmarks to be of any value.
+ public function bench_corey_regex_1_invalid($subject)
+ {
+ return (bool) preg_match('/^#?([0-9a-f]{1,2}){3}$/iD', $subject);
+ }
+
+ public function bench_corey_regex_2($subject)
+ {
+ return (bool) preg_match('/^#?([0-9a-f]){3}(([0-9a-f]){3})?$/iD', $subject);
+ }
+
+ // Optimized corey_regex_1
+ // Using non-capturing parentheses and a possessive interval
+ public function bench_geert_regex_1a_invalid($subject)
+ {
+ return (bool) preg_match('/^#?(?:[0-9a-f]{1,2}+){3}$/iD', $subject);
+ }
+
+ // Optimized corey_regex_2
+ // Removed useless parentheses, made the remaining ones non-capturing
+ public function bench_geert_regex_2a($subject)
+ {
+ return (bool) preg_match('/^#?[0-9a-f]{3}(?:[0-9a-f]{3})?$/iD', $subject);
+ }
+
+ // Optimized geert_regex_1a
+ // Possessive "#"
+ public function bench_geert_regex_1b_invalid($subject)
+ {
+ return (bool) preg_match('/^#?+(?:[0-9a-f]{1,2}+){3}$/iD', $subject);
+ }
+
+ // Optimized geert_regex_2a
+ // Possessive "#"
+ public function bench_geert_regex_2b($subject)
+ {
+ return (bool) preg_match('/^#?+[0-9a-f]{3}(?:[0-9a-f]{3})?$/iD', $subject);
+ }
+
+ // Using \z instead of $
+ public function bench_salathe_regex_1($subject)
+ {
+ return (bool) preg_match('/^#?+[0-9a-f]{3}(?:[0-9a-f]{3})?\z/i', $subject);
+ }
+
+ // Using \A instead of ^
+ public function bench_salathe_regex_2($subject)
+ {
+ return (bool) preg_match('/\A#?+[0-9a-f]{3}(?:[0-9a-f]{3})?\z/i', $subject);
+ }
+
+ // A solution without regex
+ public function bench_geert_str($subject)
+ {
+ if ($subject[0] === '#')
+ {
+ $subject = substr($subject, 1);
+ }
+
+ $strlen = strlen($subject);
+ return (($strlen === 3 OR $strlen === 6) AND ctype_xdigit($subject));
+ }
+
+ // An ugly, but fast, solution without regex
+ public function bench_salathe_str($subject)
+ {
+ if ($subject[0] === '#')
+ {
+ $subject = substr($subject, 1);
+ }
+
+ // TRUE if:
+ // 1. $subject is 6 or 3 chars long
+ // 2. $subject contains only hexadecimal digits
+ return (((isset($subject[5]) AND ! isset($subject[6])) OR
+ (isset($subject[2]) AND ! isset($subject[3])))
+ AND ctype_xdigit($subject));
+ }
+}
\ No newline at end of file
diff --git a/includes/kohana/modules/codebench/classes/bench/validurl.php b/includes/kohana/modules/codebench/classes/bench/validurl.php
new file mode 100644
index 00000000..3c88675d
--- /dev/null
+++ b/includes/kohana/modules/codebench/classes/bench/validurl.php
@@ -0,0 +1,105 @@
+
+ */
+class Bench_ValidURL extends Codebench {
+
+ public $description =
+ 'filter_var vs regex:
+ http://dev.kohanaframework.org/issues/2847';
+
+ public $loops = 1000;
+
+ public $subjects = array
+ (
+ // Valid
+ 'http://google.com',
+ 'http://google.com/',
+ 'http://google.com/?q=abc',
+ 'http://google.com/#hash',
+ 'http://localhost',
+ 'http://hello-world.pl',
+ 'http://hello--world.pl',
+ 'http://h.e.l.l.0.pl',
+ 'http://server.tld/get/info',
+ 'http://127.0.0.1',
+ 'http://127.0.0.1:80',
+ 'http://user@127.0.0.1',
+ 'http://user:pass@127.0.0.1',
+ 'ftp://my.server.com',
+ 'rss+xml://rss.example.com',
+
+ // Invalid
+ 'http://google.2com',
+ 'http://google.com?q=abc',
+ 'http://google.com#hash',
+ 'http://hello-.pl',
+ 'http://hel.-lo.world.pl',
+ 'http://ww£.google.com',
+ 'http://127.0.0.1234',
+ 'http://127.0.0.1.1',
+ 'http://user:@127.0.0.1',
+ "http://finalnewline.com\n",
+ );
+
+ public function bench_filter_var($url)
+ {
+ return (bool) filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_HOST_REQUIRED);
+ }
+
+ public function bench_regex($url)
+ {
+ // Based on http://www.apps.ietf.org/rfc/rfc1738.html#sec-5
+ if ( ! preg_match(
+ '~^
+
+ # scheme
+ [-a-z0-9+.]++://
+
+ # username:password (optional)
+ (?:
+ [-a-z0-9$_.+!*\'(),;?&=%]++ # username
+ (?::[-a-z0-9$_.+!*\'(),;?&=%]++)? # password (optional)
+ @
+ )?
+
+ (?:
+ # ip address
+ \d{1,3}+(?:\.\d{1,3}+){3}+
+
+ | # or
+
+ # hostname (captured)
+ (
+ (?!-)[-a-z0-9]{1,63}+(? 253)
+ return FALSE;
+
+ // An extra check for the top level domain
+ // It must start with a letter
+ $tld = ltrim(substr($matches[1], (int) strrpos($matches[1], '.')), '.');
+ return ctype_alpha($tld[0]);
+ }
+
+}
\ No newline at end of file
diff --git a/includes/kohana/modules/codebench/classes/codebench.php b/includes/kohana/modules/codebench/classes/codebench.php
new file mode 100644
index 00000000..0f4c55cd
--- /dev/null
+++ b/includes/kohana/modules/codebench/classes/codebench.php
@@ -0,0 +1,3 @@
+request->redirect('codebench/'.trim($_POST['class']));
+
+ // Pass the class name on to the view
+ $this->template->class = (string) $class;
+
+ // Try to load the class, then run it
+ if (Kohana::auto_load($class) === TRUE)
+ {
+ $codebench = new $class;
+ $this->template->codebench = $codebench->run();
+ }
+ }
+}
diff --git a/includes/kohana/modules/codebench/classes/kohana/codebench.php b/includes/kohana/modules/codebench/classes/kohana/codebench.php
new file mode 100644
index 00000000..d8a71c17
--- /dev/null
+++ b/includes/kohana/modules/codebench/classes/kohana/codebench.php
@@ -0,0 +1,217 @@
+ 'A',
+ 150 => 'B',
+ 200 => 'C',
+ 300 => 'D',
+ 500 => 'E',
+ 'default' => 'F',
+ );
+
+ /**
+ * Constructor.
+ *
+ * @return void
+ */
+ public function __construct()
+ {
+ // Set the maximum execution time
+ set_time_limit(Kohana::config('codebench')->max_execution_time);
+ }
+
+ /**
+ * Runs Codebench on the extending class.
+ *
+ * @return array benchmark output
+ */
+ public function run()
+ {
+ // Array of all methods to loop over
+ $methods = array_filter(get_class_methods($this), array($this, '_method_filter'));
+
+ // Make sure the benchmark runs at least once,
+ // also if no subject data has been provided.
+ if (empty($this->subjects))
+ {
+ $this->subjects = array('NULL' => NULL);
+ }
+
+ // Initialize benchmark output
+ $codebench = array
+ (
+ 'class' => get_class($this),
+ 'description' => $this->description,
+ 'loops' => array
+ (
+ 'base' => (int) $this->loops,
+ 'total' => (int) $this->loops * count($this->subjects) * count($methods),
+ ),
+ 'subjects' => $this->subjects,
+ 'benchmarks' => array(),
+ );
+
+ // Benchmark each method
+ foreach ($methods as $method)
+ {
+ // Initialize benchmark output for this method
+ $codebench['benchmarks'][$method] = array('time' => 0, 'memory' => 0);
+
+ // Using Reflection because simply calling $this->$method($subject) in the loop below
+ // results in buggy benchmark times correlating to the length of the method name.
+ $reflection = new ReflectionMethod(get_class($this), $method);
+
+ // Benchmark each subject on each method
+ foreach ($this->subjects as $subject_key => $subject)
+ {
+ // Prerun each method/subject combo before the actual benchmark loop.
+ // This way relatively expensive initial processes won't be benchmarked, e.g. autoloading.
+ // At the same time we capture the return here so we don't have to do that in the loop anymore.
+ $return = $reflection->invoke($this, $subject);
+
+ // Start the timer for one subject
+ $token = Profiler::start('codebench', $method.$subject_key);
+
+ // The heavy work
+ for ($i = 0; $i < $this->loops; ++$i)
+ {
+ $reflection->invoke($this, $subject);
+ }
+
+ // Stop and read the timer
+ $benchmark = Profiler::total($token);
+
+ // Benchmark output specific to the current method and subject
+ $codebench['benchmarks'][$method]['subjects'][$subject_key] = array
+ (
+ 'return' => $return,
+ 'time' => $benchmark[0],
+ 'memory' => $benchmark[1],
+ );
+
+ // Update method totals
+ $codebench['benchmarks'][$method]['time'] += $benchmark[0];
+ $codebench['benchmarks'][$method]['memory'] += $benchmark[1];
+ }
+ }
+
+ // Initialize the fastest and slowest benchmarks for both methods and subjects, time and memory,
+ // these values will be overwritten using min() and max() later on.
+ // The 999999999 values look like a hack, I know, but they work,
+ // unless your method runs for more than 31 years or consumes over 1GB of memory.
+ $fastest_method = $fastest_subject = array('time' => 999999999, 'memory' => 999999999);
+ $slowest_method = $slowest_subject = array('time' => 0, 'memory' => 0);
+
+ // Find the fastest and slowest benchmarks, needed for the percentage calculations
+ foreach ($methods as $method)
+ {
+ // Update the fastest and slowest method benchmarks
+ $fastest_method['time'] = min($fastest_method['time'], $codebench['benchmarks'][$method]['time']);
+ $fastest_method['memory'] = min($fastest_method['memory'], $codebench['benchmarks'][$method]['memory']);
+ $slowest_method['time'] = max($slowest_method['time'], $codebench['benchmarks'][$method]['time']);
+ $slowest_method['memory'] = max($slowest_method['memory'], $codebench['benchmarks'][$method]['memory']);
+
+ foreach ($this->subjects as $subject_key => $subject)
+ {
+ // Update the fastest and slowest subject benchmarks
+ $fastest_subject['time'] = min($fastest_subject['time'], $codebench['benchmarks'][$method]['subjects'][$subject_key]['time']);
+ $fastest_subject['memory'] = min($fastest_subject['memory'], $codebench['benchmarks'][$method]['subjects'][$subject_key]['memory']);
+ $slowest_subject['time'] = max($slowest_subject['time'], $codebench['benchmarks'][$method]['subjects'][$subject_key]['time']);
+ $slowest_subject['memory'] = max($slowest_subject['memory'], $codebench['benchmarks'][$method]['subjects'][$subject_key]['memory']);
+ }
+ }
+
+ // Percentage calculations for methods
+ foreach ($codebench['benchmarks'] as & $method)
+ {
+ // Calculate percentage difference relative to fastest and slowest methods
+ $method['percent']['fastest']['time'] = (empty($fastest_method['time'])) ? 0 : $method['time'] / $fastest_method['time'] * 100;
+ $method['percent']['fastest']['memory'] = (empty($fastest_method['memory'])) ? 0 : $method['memory'] / $fastest_method['memory'] * 100;
+ $method['percent']['slowest']['time'] = (empty($slowest_method['time'])) ? 0 : $method['time'] / $slowest_method['time'] * 100;
+ $method['percent']['slowest']['memory'] = (empty($slowest_method['memory'])) ? 0 : $method['memory'] / $slowest_method['memory'] * 100;
+
+ // Assign a grade for time and memory to each method
+ $method['grade']['time'] = $this->_grade($method['percent']['fastest']['time']);
+ $method['grade']['memory'] = $this->_grade($method['percent']['fastest']['memory']);
+
+ // Percentage calculations for subjects
+ foreach ($method['subjects'] as & $subject)
+ {
+ // Calculate percentage difference relative to fastest and slowest subjects for this method
+ $subject['percent']['fastest']['time'] = (empty($fastest_subject['time'])) ? 0 : $subject['time'] / $fastest_subject['time'] * 100;
+ $subject['percent']['fastest']['memory'] = (empty($fastest_subject['memory'])) ? 0 : $subject['memory'] / $fastest_subject['memory'] * 100;
+ $subject['percent']['slowest']['time'] = (empty($slowest_subject['time'])) ? 0 : $subject['time'] / $slowest_subject['time'] * 100;
+ $subject['percent']['slowest']['memory'] = (empty($slowest_subject['memory'])) ? 0 : $subject['memory'] / $slowest_subject['memory'] * 100;
+
+ // Assign a grade letter for time and memory to each subject
+ $subject['grade']['time'] = $this->_grade($subject['percent']['fastest']['time']);
+ $subject['grade']['memory'] = $this->_grade($subject['percent']['fastest']['memory']);
+ }
+ }
+
+ return $codebench;
+ }
+
+ /**
+ * Callback for array_filter().
+ * Filters out all methods not to benchmark.
+ *
+ * @param string method name
+ * @return boolean
+ */
+ protected function _method_filter($method)
+ {
+ // Only benchmark methods with the "bench" prefix
+ return (substr($method, 0, 5) === 'bench');
+ }
+
+ /**
+ * Returns the applicable grade letter for a score.
+ *
+ * @param integer|double score
+ * @return string grade letter
+ */
+ protected function _grade($score)
+ {
+ foreach ($this->grades as $max => $grade)
+ {
+ if ($max === 'default')
+ continue;
+
+ if ($score <= $max)
+ return $grade;
+ }
+
+ return $this->grades['default'];
+ }
+}
diff --git a/includes/kohana/modules/codebench/config/codebench.php b/includes/kohana/modules/codebench/config/codebench.php
new file mode 100644
index 00000000..590186da
--- /dev/null
+++ b/includes/kohana/modules/codebench/config/codebench.php
@@ -0,0 +1,16 @@
+ 0,
+
+ /**
+ * Expand all benchmark details by default.
+ */
+ 'expand_all' => FALSE,
+
+);
diff --git a/includes/kohana/modules/codebench/init.php b/includes/kohana/modules/codebench/init.php
new file mode 100644
index 00000000..866cc341
--- /dev/null
+++ b/includes/kohana/modules/codebench/init.php
@@ -0,0 +1,8 @@
+)')
+ ->defaults(array(
+ 'controller' => 'codebench',
+ 'action' => 'index',
+ 'class' => NULL));
diff --git a/includes/kohana/modules/codebench/views/codebench.php b/includes/kohana/modules/codebench/views/codebench.php
new file mode 100644
index 00000000..19619503
--- /dev/null
+++ b/includes/kohana/modules/codebench/views/codebench.php
@@ -0,0 +1,258 @@
+
+
+
+
+
+
+
+ Codebench
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Remember to prefix the methods you want to benchmark with “bench”.
+ You might also want to overwrite Codebench->method_filter().
+
+
\ No newline at end of file
diff --git a/includes/kohana/modules/unittest/README.markdown b/includes/kohana/modules/unittest/README.markdown
new file mode 100644
index 00000000..f19404a3
--- /dev/null
+++ b/includes/kohana/modules/unittest/README.markdown
@@ -0,0 +1,70 @@
+# Kohana-PHPUnit integration
+
+This module integrates PHPUnit with Kohana.
+
+If you look through any of the tests provided in this module you'll probably notice all theHorribleCamelCase.
+I've chosen to do this because it's part of the PHPUnit coding conventions and is required for certain features such as auto documentation.
+
+## Requirements
+
+* [PHPUnit](http://www.phpunit.de/) >= 3.4
+
+### Optional extras
+
+* The [Archive module](http://github.com/BRMatt/kohana-archive) is required if you want to download code coverage reports from the web ui, however you can also view them without downloading.
+
+## Installation
+
+**Step 0**: Download this module!
+
+To get it from git execute the following command in the root of your project:
+
+ $ git submodule add git://github.com/kohana/unittest.git modules/unittest
+
+And watch the gitorious magic...
+
+Of course, you can always download the code from the [github project](http://github.com/kohana/unittest) as an archive.
+
+The following instructions will assume you've moved it to `modules/unittest`, if you haven't then you should update all paths accordingly.
+
+**Step 1**: Enable this module in your bootstrap file:
+
+ /**
+ * Enable modules. Modules are referenced by a relative or absolute path.
+ */
+ Kohana::modules(array(
+ 'unittest' => MODPATH.'unittest', // PHPUnit integration
+ ));
+
+**Step 2**: In your app's bootstrap file modify the lines where the request is handled, which by default looks like:
+
+ /**
+ * Execute the main request using PATH_INFO. If no URI source is specified,
+ * the URI will be automatically detected.
+ */
+ echo Request::instance($_SERVER['PATH_INFO'])
+ ->execute()
+ ->send_headers()
+ ->response;
+
+To:
+
+ if ( ! defined('SUPPRESS_REQUEST'))
+ {
+ /**
+ * Execute the main request using PATH_INFO. If no URI source is specified,
+ * the URI will be automatically detected.
+ */
+ echo Request::instance($_SERVER['PATH_INFO'])
+ ->execute()
+ ->send_headers()
+ ->response;
+ }
+
+**Step 3**: Create a folder called `unittest` in your app's cache dir (`APPPATH/cache`). If you don't want to use this path for storing generated reports, skip this step and change the config file.
+
+Note: make sure the settings in `config/unittest.php` are correct for your environment. If they aren't, copy the file to `application/config/unittest.php` and change the values accordingly.
+
+**Step 4**: Start testing!
+
+You can find more info and tutorials in the [guide/](http://github.com/kohana/unittest/tree/master/guide/) directory.
diff --git a/includes/kohana/modules/unittest/classes/controller/unittest.php b/includes/kohana/modules/unittest/classes/controller/unittest.php
new file mode 100644
index 00000000..283be07b
--- /dev/null
+++ b/includes/kohana/modules/unittest/classes/controller/unittest.php
@@ -0,0 +1,359 @@
+
+ * @author Paul Banks
+ * @copyright (c) 2008-2009 Kohana Team
+ * @license http://kohanaphp.com/license
+ */
+
+Class Controller_UnitTest extends Controller_Template
+{
+ /**
+ * Whether the archive module is available
+ * @var boolean
+ */
+ protected $cc_archive_available = FALSE;
+
+ /**
+ * Unittest config
+ * @var Kohana_Config
+ */
+ protected $config = NULL;
+
+ /**
+ * The uri by which the report uri will be executed
+ * @var string
+ */
+ protected $report_uri = '';
+
+ /**
+ * The uri by which the run action will be executed
+ * @var string
+ */
+ protected $run_uri = '';
+
+ /**
+ * Is the XDEBUG extension loaded?
+ * @var boolean
+ */
+ protected $xdebug_loaded = FALSE;
+
+ /**
+ * Template
+ * @var string
+ */
+ public $template = 'unittest/layout';
+
+ /**
+ * Loads test suite
+ */
+ public function before()
+ {
+ parent::before();
+
+ if ( ! Kohana_Tests::enabled())
+ {
+ // Pretend this is a normal 404 error...
+ $this->status = 404;
+
+ throw new Kohana_Request_Exception('Unable to find a route to match the URI: :uri',
+ array(':uri' => $this->request->uri));
+ }
+
+ // Prevent the whitelist from being autoloaded, but allow the blacklist
+ // to be loaded
+ Kohana_Tests::configure_environment(FALSE);
+
+ $this->config = Kohana::config('unittest');
+
+ // This just stops some very very long lines
+ $route = Route::get('unittest');
+ $this->report_uri = $route->uri(array('action' => 'report'));
+ $this->run_uri = $route->uri(array('action' => 'run'));
+
+ // Switch used to disable cc settings
+ $this->xdebug_loaded = extension_loaded('xdebug');
+ $this->cc_archive_enabled = class_exists('Archive');
+
+ Kohana_View::set_global('xdebug_enabled', $this->xdebug_loaded);
+ Kohana_View::set_global('cc_archive_enabled', $this->cc_archive_enabled);
+ }
+
+ /**
+ * Handles index page for /unittest/ and /unittest/index/
+ */
+ public function action_index()
+ {
+ $this->template->body = View::factory('unittest/index')
+ ->set('run_uri', $this->run_uri)
+ ->set('report_uri', $this->report_uri)
+ ->set('whitelistable_items', $this->get_whitelistable_items())
+ ->set('groups', $this->get_groups_list(Kohana_Tests::suite()));
+ }
+
+ /**
+ * Handles report generation
+ */
+ public function action_report()
+ {
+ // Fairly foolproof
+ if ( ! $this->config->cc_report_path AND ! class_exists('Archive'))
+ {
+ throw new Kohana_Exception('Cannot generate report');
+ }
+
+ // We don't want to use the HTML layout, we're sending the user 100111011100110010101100
+ $this->auto_render = FALSE;
+
+ $suite = Kohana_Tests::suite();
+ $temp_path = rtrim($this->config->temp_path, '/').'/';
+ $group = (array) Arr::get($_GET, 'group', array());
+
+ // Stop unittest from interpretting "all groups" as "no groups"
+ if (empty($group) OR empty($group[0]))
+ {
+ $group = array();
+ }
+
+ if (Arr::get($_GET, 'use_whitelist', FALSE))
+ {
+ $this->whitelist(Arr::get($_GET, 'whitelist', array()));
+ }
+
+ $runner = new Kohana_Unittest_Runner($suite);
+
+ // If the user wants to download a report
+ if ($this->cc_archive_enabled AND Arr::get($_GET, 'archive') === '1')
+ {
+ // $report is the actual directory of the report,
+ // $folder is the name component of directory
+ list($report, $folder) = $runner->generate_report($group, $temp_path);
+
+ $archive = Archive::factory('zip');
+
+ // TODO: Include the test results?
+ $archive->add($report, 'report', TRUE);
+
+ $filename = $folder.'.zip';
+
+ $archive->save($temp_path.$filename);
+
+ // It'd be nice to clear up afterwards but by deleting the report dir we corrupt the archive
+ // And once the archive has been sent to the user Request stops the script so we can't delete anything
+ // It'll be up to the user to delete files periodically
+ $this->request->send_file($temp_path.$filename, $filename);
+ }
+ else
+ {
+ $folder = trim($this->config->cc_report_path, '/').'/';
+ $path = DOCROOT.$folder;
+
+ if ( ! file_exists($path))
+ {
+ throw new Kohana_Exception('Report directory :dir does not exist', array(':dir' => $path));
+ }
+
+ if ( ! is_writable($path))
+ {
+ throw new Kohana_Exception('Script doesn\'t have permission to write to report dir :dir ', array(':dir' => $path));
+ }
+
+ $runner->generate_report($group, $path, FALSE);
+
+ $this->request->redirect(URL::base(FALSE, TRUE).$folder.'index.html');
+ }
+ }
+
+ /**
+ * Handles test running interface
+ */
+ public function action_run()
+ {
+ $this->template->body = View::factory('unittest/results');
+
+ // Get the test suite and work out which groups we're testing
+ $suite = Kohana_Tests::suite();
+ $group = (array) Arr::get($_GET, 'group', array());
+
+
+ // Stop phpunit from interpretting "all groups" as "no groups"
+ if (empty($group) OR empty($group[0]))
+ {
+ $group = array();
+ }
+
+ // Only collect code coverage if the user asked for it
+ $collect_cc = (bool) Arr::get($_GET, 'collect_cc', FALSE);
+
+ if ($collect_cc AND Arr::get($_GET, 'use_whitelist', FALSE))
+ {
+ $whitelist = $this->whitelist(Arr::get($_GET, 'whitelist', array()));
+ }
+
+ $runner = new Kohana_Unittest_Runner($suite);
+
+ try
+ {
+ $runner->run($group, $collect_cc);
+
+ if ($collect_cc)
+ {
+ $this->template->body->set('coverage', $runner->calculate_cc_percentage());
+ }
+
+ if (isset($whitelist))
+ {
+ $this->template->body->set('coverage_explanation', $this->nice_whitelist_explanation($whitelist));
+ }
+ }
+ catch(Kohana_Exception $e)
+ {
+ // Code coverage is not allowed, possibly xdebug disabled?
+ // TODO: Tell the user this?
+ $runner->run($group);
+ }
+
+ // Show some results
+ $this->template->body
+ ->set('results', $runner->results)
+ ->set('totals', $runner->totals)
+ ->set('time', $this->nice_time($runner->time))
+
+ // Sets group to the currently selected group, or default all groups
+ ->set('group', Arr::get($this->get_groups_list($suite), reset($group), 'All groups'))
+ ->set('groups', $this->get_groups_list($suite))
+
+ ->set('report_uri', $this->report_uri.url::query())
+
+ // Whitelist related stuff
+ ->set('whitelistable_items', $this->get_whitelistable_items())
+ ->set('whitelisted_items', isset($whitelist) ? array_keys($whitelist) : array())
+ ->set('whitelist', ! empty($whitelist));
+ }
+
+ /**
+ * Get the list of groups from the test suite, sorted with 'All groups' prefixed
+ *
+ * @return array Array of groups in the test suite
+ */
+ protected function get_groups_list($suite)
+ {
+ // Make groups aray suitable for drop down
+ $groups = $suite->getGroups();
+ if (count($groups) > 0)
+ {
+ sort($groups);
+ $groups = array_combine($groups, $groups);
+ }
+ return array('' => 'All Groups') + $groups;
+ }
+
+ /**
+ * Gets a list of items that are whitelistable
+ *
+ * @return array
+ */
+ protected function get_whitelistable_items()
+ {
+ static $whitelist;
+
+ if (count($whitelist))
+ {
+ return $whitelist;
+ }
+
+ $whitelist = array();
+
+ $whitelist['k_app'] = 'Application';
+
+ $k_modules = array_keys(Kohana::modules());
+
+ $whitelist += array_map('ucfirst', array_combine($k_modules, $k_modules));
+
+ $whitelist['k_sys'] = 'Kohana Core';
+
+ return $whitelist;
+ }
+
+ /**
+ * Whitelists a specified set of modules specified by the user
+ *
+ * @param array $modules
+ */
+ protected function whitelist(array $modules)
+ {
+ $k_modules = Kohana::modules();
+ $whitelist = array();
+
+ // Make sure our whitelist is valid
+ foreach ($modules as $item)
+ {
+ if (isset($k_modules[$item]))
+ {
+ $whitelist[$item] = $k_modules[$item];
+ }
+ elseif ($item === 'k_app')
+ {
+ $whitelist[$item] = APPPATH;
+ }
+ elseif ($item === 'k_sys')
+ {
+ $whitelist[$item] = SYSPATH;
+ }
+ }
+
+ if (count($whitelist))
+ {
+ Kohana_Tests::whitelist($whitelist);
+ }
+
+ return $whitelist;
+ }
+
+ /**
+ * Prettifies the list of whitelisted modules
+ *
+ * @param array Array of whitelisted items
+ * @return string
+ */
+ protected function nice_whitelist_explanation(array $whitelist)
+ {
+ $items = array_intersect_key($this->get_whitelistable_items(), $whitelist);
+
+ return implode(', ', $items);
+ }
+
+ protected function nice_time($time)
+ {
+ $parts = array();
+
+ if ($time > DATE::DAY)
+ {
+ $parts[] = floor($time/DATE::DAY).'d';
+ $time = $time % DATE::DAY;
+ }
+
+ if ($time > DATE::HOUR)
+ {
+ $parts[] = floor($time/DATE::HOUR).'h';
+ $time = $time % DATE::HOUR;
+ }
+
+ if ($time > DATE::MINUTE)
+ {
+ $parts[] = floor($time/DATE::MINUTE).'m';
+ $time = $time % DATE::MINUTE;
+ }
+
+ if ($time > 0)
+ {
+ $parts[] = round($time, 1).'s';
+ }
+
+ return implode(' ', $parts);
+ }
+} // End Controller_PHPUnit
diff --git a/includes/kohana/modules/unittest/classes/kohana/tests.php b/includes/kohana/modules/unittest/classes/kohana/tests.php
new file mode 100644
index 00000000..39fa3640
--- /dev/null
+++ b/includes/kohana/modules/unittest/classes/kohana/tests.php
@@ -0,0 +1,263 @@
+
+ * @author Paul Banks
+ * @copyright (c) 2008-2009 Kohana Team
+ * @license http://kohanaphp.com/license
+ */
+class Kohana_Tests
+{
+ static protected $cache = array();
+
+ /**
+ * Loads test files if they cannot be found by kohana
+ * @param $class
+ */
+ static function autoload($class)
+ {
+ $file = str_replace('_', '/', $class);
+
+ if ($file = Kohana::find_file('tests', $file))
+ {
+ require_once $file;
+ }
+ }
+
+ /**
+ * Configures the environment for testing
+ *
+ * Does the following:
+ *
+ * * Loads the phpunit framework (for the web ui)
+ * * Restores exception phpunit error handlers (for cli)
+ * * registeres an autoloader to load test files
+ */
+ static public function configure_environment($do_whitelist = TRUE, $do_blacklist = TRUE)
+ {
+ if ( ! class_exists('PHPUnit_Util_Filter', FALSE))
+ {
+ // Make sure the PHPUnit classes are available
+ require_once 'PHPUnit/Framework.php';
+ }
+
+ if (Kohana::$is_cli)
+ {
+ restore_exception_handler();
+ restore_error_handler();
+ }
+
+ spl_autoload_register(array('Kohana_Tests', 'autoload'));
+
+ Kohana_Tests::$cache = ($cache = Kohana::cache('unittest_whitelist_cache')) === NULL ? array() : $cache;
+
+ $config = Kohana::config('unittest');
+
+ if ($do_whitelist AND $config->use_whitelist)
+ {
+ self::whitelist();
+ }
+
+ if ($do_blacklist AND count($config['blacklist']))
+ {
+ foreach ($config->blacklist as $item)
+ {
+ if (is_dir($item))
+ {
+ PHPUnit_Util_Filter::addDirectoryToFilter($item);
+ }
+ else
+ {
+ PHPUnit_Util_Filter::addFileToFilter($item);
+ }
+ }
+ }
+ }
+
+ /**
+ * Helper function to see if unittest is enabled in the config
+ *
+ * @return boolean
+ */
+ static function enabled()
+ {
+ $p_environment = Kohana::config('unittest.environment');
+ $k_environment = Kohana::$environment;
+
+ return (is_array($p_environment) AND in_array($k_environment, $p_environment))
+ OR
+ ($k_environment === $p_environment);
+ }
+
+ /**
+ * Creates the test suite for kohana
+ *
+ * @return PHPUnit_Framework_TestSuite
+ */
+ static function suite()
+ {
+ static $suite = NULL;
+
+ if ($suite instanceof PHPUnit_Framework_TestSuite)
+ {
+ return $suite;
+ }
+
+ $files = Kohana::list_files('tests');
+
+ $suite = new PHPUnit_Framework_TestSuite();
+
+ self::addTests($suite, $files);
+
+ return $suite;
+ }
+
+ /**
+ * Add files to test suite $suite
+ *
+ * Uses recursion to scan subdirectories
+ *
+ * @param PHPUnit_Framework_TestSuite $suite The test suite to add to
+ * @param array $files Array of files to test
+ */
+ static function addTests(PHPUnit_Framework_TestSuite $suite, array $files)
+ {
+ foreach ($files as $file)
+ {
+ if (is_array($file))
+ {
+ self::addTests($suite, $file);
+ }
+ else
+ {
+ // Make sure we only include php files
+ if (is_file($file) AND substr($file, -strlen(EXT)) === EXT)
+ {
+ // The default PHPUnit TestCase extension
+ if ( ! strpos($file, 'TestCase'.EXT))
+ {
+ $suite->addTestFile($file);
+ }
+ else
+ {
+ require_once($file);
+ }
+
+ PHPUnit_Util_Filter::addFileToFilter($file);
+ }
+ }
+ }
+ }
+
+ /**
+ * Sets the whitelist
+ *
+ * If no directories are provided then the function'll load the whitelist
+ * set in the config file
+ *
+ * @param array $directories Optional directories to whitelist
+ */
+ static public function whitelist(array $directories = NULL)
+ {
+ if (empty($directories))
+ {
+ $directories = self::get_config_whitelist();
+ }
+
+ if (count($directories))
+ {
+ foreach ($directories as &$directory)
+ {
+ $directory = realpath($directory).'/';
+ }
+
+ // Only whitelist the "top" files in the cascading filesystem
+ self::set_whitelist(Kohana::list_files('classes', $directories));
+ }
+ }
+
+ /**
+ * Works out the whitelist from the config
+ * Used only on the CLI
+ *
+ * @returns array Array of directories to whitelist
+ */
+ static protected function get_config_whitelist()
+ {
+ $config = Kohana::config('unittest');
+ $directories = array();
+
+ if ($config->whitelist['app'])
+ {
+ $directories['k_app'] = APPPATH;
+ }
+
+ if ($modules = $config->whitelist['modules'])
+ {
+ $k_modules = Kohana::modules();
+
+ // Have to do this because kohana merges config...
+ // If you want to include all modules & override defaults then TRUE must be the first
+ // value in the modules array of your app/config/unittest file
+ if (array_search(TRUE, $modules, TRUE) === (count($modules) - 1))
+ {
+ $modules = $k_modules;
+ }
+ elseif (array_search(FALSE, $modules, TRUE) === FALSE)
+ {
+ $modules = array_intersect_key($k_modules, array_combine($modules, $modules));
+ }
+ else
+ {
+ // modules are disabled
+ $modules = array();
+ }
+
+ $directories += $modules;
+ }
+
+ if ($config->whitelist['system'])
+ {
+ $directories['k_sys'] = SYSPATH;
+ }
+
+ return $directories;
+ }
+
+ /**
+ * Recursively whitelists an array of files
+ *
+ * @param array $files Array of files to whitelist
+ */
+ static protected function set_whitelist($files)
+ {
+ foreach ($files as $file)
+ {
+ if (is_array($file))
+ {
+ self::set_whitelist($file);
+ }
+ else
+ {
+ if ( ! isset(Kohana_Tests::$cache[$file]))
+ {
+ $relative_path = substr($file, strrpos($file, 'classes'.DIRECTORY_SEPARATOR) + 8, -strlen(EXT));
+ $cascading_file = Kohana::find_file('classes', $relative_path);
+
+ // The theory is that if this file is the highest one in the cascading filesystem
+ // then it's safe to whitelist
+ Kohana_Tests::$cache[$file] = ($cascading_file === $file);
+ }
+
+ if (Kohana_Tests::$cache[$file])
+ {
+ PHPUnit_Util_Filter::addFileToWhitelist($file);
+ }
+ }
+ }
+ }
+
+}
diff --git a/includes/kohana/modules/unittest/classes/kohana/unittest/helpers.php b/includes/kohana/modules/unittest/classes/kohana/unittest/helpers.php
new file mode 100644
index 00000000..d20e5f75
--- /dev/null
+++ b/includes/kohana/modules/unittest/classes/kohana/unittest/helpers.php
@@ -0,0 +1,176 @@
+
+ * @author Paul Banks
+ * @copyright (c) 2008-2009 Kohana Team
+ * @license http://kohanaphp.com/license
+ */
+class Kohana_Unittest_Helpers
+{
+ /**
+ * Static variable used to work out whether we have an internet
+ * connection
+ * @see has_internet
+ * @var boolean
+ */
+ static protected $_has_internet = NULL;
+
+ /**
+ * Check for internet connectivity
+ *
+ * @return boolean Whether an internet connection is available
+ */
+ public static function has_internet()
+ {
+ if ( ! isset(self::$_has_internet))
+ {
+ // The @ operator is used here to avoid DNS errors when there is no connection.
+ $sock = @fsockopen("www.google.com", 80, $errno, $errstr, 1);
+
+ self::$_has_internet = (bool) $sock ? TRUE : FALSE;
+ }
+
+ return self::$_has_internet;
+ }
+
+ /**
+ * Helper function which replaces the "/" to OS-specific delimiter
+ *
+ * @param string $path
+ * @return string
+ */
+ static public function dir_separator($path)
+ {
+ return str_replace('/', DIRECTORY_SEPARATOR, $path);
+ }
+
+ /**
+ * Removes all cache files from the kohana cache dir
+ *
+ * @return void
+ */
+ static public function clean_cache_dir()
+ {
+ $cache_dir = opendir(Kohana::$cache_dir);
+
+ while($dir = readdir($cache_dir))
+ {
+ // Cache files are split into directories based on first two characters of hash
+ if ($dir[0] !== '.' AND strlen($dir) === 2)
+ {
+ $dir = self::dir_separator(Kohana::$cache_dir.'/'.$dir.'/');
+
+ $cache = opendir($dir);
+
+ while($file = readdir($cache))
+ {
+ if ($file[0] !== '.')
+ {
+ unlink($dir.$file);
+ }
+ }
+
+ closedir($cache);
+
+ rmdir($dir);
+ }
+ }
+
+ closedir($cache_dir);
+ }
+
+ /**
+ * Backup of the environment variables
+ * @see set_environment
+ * @var array
+ */
+ protected $_environment_backup = array();
+
+ /**
+ * Allows easy setting & backing up of enviroment config
+ *
+ * Option types are checked in the following order:
+ *
+ * * Server Var
+ * * Static Variable
+ * * Config option
+ *
+ * @param array $environment List of environment to set
+ */
+ public function set_environment(array $environment)
+ {
+ if ( ! count($environment))
+ return FALSE;
+
+ foreach ($environment as $option => $value)
+ {
+ $backup_needed = ! array_key_exists($option, $this->_environment_backup);
+
+ // Handle changing superglobals
+ if (in_array($option, array('_GET', '_POST', '_SERVER', '_FILES')))
+ {
+ // For some reason we need to do this in order to change the superglobals
+ global $$option;
+
+ if($backup_needed)
+ {
+ $this->_environment_backup[$option] = $$option;
+ }
+
+ // PHPUnit makes a backup of superglobals automatically
+ $$option = $value;
+ }
+ // If this is a static property i.e. Html::$windowed_urls
+ elseif (strpos($option, '::$') !== FALSE)
+ {
+ list($class, $var) = explode('::$', $option, 2);
+
+ $class = new ReflectionClass($class);
+
+ if ($backup_needed)
+ {
+ $this->_environment_backup[$option] = $class->getStaticPropertyValue($var);
+ }
+
+ $class->setStaticPropertyValue($var, $value);
+ }
+ // If this is an environment variable
+ elseif (preg_match('/^[A-Z_-]+$/', $option) OR isset($_SERVER[$option]))
+ {
+ if($backup_needed)
+ {
+ $this->_environment_backup[$option] = isset($_SERVER[$option]) ? $_SERVER[$option] : '';
+ }
+
+ $_SERVER[$option] = $value;
+ }
+ // Else we assume this is a config option
+ else
+ {
+ if ($backup_needed)
+ {
+ $this->_environment_backup[$option] = Kohana::config($option);
+ }
+
+ list($group, $var) = explode('.', $option, 2);
+
+ Kohana::config($group)->set($var, $value);
+ }
+ }
+ }
+
+ /**
+ * Restores the environment to the original state
+ *
+ * @chainable
+ * @return Kohana_Unittest_Helpers $this
+ */
+ public function restore_environment()
+ {
+ $this->set_environment($this->_environment_backup);
+ }
+}
diff --git a/includes/kohana/modules/unittest/classes/kohana/unittest/runner.php b/includes/kohana/modules/unittest/classes/kohana/unittest/runner.php
new file mode 100644
index 00000000..5a5486c7
--- /dev/null
+++ b/includes/kohana/modules/unittest/classes/kohana/unittest/runner.php
@@ -0,0 +1,304 @@
+
+ * @author Paul Banks
+ * @copyright (c) 2008-2009 Kohana Team
+ * @license http://kohanaphp.com/license
+ */
+Class Kohana_Unittest_Runner implements PHPUnit_Framework_TestListener
+{
+ /**
+ * Results
+ * @var array
+ */
+ protected $results = array(
+ 'errors' => array(),
+ 'failures' => array(),
+ 'skipped' => array(),
+ 'incomplete' => array(),
+ );
+
+ /**
+ * Test result totals
+ * @var array
+ */
+ protected $totals = array(
+ 'tests' => 0,
+ 'passed' => 0,
+ 'errors' => 0,
+ 'failures' => 0,
+ 'skipped' => 0,
+ 'incomplete' => 0,
+ 'assertions' => 0,
+ );
+
+ /**
+ * Info about the current test running
+ * @var array
+ */
+ protected $current = array();
+
+ /**
+ * Time for tests to run (seconds)
+ * @var float
+ */
+ protected $time = 0;
+
+ /**
+ * Result collector
+ * @var PHPUnit_Framework_TestResult
+ */
+ protected $result = NULL;
+
+ /**
+ * the test suite to run
+ * @var PHPUnit_Framework_TestSuite
+ */
+ protected $suite = NULL;
+
+ /**
+ * Constructor
+ *
+ * @param PHPUnit_Framework_TestSuite $suite The suite to test
+ * @param PHPUnit_Framework_TestResult $result Optional result object to use
+ */
+ function __construct(PHPUnit_Framework_TestSuite $suite, PHPUnit_Framework_TestResult $result = NULL)
+ {
+ if ($result === NULL)
+ {
+ $result = new PHPUnit_Framework_TestResult;
+ }
+
+ $result->addListener($this);
+
+ $this->suite = $suite;
+ $this->result = $result;
+ }
+
+ /**
+ * Magic getter to allow access to member variables
+ *
+ * @param string $var Variable to get
+ * @return mixed
+ */
+ function __get($var)
+ {
+ return $this->$var;
+ }
+
+ /**
+ * Calcualtes stats for each file covered by the code testing
+ *
+ * Each member of the returned array is formatted like so:
+ *
+ *
+ * array(
+ * 'coverage' => $coverage_percent_for_file,
+ * 'loc' => $lines_of_code,
+ * 'locExecutable' => $lines_of_executable_code,
+ * 'locExecuted' => $lines_of_code_executed
+ * );
+ *
+ *
+ * @return array Statistics for code coverage of each file
+ */
+ public function calculate_cc()
+ {
+ if ($this->result->getCollectCodeCoverageInformation())
+ {
+ $coverage = $this->result->getCodeCoverageInformation();
+
+ $coverage_summary = PHPUnit_Util_CodeCoverage::getSummary($coverage);
+
+ $stats = array();
+
+ foreach ($coverage_summary as $file => $_lines)
+ {
+ $stats[$file] = PHPUnit_Util_CodeCoverage::getStatistics($coverage_summary, $file);
+ }
+
+ return $stats;
+ }
+
+ return FALSE;
+ }
+
+ /**
+ * Calculates the percentage code coverage information
+ *
+ * @return boolean|float FALSE if cc is not enabled, float for coverage percent
+ */
+ public function calculate_cc_percentage()
+ {
+ if ($stats = $this->calculate_cc())
+ {
+ $executable = 0;
+ $executed = 0;
+
+ foreach ($stats as $stat)
+ {
+ $executable += $stat['locExecutable'];
+ $executed += $stat['locExecuted'];
+ }
+
+ return $executable > 0 ? ($executed / $executable) * 100 : 100;
+ }
+
+ return FALSE;
+ }
+
+ /**
+ * Generate a report using the specified $temp_path
+ *
+ * @param array $groups Groups to test
+ * @param string $temp_path Temporary path to use while generating report
+ */
+ public function generate_report(array $groups, $temp_path, $create_sub_dir = TRUE)
+ {
+ if ( ! is_writable($temp_path))
+ {
+ throw new Kohana_Exception('Temp path :path does not exist or is not writable by the webserver', array(':path' => $temp_path));
+ }
+
+ $folder_path = $temp_path;
+
+ if ($create_sub_dir === TRUE)
+ {
+ // Icky, highly unlikely, but do it anyway
+ // Basically adds "(n)" to the end of the filename until there's a free file
+ $count = 0;
+ do
+ {
+ $folder_name = date('Y-m-d_H:i:s')
+ .( ! empty($groups) ? '['.implode(',', $groups).']' : '')
+ .($count > 0 ? '('.$count.')' : '');
+ ++$count;
+ }
+ while(is_dir($folder_path.$folder_name));
+
+ $folder_path .= $folder_name;
+
+ mkdir($folder_path, 0777);
+ }
+ else
+ {
+ $folder_name = basename($folder_path);
+ }
+
+ $this->run($groups, TRUE);
+
+ require_once 'PHPUnit/Runner/Version.php';
+ require_once 'PHPUnit/Util/Report.php';
+
+ PHPUnit_Util_Report::render($this->result, $folder_path);
+
+ return array($folder_path, $folder_name);
+ }
+
+ /**
+ * Runs the test suite using the result specified in the constructor
+ *
+ * @param array $groups Optional array of groups to test
+ * @param bool $collect_cc Optional, Should code coverage be collected?
+ * @return Kohana_PHPUnit Instance of $this
+ */
+ public function run(array $groups = array(), $collect_cc = FALSE)
+ {
+ if ($collect_cc AND ! extension_loaded('xdebug'))
+ {
+ throw new Kohana_Exception('Code coverage cannot be collected because the xdebug extension is not loaded');
+ }
+
+ $this->result->collectCodeCoverageInformation((bool) $collect_cc);
+
+ // Run the tests.
+ $this->suite->run($this->result, FALSE, $groups);
+
+ return $this;
+ }
+
+ public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ $this->totals['errors']++;
+ $this->current['result'] = 'errors';
+ $this->current['message'] = $test->getStatusMessage();
+
+ }
+
+ public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
+ {
+ $this->totals['failures']++;
+ $this->current['result'] = 'failures';
+ $this->current['message'] = $test->getStatusMessage();
+ }
+
+ public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ $this->totals['incomplete']++;
+ $this->current['result'] = 'incomplete';
+ $this->current['message'] = $test->getStatusMessage();
+ }
+
+ public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
+ {
+ $this->totals['skipped']++;
+ $this->current['result'] = 'skipped';
+ $this->current['message'] = $test->getStatusMessage();
+ }
+
+ public function startTest(PHPUnit_Framework_Test $test)
+ {
+ $this->current['name'] = $test->getName(FALSE);
+ $this->current['description'] = $test->toString();
+ $this->current['result'] = 'passed';
+ }
+
+ public function endTest(PHPUnit_Framework_Test $test, $time)
+ {
+ // Add totals
+ $this->totals['tests']++;
+ $this->totals['assertions'] += $test->getNumAssertions();
+
+ // Handle passed tests
+ if ($this->current['result'] == 'passed')
+ {
+ // Add to total
+ $this->totals['passed']++;
+ }
+ else
+ {
+ // Add to results
+ $this->results[$this->current['result']][] = $this->current;
+ }
+
+ $this->current = array();
+
+ $this->time += $time;
+ }
+
+ public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
+ {
+ }
+
+ public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
+ {
+ // Parse test descriptions to make them look nicer
+ foreach ($this->results as $case => $testresults)
+ {
+ foreach ($testresults as $type => $result)
+ {
+ preg_match('/^(?:([a-z0-9_]+?)::)?([a-z0-9_]+)(?: with data set (#\d+ \(.*?\)))?/i', $result['description'], $m);
+
+ $this->results[$case][$type] += array(
+ 'class' => $m[1],
+ 'test' => $m[2],
+ 'data_set' => isset($m[3]) ? $m[3] : FALSE,
+ );
+ }
+ }
+ }
+}
diff --git a/includes/kohana/modules/unittest/classes/kohana/unittest/testcase.php b/includes/kohana/modules/unittest/classes/kohana/unittest/testcase.php
new file mode 100644
index 00000000..f22efdc9
--- /dev/null
+++ b/includes/kohana/modules/unittest/classes/kohana/unittest/testcase.php
@@ -0,0 +1,102 @@
+
+ * @author Paul Banks
+ * @copyright (c) 2008-2009 Kohana Team
+ * @license http://kohanaphp.com/license
+ */
+Abstract Class Kohana_Unittest_TestCase extends PHPUnit_Framework_TestCase
+{
+ /**
+ * Make sure PHPUnit backs up globals
+ * @var boolean
+ */
+ protected $backupGlobals = TRUE;
+
+ /**
+ * A set of unittest helpers that are shared between normal / database
+ * testcases
+ * @var Kohana_Unittest_Helpers
+ */
+ protected $_helpers = NULL;
+
+ /**
+ * A default set of environment to be applied before each test
+ * @var array
+ */
+ protected $environmentDefault = array();
+
+ /**
+ * Creates a predefined environment using the default environment
+ *
+ * Extending classes that have their own setUp() should call
+ * parent::setUp()
+ */
+ public function setUp()
+ {
+ $this->_helpers = new Kohana_Unittest_Helpers;
+
+ $this->setEnvironment($this->environmentDefault);
+ }
+
+ /**
+ * Restores the original environment overriden with setEnvironment()
+ *
+ * Extending classes that have their own tearDown()
+ * should call parent::tearDown()
+ */
+ public function tearDown()
+ {
+ $this->_helpers->restore_environment();
+ }
+
+ /**
+ * Removes all kohana related cache files in the cache directory
+ */
+ public function cleanCacheDir()
+ {
+ return Kohana_Unittest_Helpers::clean_cache_dir();
+ }
+
+ /**
+ * Helper function that replaces all occurences of '/' with
+ * the OS-specific directory separator
+ *
+ * @param string $path The path to act on
+ * @return string
+ */
+ public function dirSeparator($path)
+ {
+ return Kohana_Unittest_Helpers::dir_separator($path);
+ }
+
+ /**
+ * Allows easy setting & backing up of enviroment config
+ *
+ * Option types are checked in the following order:
+ *
+ * * Server Var
+ * * Static Variable
+ * * Config option
+ *
+ * @param array $environment List of environment to set
+ */
+ public function setEnvironment(array $environment)
+ {
+ return $this->_helpers->set_environment($environment);
+ }
+
+ /**
+ * Check for internet connectivity
+ *
+ * @return boolean Whether an internet connection is available
+ */
+ public function hasInternet()
+ {
+ return Kohana_Unittest_Helpers::has_internet();
+ }
+}
\ No newline at end of file
diff --git a/includes/kohana/modules/unittest/config/unittest.php b/includes/kohana/modules/unittest/config/unittest.php
new file mode 100644
index 00000000..b634d8ec
--- /dev/null
+++ b/includes/kohana/modules/unittest/config/unittest.php
@@ -0,0 +1,53 @@
+ Kohana::DEVELOPMENT,
+
+ // This is the folder where we generate and zip all the reports for downloading
+ // Needs to be readable and writable
+ 'temp_path' => Kohana::$cache_dir.'/unittest',
+
+ // Path from DOCROOT (i.e. http://yourdomain/) to the folder where HTML cc reports can be published.
+ // If you'd prefer not to allow users to do this then simply set the value to FALSE.
+ // Example value of 'cc_report_path' would allow devs to see report at http://yourdomain/report/
+ 'cc_report_path' => 'report',
+
+ // If you don't use a whitelist then only files included during the request will be counted
+ // If you do, then only whitelisted items will be counted
+ 'use_whitelist' => FALSE,
+
+ // Items to whitelist, only used in cli
+ // Web runner ui allows user to choose which items to whitelist
+ 'whitelist' => array(
+
+ // Should the app be whitelisted?
+ // Useful if you just want to test your application
+ 'app' => TRUE,
+
+ // Set to array(TRUE) to include all modules, or use an array of module names
+ // (the keys of the array passed to Kohana::modules() in the bootstrap)
+ // Or set to FALSE to exclude all modules
+ 'modules' => array(TRUE),
+
+ // If you don't want the Kohana code coverage reports to pollute your app's,
+ // then set this to FALSE
+ 'system' => TRUE,
+ ),
+
+ // Does what it says on the tin
+ // Blacklisted files won't be included in code coverage reports
+ // If you use a whitelist then the blacklist will be ignored
+ 'use_blacklist' => FALSE,
+
+ // List of individual files/folders to blacklist
+ 'blacklist' => array(
+ ),
+
+ // A database connection that can be used when testing
+ // This doesn't overwrite anything, tests will have to use this value manually
+ 'db_connection' => 'unittest',
+);
diff --git a/includes/kohana/modules/unittest/example.phpunit.xml b/includes/kohana/modules/unittest/example.phpunit.xml
new file mode 100644
index 00000000..9792d37d
--- /dev/null
+++ b/includes/kohana/modules/unittest/example.phpunit.xml
@@ -0,0 +1,16 @@
+
+
+
+
+ rel/path/to/unittest/tests.php
+
+
+
diff --git a/includes/kohana/modules/unittest/guide/menu.unittest.md b/includes/kohana/modules/unittest/guide/menu.unittest.md
new file mode 100644
index 00000000..7bbaf989
--- /dev/null
+++ b/includes/kohana/modules/unittest/guide/menu.unittest.md
@@ -0,0 +1,5 @@
+1. **UnitTest**
+ - [Testing](unittest.testing)
+ - [Mock Objects](unittest.mockobjects)
+ - [Troubleshooting](unittest.troubleshooting)
+ - [Testing workflows](unittest.testing_workflows)
diff --git a/includes/kohana/modules/unittest/guide/unittest.mockobjects.md b/includes/kohana/modules/unittest/guide/unittest.mockobjects.md
new file mode 100644
index 00000000..64b2d230
--- /dev/null
+++ b/includes/kohana/modules/unittest/guide/unittest.mockobjects.md
@@ -0,0 +1,265 @@
+# Mock objects
+
+Sometimes when writing tests you need to test something that depends on an object being in a certain state.
+
+Say for example you're testing a model - you want to make sure that the model is running the correct query, but you don't want it to run on a real database server. You can create a mock database connection which responds in the way the model expects, but doesn't actually connect to a physical database.
+
+PHPUnit has a built in mock object creator which can generate mocks for classes (inc. abstract ones) on the fly.
+It creates a class that extends the one you want to mock. You can also tell PHPUnit to override certain functions to return set values / assert that they're called in a specific way.
+
+## Creating an instance of a mock class
+
+You create mocks from within testcases using the getMock() function, which is defined in `PHPUnit_Framework_TestCase` like so:
+
+ getMock($originalClassName, $methods = array(), array $arguments = array(), $mockClassName = '', $callOriginalConstructor = TRUE, $callOriginalClone = TRUE, $callAutoload = TRUE)
+
+`$originalClassName`
+: The name of the class that you want to mock
+
+`$methods`
+: The methods of $originalClassName that you want to mock.
+ You need to tell PHPUnit in advance because PHP doesn't allow you to extend an object once it's been initialised.
+
+`$arguments`
+: An array of arguments to pass to the mock's constructor
+
+`$mockClassName`
+: Allows you to specify the name that will be given to the mock
+
+`$callOriginalConstructor`
+: Should the mock call its parent's constructor automatically?
+
+`$callOriginalClone`
+: Should the mock call its parent's clone method?
+
+Most of the time you'll only need to use the first two parameters, i.e.:
+
+ $mock = $this->getMock('ORM');
+
+`$mock` now contains a mock of ORM and can be handled as though it were a vanilla instance of `ORM`
+
+ $mock = $this->getMock('ORM', array('check'));
+
+`$mock` now contains a mock of ORM, but this time we're also mocking the check() method.
+
+## Mocking methods
+
+Assuming we've created a mock object like so:
+
+ $mock = $this->getMock('ORM', array('check'));
+
+We now need to tell PHPUnit how to mock the check function when its called.
+
+### How many times should it be called?
+
+You start off by telling PHPUnit how many times the method should be called by calling expects() on the mock object:
+
+ $mock->expects($matcher);
+
+`expects()` takes one argument, an invoker matcher which you can create using factory methods defined in `PHPUnit_Framework_TestCase`:
+
+#### Possible invoker matchers:
+
+`$this->any()`
+: Returns a matcher that allows the method to be called any number of times
+
+`$this->never()`
+: Returns a matcher that asserts that the method is never called
+
+`$this->once()`
+: Returns a matcher that asserts that the method is only called once
+
+`$this->atLeastOnce()`
+: Returns a matcher that asserts that the method is called at least once
+
+`$this->exactly($count)`
+: Returns a matcher that asserts that the method is called at least `$count` times
+
+`$this->at($index)`
+: Returns a matcher that matches when the method it is evaluated for is invoked at the given $index.
+
+In our example we want `check()` to be called once on our mock object, so if we update it accordingly:
+
+ $mock = $this->getMock('ORM', array('check'));
+
+ $mock->expects($this->once());
+
+### What is the method we're mocking?
+
+Although we told PHPUnit what methods we want to mock, we haven't actually told it what method these rules we're specifiying apply to.
+You do this by calling `method()` on the returned from `expects()`:
+
+ $mock->expects($matcher)
+ ->method($methodName);
+
+As you can probably guess, `method()` takes one parameter, the name of the method you're mocking.
+There's nothing very fancy about this function.
+
+ $mock = $this->GetMock('ORM', array('check'));
+
+ $mock->expects($this->once())
+ ->method('check');
+
+
+### What parameters should our mock method expect?
+
+There are two ways to do this, either
+
+* Tell the method to accept any parameters
+* Tell the method to accept a specific set of parameters
+
+The former can be achieved by calling `withAnyParameters()` on the object returned from `method()`
+
+ $mock->expects($matcher)
+ ->method($methodName)
+ ->withAnyParameters();
+
+To only allow specific parameters you can use the `with()` method which accepts any number of parameters.
+The order in which you define the parameters is the order that it expects them to be in when called.
+
+ $mock->expects($matcher)
+ ->method($methodName)
+ ->with($param1, $param2);
+
+Calling `with()` without any parameters will force the mock method to accept no parameters.
+
+PHPUnit has a fairly complex way of comparing parameters passed to the mock method with the expected values, which can be summarised like so -
+
+* If the values are identical, they are equal
+* If the values are of different types they are not equal
+* If the values are numbers they they are considered equal if their difference is equal to zero (this level of accuracy can be changed)
+* If the values are objects then they are converted to arrays and are compared as arrays
+* If the values are arrays then any sub-arrays deeper than x levels (default 10) are ignored in the comparision
+* If the values are arrays and one contains more than elements that the other (at any depth up to the max depth), then they are not equal
+
+#### More advanced parameter comparisions
+
+Sometimes you need to be more specific about how PHPUnit should compare parameters, i.e. if you want to make sure that one of the parameters is an instance of an object, yet isn't necessarily identical to a particular instance.
+
+In PHPUnit, the logic for validating objects and datatypes has been refactored into "constraint objects". If you look in any of the assertX() methods you can see that they are nothing more than wrappers for associating constraint objects with tests.
+
+If a parameter passed to `with()` is not an instance of a constraint object (one which extends `PHPUnit_Framework_Constraint`) then PHPUnit creates a new `IsEqual` comparision object for it.
+
+i.e., the following methods produce the same result:
+
+ ->with('foo', 1);
+
+ ->with($this->equalTo('foo'), $this->equalTo(1));
+
+Here are some of the wrappers PHPUnit provides for creating constraint objects:
+
+`$this->arrayHasKey($key)`
+: Asserts that the parameter will have an element with index `$key`
+
+`$this->attribute(PHPUnit_Framework_Constraint $constraint, $attributeName)`
+: Asserts that object attribute `$attributeName` of the parameter will satisfy `$constraint`, where constraint is an instance of a constraint (i.e. `$this->equalTo()`)
+
+`$this->fileExists()`
+: Accepts no parameters, asserts that the parameter is a path to a valid file (i.e. `file_exists() === TRUE`)
+
+`$this->greaterThan($value)`
+: Asserts that the parameter is greater than `$value`
+
+`$this->anything()`
+: Returns TRUE regardless of what the parameter is
+
+`$this->equalTo($value, $delta = 0, $canonicalizeEOL = FALSE, $ignoreCase = False)`
+: Asserts that the parameter is equal to `$value` (same as not passing a constraint object to `with()`)
+: `$delta` is the degree of accuracy to use when comparing numbers. i.e. 0 means numbers need to be identical, 1 means numbers can be within a distance of one from each other
+: If `$canonicalizeEOL` is TRUE then all newlines in string values will be converted to `\n` before comparision
+: If `$ignoreCase` is TRUE then both strings will be converted to lowercase before comparision
+
+`$this->identicalTo($value)`
+: Asserts that the parameter is identical to `$value`
+
+`$this->isType($type)`
+: Asserts that the parameter is of type `$type`, where `$type` is a string representation of the core PHP data types
+
+`$this->isInstanceOf($className)`
+: Asserts that the parameter is an instance of `$className`
+
+`$this->lessThan($value)`
+: Asserts that the parameter is less than `$value`
+
+`$this->objectHasAttribute($attribute)`
+: Asserts that the paramater (which is assumed to be an object) has an attribute `$attribute`
+
+`$this->matchesRegularExpression($pattern)`
+: Asserts that the parameter matches the PCRE pattern `$pattern` (using `preg_match()`)
+
+`$this->stringContains($string, $ignoreCase = FALSE)`
+: Asserts that the parameter contains the string `$string`. If `$ignoreCase` is TRUE then a case insensitive comparision is done
+
+`$this->stringEndsWith($suffix)`
+: Asserts that the parameter ends with `$suffix` (assumes parameter is a string)
+
+`$this->stringStartsWith($prefix)`
+: Asserts that the parameter starts with `$prefix` (assumes parameter is a string)
+
+`$this->contains($value)`
+: Asserts that the parameter contains at least one value that is identical to `$value` (assumes parameter is array or `SplObjectStorage`)
+
+`$this->containsOnly($type, $isNativeType = TRUE)`
+: Asserts that the parameter only contains items of type `$type`. `$isNativeType` should be set to TRUE when `$type` refers to a built in PHP data type (i.e. int, string etc.) (assumes parameter is array)
+
+
+There are more constraint objects than listed here, look in `PHPUnit_Framework_Assert` and `PHPUnit/Framework/Constraint` if you need more constraints.
+
+If we continue our example, we have the following:
+
+ $mock->expects($this->once())
+ ->method('check')
+ ->with();
+
+So far PHPUnit knows that we want the `check()` method to be called once, with no parameters. Now we just need to get it to return something...
+
+### What should the method return?
+
+This is the final stage of mocking a method.
+
+By default PHPUnit can return either
+
+* A fixed value
+* One of the parameters that were passed to it
+* The return value of a specified callback
+
+Specifying a return value is easy, just call `will()` on the object returned by either `method()` or `with()`.
+
+The function is defined like so:
+
+ public function will(PHPUnit_Framework_MockObject_Stub $stub)
+
+PHPUnit provides some MockObject stubs out of the box, you can access them via (when called from a testcase):
+
+`$this->returnValue($value)`
+: Returns `$value` when the mocked method is called
+
+`$this->returnArgument($argumentIndex)`
+: Returns the `$argumentIndex`th argument that was passed to the mocked method
+
+`$this->returnCallback($callback)`
+: Returns the value of the callback, useful for more complicated mocking.
+: `$callback` should a valid callback (i.e. `is_callable($callback) === TRUE`). PHPUnit will pass the callback all of the parameters that the mocked method was passed, in the same order / argument index (i.e. the callback is invoked by `call_user_func_array()`).
+: You can usually create the callback in your testcase, as long as doesn't begin with "test"
+
+Obviously if you really want to you can create your own MockObject stub, but these three should cover most situations.
+
+Updating our example gives:
+
+ $mock->expects($this->once())
+ ->method('check')
+ ->with()
+ ->will($this->returnValue(TRUE));
+
+
+And we're done!
+
+If you now call `$mock->check()` the value TRUE should be returned.
+
+If you don't call a mocked method and PHPUnit expects it to be called then the test the mock was generated for will fail.
+
+
+
diff --git a/includes/kohana/modules/unittest/guide/unittest.testing.md b/includes/kohana/modules/unittest/guide/unittest.testing.md
new file mode 100644
index 00000000..dccc3352
--- /dev/null
+++ b/includes/kohana/modules/unittest/guide/unittest.testing.md
@@ -0,0 +1,117 @@
+### From the command line
+
+ $ phpunit --bootstrap=index.php modules/unittest/tests.php
+
+Of course, you'll need to make sure the path to the tests.php file is correct. If you want you can copy it to a more accessible location
+
+### From the web
+
+Just navigate to http://example.com/unittest. You may need to use http://example.com/index.php/unittest if you have not enabled url rewriting in your .htaccess.
+
+## Writing tests
+
+If you're writing a test for your application, place it in "application/tests". Similarly, if you're writing a test for a module place it in modules/[modulefolder]/tests
+
+Rather than tell you how to write tests I'll point you in the direction of the [PHPUnit Manual](http://www.phpunit.de/manual/3.4/en/index.html). One thing you should bear in mind when writing tests is that testcases should extend Kohana_Unittest_Testcase rathr than PHPUnit_Framework_TestCase.
+
+Here's a taster of some of the cool things you can do with phpunit:
+
+### Data Providers
+
+Sometimes you want to be able to run a specific test with different sets of data to try and test every eventuality
+
+Ordinarily you could use a foreach loop to iterate over an array of test data, however PHPUnit already can take care of this for us rather easily using "Data Providers". A data provider is a function that returns an array of arguments that can be passed to a test.
+
+ assertSame(
+ $length,
+ strlen($string)
+ );
+ }
+ }
+
+The key thing to notice is the `@dataProvider` tag in the doccomment, this is what tells PHPUnit to use a data provider. The provider prefix is totally optional but it's a nice standard to identify providers.
+
+For more info see:
+
+* [Data Providers in PHPUnit 3.2](http://sebastian-bergmann.de/archives/702-Data-Providers-in-PHPUnit-3.2.html)
+* [Data Providers](http://www.phpunit.de/manual/3.4/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.data-providers)
+
+
+### Grouping tests
+
+To allow users to selectively run tests you need to organise your tests into groups. Here's an example test showing how to do this:
+
+
+ = 3.4, there is a bug in 3.3 which causes this.
+
+## Some of my classes aren't getting whitelisted for code coverage even though their module is
+
+Only the "highest" files in the cascading filesystem are whitelisted for code coverage.
+
+To test your module's file, remove the higher file from the cascading filesystem by disabling their respective module.
+
+A good way of testing is to create a "vanilla" testing environment for your module, devoid of anything that isn't required by the module.
+
+## I get a blank page when trying to generate a code coverage report
+
+Try the following:
+
+1. Generate a html report from the command line using `phpunit {bootstrap info} --coverage-html ./report {insert path to tests.php}`. If any error messages show up, fix them and try to generate the report again
+2. Increase the php memory limit
+3. Make sure that display_errors is set to "on" in your php.ini config file (this value can sometimes be overriden in a .htaccess file)
diff --git a/includes/kohana/modules/unittest/init.php b/includes/kohana/modules/unittest/init.php
new file mode 100644
index 00000000..54994872
--- /dev/null
+++ b/includes/kohana/modules/unittest/init.php
@@ -0,0 +1,23 @@
+)')
+ ->defaults(array(
+ 'controller' => 'unittest',
+ 'action' => 'index',
+ ));
diff --git a/includes/kohana/modules/unittest/tests.php b/includes/kohana/modules/unittest/tests.php
new file mode 100644
index 00000000..c8398dd8
--- /dev/null
+++ b/includes/kohana/modules/unittest/tests.php
@@ -0,0 +1,20 @@
+
+
+
');
+ }
+
+} // End Kodoc_Markdown
diff --git a/includes/kohana/modules/userguide/classes/kohana/kodoc/method.php b/includes/kohana/modules/userguide/classes/kohana/kodoc/method.php
new file mode 100644
index 00000000..d704a8c0
--- /dev/null
+++ b/includes/kohana/modules/userguide/classes/kohana/kodoc/method.php
@@ -0,0 +1,141 @@
+method = new ReflectionMethod($class, $method);
+
+ $this->class = $parent = $this->method->getDeclaringClass();
+
+ if ($modifiers = $this->method->getModifiers())
+ {
+ $this->modifiers = ''.implode(' ', Reflection::getModifierNames($modifiers)).' ';
+ }
+
+ do
+ {
+ if ($parent->hasMethod($method) AND $comment = $parent->getMethod($method)->getDocComment())
+ {
+ // Found a description for this method
+ break;
+ }
+ }
+ while ($parent = $parent->getParentClass());
+
+ list($this->description, $tags) = Kodoc::parse($comment);
+
+ if ($file = $this->class->getFileName())
+ {
+ $this->source = Kodoc::source($file, $this->method->getStartLine(), $this->method->getEndLine());
+ }
+
+ if (isset($tags['param']))
+ {
+ $params = array();
+
+ foreach ($this->method->getParameters() as $i => $param)
+ {
+ $param = new Kodoc_Method_Param(array($this->method->class,$this->method->name),$i);
+
+ if (isset($tags['param'][$i]))
+ {
+ preg_match('/^(\S+)(?:\s*(?:\$'.$param->name.'\s*)?(.+))?$/', $tags['param'][$i], $matches);
+
+ $param->type = $matches[1];
+
+ if (isset($matches[2]))
+ {
+ $param->description = $matches[2];
+ }
+ }
+ $params[] = $param;
+ }
+
+ $this->params = $params;
+
+ unset($tags['param']);
+ }
+
+ if (isset($tags['return']))
+ {
+ foreach ($tags['return'] as $return)
+ {
+ if (preg_match('/^(\S*)(?:\s*(.+?))?$/', $return, $matches))
+ {
+ $this->return[] = array($matches[1], isset($matches[2]) ? $matches[2] : '');
+ }
+ }
+
+ unset($tags['return']);
+ }
+
+ $this->tags = $tags;
+ }
+
+ public function params_short()
+ {
+ $out = '';
+ $required = TRUE;
+ $first = TRUE;
+ foreach ($this->params as $param)
+ {
+ if ($required AND $param->default AND $first)
+ {
+ $out .= '[ '.$param;
+ $required = FALSE;
+ $first = FALSE;
+ }
+ elseif ($required AND $param->default)
+ {
+ $out .= '[, '.$param;
+ $required = FALSE;
+ }
+ elseif ($first)
+ {
+ $out .= $param;
+ $first = FALSE;
+ }
+ else
+ {
+ $out .= ', '.$param;
+ }
+ }
+
+ if ( ! $required)
+ {
+ $out .= '] ';
+ }
+
+ return $out;
+ }
+
+} // End Kodoc_Method
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/classes/kohana/kodoc/method/param.php b/includes/kohana/modules/userguide/classes/kohana/kodoc/method/param.php
new file mode 100644
index 00000000..8afa72d8
--- /dev/null
+++ b/includes/kohana/modules/userguide/classes/kohana/kodoc/method/param.php
@@ -0,0 +1,101 @@
+param = new ReflectionParameter($method, $param);
+
+ $this->name = $this->param->name;
+
+ if ($this->param->isDefaultValueAvailable())
+ {
+ $this->default = Kohana::dump($this->param->getDefaultValue());
+ }
+
+ if ($this->param->isPassedByReference())
+ {
+ $this->reference = TRUE;
+ }
+
+ if ($this->param->isOptional())
+ {
+ $this->optional = TRUE;
+ }
+ }
+
+ public function __toString()
+ {
+ $display = '';
+
+ if ($this->type)
+ {
+ $display .= ''.$this->type.' ';
+ }
+
+ if ($this->reference)
+ {
+ $display .= '& ';
+ }
+
+ if ($this->description)
+ {
+ $display .= '$'.$this->name.' ';
+ }
+ else
+ {
+ $display .= '$'.$this->name.' ';
+ }
+
+ if ($this->default)
+ {
+ $display .= '= '.$this->default.' ';
+ }
+
+ return $display;
+ }
+
+} // End Kodoc_Method_Param
diff --git a/includes/kohana/modules/userguide/classes/kohana/kodoc/missing.php b/includes/kohana/modules/userguide/classes/kohana/kodoc/missing.php
new file mode 100644
index 00000000..f7256724
--- /dev/null
+++ b/includes/kohana/modules/userguide/classes/kohana/kodoc/missing.php
@@ -0,0 +1,36 @@
+getDocComment());
+
+ $this->description = $description;
+
+ if ($modifiers = $property->getModifiers())
+ {
+ $this->modifiers = ''.implode(' ', Reflection::getModifierNames($modifiers)).' ';
+ }
+
+ if (isset($tags['var']))
+ {
+ if (preg_match('/^(\S*)(?:\s*(.+?))?$/', $tags['var'][0], $matches))
+ {
+ $this->type = $matches[1];
+
+ if (isset($matches[2]))
+ {
+ $this->description = $matches[2];
+ }
+ }
+ }
+
+ $this->property = $property;
+
+ if ($property->isStatic())
+ {
+ $this->value = Kohana::debug($property->getValue($class));
+ }
+ }
+
+} // End Kodoc_Property
diff --git a/includes/kohana/modules/userguide/config/userguide.php b/includes/kohana/modules/userguide/config/userguide.php
new file mode 100644
index 00000000..aae05699
--- /dev/null
+++ b/includes/kohana/modules/userguide/config/userguide.php
@@ -0,0 +1,17 @@
+ 'about.kohana',
+
+ // Default the userguide language.
+ 'lang' => 'en-us',
+
+ // Enable the API browser. TRUE or FALSE
+ 'api_browser' => TRUE,
+
+ // Enable these packages in the API browser. TRUE for all packages, or a string of comma seperated packages, using 'None' for a class with no @package
+ // Example: 'api_packages' => 'Kohana,Kohana/Database,Kohana/ORM,None',
+ 'api_packages' => TRUE,
+);
diff --git a/includes/kohana/modules/userguide/guide/about.conventions.md b/includes/kohana/modules/userguide/guide/about.conventions.md
new file mode 100644
index 00000000..bf0acfe1
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/about.conventions.md
@@ -0,0 +1,293 @@
+# 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
+
+## Coding Standards {#coding_standards}
+
+In order to produce highly consistent source code, we ask that everyone follow the coding standards as closely as possible.
+
+### Brackets
+Please use [BSD/Allman Style](http://en.wikipedia.org/wiki/Indent_style#BSD.2FAllman_style) bracketing.
+
+### Naming Conventions
+
+Kohana uses under_score naming, not camelCase naming.
+
+#### Classes
+
+ // Controller class, uses Controller_ prefix
+ class Controller_Apple extends Controller {
+
+ // Model class, uses Model_ prefix
+ class Model_Cheese extends Model {
+
+ // Regular class
+ class Peanut {
+
+When creating an instance of a class, don't use parentheses if you're not passing something on to the constructor:
+
+ // Correct:
+ $db = new Database;
+
+ // Incorrect:
+ $db = new Database();
+
+#### Functions and Methods
+
+Functions should be all lowercase, and use under_scores to separate words:
+
+ function drink_beverage($beverage)
+ {
+
+#### Variables
+
+All variables should be lowercase and use under_score, not camelCase:
+
+ // Correct:
+ $foo = 'bar';
+ $long_example = 'uses underscores';
+
+ // Incorrect:
+ $weDontWantThis = 'understood?';
+
+### Indentation
+
+You must use tabs to indent your code. Using spaces for tabbing is strictly forbidden.
+
+Vertical spacing (for multi-line) is done with spaces. Tabs are not good for vertical alignment because different people have different tab widths.
+
+ $text = 'this is a long text block that is wrapped. Normally, we aim for '
+ . 'wrapping at 80 chars. Vertical alignment is very important for '
+ . 'code readability. Remember that all indentation is done with tabs,'
+ . 'but vertical alignment should be completed with spaces, after '
+ . 'indenting with tabs.';
+
+### String concatenation
+
+Don't put spaces around the concatenation operator:
+
+ // Correct:
+ $str = 'one'.$var.'two';
+
+ // Incorrect:
+ $str = 'one'. $var .'two';
+ $str = 'one' . $var . 'two';
+
+### Single Line Statements
+
+Single-line IF statements should only be used when breaking normal execution (e.g. return or continue):
+
+ // Acceptable:
+ if ($foo == $bar)
+ return $foo;
+
+ if ($foo == $bar)
+ continue;
+
+ if ($foo == $bar)
+ break;
+
+ if ($foo == $bar)
+ throw new Exception('You screwed up!');
+
+ // Not acceptable:
+ if ($baz == $bun)
+ $baz = $bar + 2;
+
+### Comparison Operations
+
+Please use OR and AND for comparison:
+
+ // Correct:
+ if (($foo AND $bar) OR ($b AND $c))
+
+ // Incorrect:
+ if (($foo && $bar) || ($b && $c))
+
+Please use elseif, not else if:
+
+ // Correct:
+ elseif ($bar)
+
+ // Incorrect:
+ else if($bar)
+
+### Switch structures
+
+Each case, break and default should be on a separate line. The block inside a case or default must be indented by 1 tab.
+
+ switch ($var)
+ {
+ case 'bar':
+ case 'foo':
+ echo 'hello';
+ break;
+ case 1:
+ echo 'one';
+ break;
+ default:
+ echo 'bye';
+ break;
+ }
+
+### Parentheses
+
+There should be one space after statement name, followed by a parenthesis. The ! (bang) character must have a space on either side to ensure maximum readability. Except in the case of a bang or type casting, there should be no whitespace after an opening parenthesis or before a closing parenthesis.
+
+ // Correct:
+ if ($foo == $bar)
+ if ( ! $foo)
+
+ // Incorrect:
+ if($foo == $bar)
+ if(!$foo)
+ if ((int) $foo)
+ if ( $foo == $bar )
+ if (! $foo)
+
+### Ternaries
+
+All ternary operations should follow a standard format. Use parentheses around expressions only, not around just variables.
+
+ $foo = ($bar == $foo) ? $foo : $bar;
+ $foo = $bar ? $foo : $bar;
+
+All comparisons and operations must be done inside of a parentheses group:
+
+ $foo = ($bar > 5) ? ($bar + $foo) : strlen($bar);
+
+When separating complex ternaries (ternaries where the first part goes beyond ~80 chars) into multiple lines, spaces should be used to line up operators, which should be at the front of the successive lines:
+
+ $foo = ($bar == $foo)
+ ? $foo
+ : $bar;
+
+### Type Casting
+
+Type casting should be done with spaces on each side of the cast:
+
+ // Correct:
+ $foo = (string) $bar;
+ if ( (string) $bar)
+
+ // Incorrect:
+ $foo = (string)$bar;
+
+When possible, please use type casting instead of ternary operations:
+
+ // Correct:
+ $foo = (bool) $bar;
+
+ // Incorrect:
+ $foo = ($bar == TRUE) ? TRUE : FALSE;
+
+When casting type to integer or boolean, use the short format:
+
+ // Correct:
+ $foo = (int) $bar;
+ $foo = (bool) $bar;
+
+ // Incorrect:
+ $foo = (integer) $bar;
+ $foo = (boolean) $bar;
+
+### Constants
+
+Always use uppercase for constants:
+
+ // Correct:
+ define('MY_CONSTANT', 'my_value');
+ $a = TRUE;
+ $b = NULL;
+
+ // Incorrect:
+ define('MyConstant', 'my_value');
+ $a = True;
+ $b = null;
+
+Place constant comparisons at the end of tests:
+
+ // Correct:
+ if ($foo !== FALSE)
+
+ // Incorrect:
+ if (FALSE !== $foo)
+
+This is a slightly controversial choice, so I will explain the reasoning. If we were to write the previous example in plain English, the correct example would read:
+
+ if variable $foo is not exactly FALSE
+
+And the incorrect example would read:
+
+ if FALSE is not exactly variable $foo
+
+Since we are reading left to right, it simply doesn't make sense to put the constant first.
+
+### Comments
+
+#### One-line comments
+
+Use //, preferably above the line of code you're commenting on. Leave a space after it and start with a capital. Never use #.
+
+ // Correct
+
+ //Incorrect
+ // incorrect
+ # Incorrect
+
+### Regular expressions
+
+When coding regular expressions please use PCRE rather than the POSIX flavor. PCRE is considered more powerful and faster.
+
+ // Correct:
+ if (preg_match('/abc/i'), $str)
+
+ // Incorrect:
+ if (eregi('abc', $str))
+
+Use single quotes around your regular expressions rather than double quotes. Single-quoted strings are more convenient because of their simplicity. Unlike double-quoted strings they don't support variable interpolation nor integrated backslash sequences like \n or \t, etc.
+
+ // Correct:
+ preg_match('/abc/', $str);
+
+ // Incorrect:
+ preg_match("/abc/", $str);
+
+When performing a regular expression search and replace, please use the $n notation for backreferences. This is preferred over \\n.
+
+ // Correct:
+ preg_replace('/(\d+) dollar/', '$1 euro', $str);
+
+ // Incorrect:
+ preg_replace('/(\d+) dollar/', '\\1 euro', $str);
+
+Finally, please note that the $ character for matching the position at the end of the line allows for a following newline character. Use the D modifier to fix this if needed. [More info](http://blog.php-security.org/archives/76-Holes-in-most-preg_match-filters.html).
+
+ $str = "email@example.com\n";
+
+ preg_match('/^.+@.+$/', $str); // TRUE
+ preg_match('/^.+@.+$/D', $str); // FALSE
diff --git a/includes/kohana/modules/userguide/guide/about.filesystem.md b/includes/kohana/modules/userguide/guide/about.filesystem.md
new file mode 100644
index 00000000..c760c3e2
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/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:
+
+
+
+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.
diff --git a/includes/kohana/modules/userguide/guide/about.flow.md b/includes/kohana/modules/userguide/guide/about.flow.md
new file mode 100644
index 00000000..5e7f6bc3
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/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.
diff --git a/includes/kohana/modules/userguide/guide/about.install.md b/includes/kohana/modules/userguide/guide/about.install.md
new file mode 100644
index 00000000..f3ab2a98
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/about.install.md
@@ -0,0 +1,96 @@
+# Installation
+
+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 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 continuing.
+
+
+
+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:
+
+
+
+## Setting up a production environment
+
+There are a few things you'll want to do with your application before moving into production.
+
+1. See the [Configuration page](about.configuration) in the docs.
+ This covers most of the global settings that would change between environments.
+ As a general rule, you should enable caching and disable profiling ([Kohana::init] settings) for production sites.
+ [Route caching](api/Route#cache) can also help if you have a lot of routes.
+2. Catch all exceptions in `application/bootstrap.php`, so that sensitive data is cannot be leaked by stack traces.
+ See the example below which was taken from Shadowhand's [wingsc.com source](http://github.com/shadowhand/wingsc).
+3. Turn on APC or some kind of opcode caching.
+ This is the single easiest performance boost you can make to PHP itself. The more complex your application, the bigger the benefit of using opcode caching.
+
+[!!] Note: The default bootstrap will set Kohana::$environment = $_ENV['KOHANA_ENV'] if set. Docs on how to supply this variable are available in your web server's documentation (e.g. [Apache](http://httpd.apache.org/docs/1.3/mod/mod_env.html#setenv), [Lighttpd](http://redmine.lighttpd.net/wiki/1/Docs:ModSetEnv#Options), [Nginx](http://wiki.nginx.org/NginxHttpFcgiModule#fastcgi_param)). This is considered better practice than many alternative methods to set Kohana::$enviroment.
+
+ /**
+ * Set the environment string by the domain (defaults to Kohana::DEVELOPMENT).
+ */
+ Kohana::$environment = ($_SERVER['SERVER_NAME'] !== 'localhost') ? Kohana::PRODUCTION : Kohana::DEVELOPMENT;
+ /**
+ * Initialise Kohana based on environment
+ */
+ Kohana::init(array(
+ 'base_url' => '/',
+ 'index_file' => FALSE,
+ 'profile' => Kohana::$environment !== Kohana::PRODUCTION,
+ 'caching' => Kohana::$environment === Kohana::PRODUCTION,
+ ));
+
+ /**
+ * Execute the main request using PATH_INFO. If no URI source is specified,
+ * the URI will be automatically detected.
+ */
+ $request = Request::instance($_SERVER['PATH_INFO']);
+
+ try
+ {
+ // Attempt to execute the response
+ $request->execute();
+ }
+ catch (Exception $e)
+ {
+ if (Kohana::$environment === Kohana::DEVELOPMENT)
+ {
+ // Just re-throw the exception
+ throw $e;
+ }
+
+ // Log the error
+ Kohana::$log->add(Kohana::ERROR, Kohana::exception_text($e));
+
+ // Create a 404 response
+ $request->status = 404;
+ $request->response = View::factory('template')
+ ->set('title', '404')
+ ->set('content', View::factory('errors/404'));
+ }
+
+ if ($request->send_headers()->response)
+ {
+ // Get the total memory and execution time
+ $total = array(
+ '{memory_usage}' => number_format((memory_get_peak_usage() - KOHANA_START_MEMORY) / 1024, 2).'KB',
+ '{execution_time}' => number_format(microtime(TRUE) - KOHANA_START_TIME, 5).' seconds');
+
+ // Insert the totals into the response
+ $request->response = str_replace(array_keys($total), $total, $request->response);
+ }
+
+
+ /**
+ * Display the request response.
+ */
+ echo $request->response;
+
diff --git a/includes/kohana/modules/userguide/guide/about.kohana.md b/includes/kohana/modules/userguide/guide/about.kohana.md
new file mode 100644
index 00000000..c1ad270f
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/about.kohana.md
@@ -0,0 +1,18 @@
+# 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.
+
+## Contribute to the Documentation
+
+We are working very hard to provide complete documentation. To help improve the guide, please [fork the userguide](http://github.com/kohana/userguide), make your changes, and send a pull request. If you are not familiar with git, you can also submit a [feature request](http://dev.kohanaframework.org/projects/kohana3/issues) (requires registration).
+
+## Help, I can't find the answer?
+If you are having trouble finding an answer here, have a look through the [unofficial wiki](http://kerkness.ca/wiki/doku.php). Your answer may also be found by searching the [forum](http://forum.kohanaphp.com/) or [stackoverflow](http://stackoverflow.com/questions/tagged/kohana) followed by asking your question on either. Additionally, you can chat with the community of developers on the freenode [#kohana](irc://irc.freenode.net/kohana) IRC channel.
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/about.mvc.md b/includes/kohana/modules/userguide/guide/about.mvc.md
new file mode 100644
index 00000000..4c9442d5
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/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
diff --git a/includes/kohana/modules/userguide/guide/about.translation.md b/includes/kohana/modules/userguide/guide/about.translation.md
new file mode 100644
index 00000000..00c3f529
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/about.translation.md
@@ -0,0 +1,4 @@
+# Translation
+
+[!!] This article is a stub!
+
diff --git a/includes/kohana/modules/userguide/guide/about.upgrading.md b/includes/kohana/modules/userguide/guide/about.upgrading.md
new file mode 100644
index 00000000..ce11fcf4
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/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','((/(/)))')
+ ->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(/(/(/)))');
+
+And if you wanted to force the user to specify a controller:
+
+ Route::set('admin', 'admin/(/(/))');
+
+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', '((/(/)))', 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();
+ }
+ }
diff --git a/includes/kohana/modules/userguide/guide/de-de/about.conventions.md b/includes/kohana/modules/userguide/guide/de-de/about.conventions.md
new file mode 100644
index 00000000..42f02aaa
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/de-de/about.conventions.md
@@ -0,0 +1,300 @@
+# Regeln
+
+Es wird dazu ermutigt, dem Kohana [Programmierstil](http://dev.kohanaframework.org/wiki/kohana2/CodingStyle) zu folgen. Dieser benutzt den [Allman/BSD](http://de.wikipedia.org/wiki/Einr%C3%BCckungsstil#Allman_.2F_BSD_.2F_.E2.80.9EEast_Coast.E2.80.9C_.2F_Horstmann)-Stil.
+
+## Klassennamen und Dateilage {#classes}
+
+Das automatische Laden von Klassen wird durch ihre strengen Namensregeln ermglicht. Die Klassen beginnen mit einem Grobuchstaben und ihre Wrter werden durch Unterstriche getrennt. Diese sind entscheidend, an welcher Stelle die Klasse im Dateisystem gefunden wird.
+
+Folgende Regeln gelten:
+
+1. Binnenversalien (camelCase) sollten nicht benutzt werden, auer wenn eine weitere Ordner-Ebene unerwnscht ist
+2. alle Datei- und Verzeichnisnamen in Kleinbuchstaben
+3. alle Klassen werden im `classes`-Verzeichnis in jeder Ebene des [Kaskaden-Dateisystem](about.filesystem) zusammengefasst
+
+[!!] Im Gegensatz zu Kohana v2.x besteht keine Unterteilung zwischen "Controllern", "Models", "Bibliotheken" und "Helfern". Alle Klassen befinden sich im "classes/"-Verzeichnis, unabhngig ob es statische "Helfer" oder Objekt-"Bibliotheken" sind. Man kann irgendeinen Klassen-Aufbau (statische Klasse, Singleton, Adapter) verwenden, den man mag.
+
+## Beispiele
+
+Denk daran, dass der Unterstrich in Klassennamen eine tiefere Verzeichnisebene bedeutet. Beachte folgende Beispiele:
+
+Klassenname | Dateipfad
+----------------------|-------------------------------
+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
+
+## Programmierstil {#coding_standards}
+
+Um einen sehr konsistenten Quelltext zu produzieren, bitten wir jeden den folgenden Programmierstil so genau wie mglich umzusetzen.
+
+### Klammerung
+
+Bitte benutze den den [Allman/BSD](http://de.wikipedia.org/wiki/Einr%C3%BCckungsstil#Allman_.2F_BSD_.2F_.E2.80.9EEast_Coast.E2.80.9C_.2F_Horstmann)-Stil.
+
+### Namensregeln
+
+Kohana benutzt fr Namen Unter_striche, keine BinnenVersalien (camelCase).
+
+#### Klassen
+
+ // Libary
+ class Beer {
+
+ // Libary extension, uses Kohana_ prefix
+ class Beer extends Kohana_Beer {
+
+ // Controller class, uses Controller_ prefix
+ class Controller_Apple extends Controller {
+
+ // Model class, uses Model_ prefix
+ class Model_Cheese extends Model {
+
+ // Helper class, cf. libary
+ class peanut {
+
+Benutze keine Klammern, wenn eine Klasseninstanz erstellt, aber keine Parameter bergibt:
+
+ // Correct:
+ $db = new Database;
+
+ // Incorrect:
+ $db = new Database();
+
+#### Funktionen und Methoden
+
+Funktionen sollten kleingeschrieben sein und Unter_striche zur Worttrennung benutzen:
+
+ function drink_beverage($beverage)
+ {
+
+#### Variablen
+
+Alle Variablen sollten ebenfalls kleingeschrieben sein und Unter_striche benutzen, keine BinnenVersalien (camelCase):
+
+ // Correct:
+ $foo = 'bar';
+ $long_example = 'uses underscores';
+
+ // Incorrect:
+ $weDontWantThis = 'understood?';
+
+### Einrckung
+
+Du musst zur Einrckung deines Quelltextes Tabulatoren benutzen. Leerzeichen fr Tabellarisierung zu verwenden, ist strengstens verboten.
+
+Vertikaler Abstand (bei Mehrzeiligkeit) wird mit Leerzeichen gemacht. Tabulatoren sind schlecht fr die vertikale Ausrichtung, weil verschiedene Leute unterschiedliche Tabulatoren-Breiten haben.
+
+ $text = 'this is a long text block that is wrapped. Normally, we aim for '
+ . 'wrapping at 80 chars. Vertical alignment is very important for '
+ . 'code readability. Remember that all indentation is done with tabs,'
+ . 'but vertical alignment should be completed with spaces, after '
+ . 'indenting with tabs.';
+
+### Zeichenkettenverknpfung
+
+Setze keine Leerzeichen um den Verknpfungsoperator:
+
+ // Correct:
+ $str = 'one'.$var.'two';
+
+ // Incorrect:
+ $str = 'one'. $var .'two';
+ $str = 'one' . $var . 'two';
+
+### Einzeilige Ausdrcke
+
+Einzeilige IF-Bedingungen sollten nur bei Anweisungen benutzt werden, die die normale Verarbeitung unterbrechen (z.B. return oder continue):
+
+ // Acceptable:
+ if ($foo == $bar)
+ return $foo;
+
+ if ($foo == $bar)
+ continue;
+
+ if ($foo == $bar)
+ break;
+
+ if ($foo == $bar)
+ throw new Exception('You screwed up!');
+
+ // Not acceptable:
+ if ($baz == $bun)
+ $baz = $bar + 2;
+
+### Vergleichsoperatoren
+
+Bitte benutze OR and AND:
+
+ // Correct:
+ if (($foo AND $bar) OR ($b AND $c))
+
+ // Incorrect:
+ if (($foo && $bar) || ($b && $c))
+
+Bitte benutze elseif, nicht else if:
+
+ // Correct:
+ elseif ($bar)
+
+ // Incorrect:
+ else if($bar)
+
+### Switch structures
+
+Each case, break and default should be on a separate line. The block inside a case or default must be indented by 1 tab.
+
+ switch ($var)
+ {
+ case 'bar':
+ case 'foo':
+ echo 'hello';
+ break;
+ case 1:
+ echo 'one';
+ break;
+ default:
+ echo 'bye';
+ break;
+ }
+
+### Parentheses
+
+There should be one space after statement name, followed by a parenthesis. The ! (bang) character must have a space on either side to ensure maximum readability. Except in the case of a bang or type casting, there should be no whitespace after an opening parenthesis or before a closing parenthesis.
+
+ // Correct:
+ if ($foo == $bar)
+ if ( ! $foo)
+
+ // Incorrect:
+ if($foo == $bar)
+ if(!$foo)
+ if ((int) $foo)
+ if ( $foo == $bar )
+ if (! $foo)
+
+### Ternaries
+
+All ternary operations should follow a standard format. Use parentheses around expressions only, not around just variables.
+
+ $foo = ($bar == $foo) ? $foo : $bar;
+ $foo = $bar ? $foo : $bar;
+
+All comparisons and operations must be done inside of a parentheses group:
+
+ $foo = ($bar > 5) ? ($bar + $foo) : strlen($bar);
+
+When separating complex ternaries (ternaries where the first part goes beyond ~80 chars) into multiple lines, spaces should be used to line up operators, which should be at the front of the successive lines:
+
+ $foo = ($bar == $foo)
+ ? $foo
+ : $bar;
+
+### Type Casting
+
+Type casting should be done with spaces on each side of the cast:
+
+ // Correct:
+ $foo = (string) $bar;
+ if ( (string) $bar)
+
+ // Incorrect:
+ $foo = (string)$bar;
+
+When possible, please use type casting instead of ternary operations:
+
+ // Correct:
+ $foo = (bool) $bar;
+
+ // Incorrect:
+ $foo = ($bar == TRUE) ? TRUE : FALSE;
+
+When casting type to integer or boolean, use the short format:
+
+ // Correct:
+ $foo = (int) $bar;
+ $foo = (bool) $bar;
+
+ // Incorrect:
+ $foo = (integer) $bar;
+ $foo = (boolean) $bar;
+
+### Constants
+
+Always use uppercase for constants:
+
+ // Correct:
+ define('MY_CONSTANT', 'my_value');
+ $a = TRUE;
+ $b = NULL;
+
+ // Incorrect:
+ define('MyConstant', 'my_value');
+ $a = True;
+ $b = null;
+
+Place constant comparisons at the end of tests:
+
+ // Correct:
+ if ($foo !== FALSE)
+
+ // Incorrect:
+ if (FALSE !== $foo)
+
+This is a slightly controversial choice, so I will explain the reasoning. If we were to write the previous example in plain English, the correct example would read:
+
+ if variable $foo is not exactly FALSE
+
+And the incorrect example would read:
+
+ if FALSE is not exactly variable $foo
+
+Since we are reading left to right, it simply doesn't make sense to put the constant first.
+
+### Comments
+
+#### One-line comments
+
+Use //, preferably above the line of code you're commenting on. Leave a space after it and start with a capital. Never use #.
+
+ // Correct
+
+ //Incorrect
+ // incorrect
+ # Incorrect
+
+### Regular expressions
+
+When coding regular expressions please use PCRE rather than the POSIX flavor. PCRE is considered more powerful and faster.
+
+ // Correct:
+ if (preg_match('/abc/i'), $str)
+
+ // Incorrect:
+ if (eregi('abc', $str))
+
+Use single quotes around your regular expressions rather than double quotes. Single-quoted strings are more convenient because of their simplicity. Unlike double-quoted strings they don't support variable interpolation nor integrated backslash sequences like \n or \t, etc.
+
+ // Correct:
+ preg_match('/abc/', $str);
+
+ // Incorrect:
+ preg_match("/abc/", $str);
+
+When performing a regular expression search and replace, please use the $n notation for backreferences. This is preferred over \\n.
+
+ // Correct:
+ preg_replace('/(\d+) dollar/', '$1 euro', $str);
+
+ // Incorrect:
+ preg_replace('/(\d+) dollar/', '\\1 euro', $str);
+
+Finally, please note that the $ character for matching the position at the end of the line allows for a following newline character. Use the D modifier to fix this if needed. [More info](http://blog.php-security.org/archives/76-Holes-in-most-preg_match-filters.html).
+
+ $str = "email@example.com\n";
+
+ preg_match('/^.+@.+$/', $str); // TRUE
+ preg_match('/^.+@.+$/D', $str); // FALSE
diff --git a/includes/kohana/modules/userguide/guide/de-de/about.kohana.md b/includes/kohana/modules/userguide/guide/de-de/about.kohana.md
new file mode 100644
index 00000000..68af7fb8
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/de-de/about.kohana.md
@@ -0,0 +1,15 @@
+# Was ist Kohana?
+
+Kohana ist ein Open-Source-basiertes, [objektorientiertes](http://de.wikipedia.org/wiki/Objektorientierte_Programmierung) [MVC](http://de.wikipedia.org/wiki/Model_View_Controller "Model View Controller")-[Webframework](http://de.wikipedia.org/wiki/Web_Application_Framework) unter Verwendung von [PHP5](http://php.net/manual/de/intro-whatis "PHP Hypertext Preprocessor"). Es wird von Freiwilligen entwickelt, das darauf abzielt schnell, sicher und schlank zu sein.
+
+[!!] Kohana ist unter der [BSD-Lizenz](http://kohanaframework.org/license) verffentlicht, so dass man es rechtlich fr alle Arten von Open-Source-, kommerzieller oder privater Projekte nutzen kann.
+
+## Was macht Kohana besonders?
+
+Durch den einzigartigen [Dateisystem](about.filesystem)-Aufbau ist alles erweiterbar und man braucht wenige oder keine [Einstellungen](about.configuration) vornehmen. Die [Fehlerbehandlung](debugging.errors) hilft, die Fehlerquelle schnell zu finden, und die [Fehlersuche](debugging) und [Programmanalyse](debugging.profiling) ermglichen einen Einblick in die Anwendung.
+
+Um die Sicherheit deiner Anwendung zu untersttzen, enthlt Kohana Werkzeuge fr [XSS-Entfernung](security.xss), [Eingabe-berprfung](security.validation), [signierte Cookies](security.cookies), [Formular](security.forms)- und [HTML](security.html)-Erstellung. Die [Datenbank](security.database)-Schicht bietet Schutz vor [SQL-Injection](http://de.wikipedia.org/wiki/SQL-Injection). Natrlich wurde der gesamte offizielle Quelltext sorgfltig geschrieben und auf Sicherheit geprft.
+
+## Diese Dokumentation ist scheie!
+
+Wir bemhen uns um eine vollstndige Dokumentation. Wenn eine Frage trotzdem offen bleibt, versuche es beim [inoffiziellen Wiki](http://kerkness.ca/wiki/doku.php). Wenn du etwas zum Handbuch beitragen oder ndern willst, erstelle bitte [eine Kopie](http://github.com/kohana/userguide), bearbeite sie und stelle eine Anfrage zur Zusammenfhrung. Falls du nicht mit git vertraut bist, kannst du auch ein [Feature-Vorschlag](http://dev.kohanaframework.org/projects/kohana3/issues) (Anmeldung erforderlich) machen.
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/de-de/menu.md b/includes/kohana/modules/userguide/guide/de-de/menu.md
new file mode 100644
index 00000000..88e7bc8e
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/de-de/menu.md
@@ -0,0 +1,31 @@
+1. **Erste Schritte**
+ - [Was ist Kohana?](about.kohana)
+ - [Regeln und Stil](about.conventions)
+ - [Model-View-Controller](about.mvc)
+ - [Kaskaden-Dateisystem](about.filesystem)
+ - [Anfrageablauf](about.flow)
+ - [Installation](about.install)
+ - [Upgrading](about.upgrading)
+ - [Schnittstellenbersicht](api)
+2. **Allgemeine Verwendung**
+ - [Einstellungen](using.configuration)
+ - [autom. Klassen-Aufruf](using.autoloading)
+ - [Views und HTML](using.views)
+ - [Sessions und Cookies](using.sessions)
+ - [Nachrichten](using.messages)
+3. **Fehlersuche**
+ - [Quelltext](debugging.code)
+ - [Fehlerbehandlung](debugging.errors)
+ - [Programmanalyse](debugging.profiling)
+4. **Sicherheit**
+ - [XSS](security.xss)
+ - [Validierung](security.validation)
+ - [Cookies](security.cookies)
+ - [Datenbank](security.database)
+5. **Tutorials**
+ - [Hallo Welt](tutorials.helloworld)
+ - [Routen, URLs und Verweise](tutorials.urls)
+ - [Bereinigte URLs](tutorials.removeindex)
+ - [Datenbanken](tutorials.databases)
+ - [ORM](tutorials.orm)
+ - [Arbeit mit Git](tutorials.git)
diff --git a/includes/kohana/modules/userguide/guide/debugging.code.md b/includes/kohana/modules/userguide/guide/debugging.code.md
new file mode 100644
index 00000000..f08b2b01
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/debugging.code.md
@@ -0,0 +1,18 @@
+# Debugging
+
+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](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
+ echo Kohana::debug($foo, $bar);
+
+Kohana also provides a method to show the source code of a particular file using [Kohana::debug_source].
+
+ // Display this line of source code
+ echo Kohana::debug_source(__FILE__, __LINE__);
+
+If you want to display information about your application files without exposing the installation directory, you can use [Kohana::debug_path]:
+
+ // Displays "APPPATH/cache" rather than the real path
+ echo Kohana::debug_path(APPPATH.'cache');
diff --git a/includes/kohana/modules/userguide/guide/debugging.errors.md b/includes/kohana/modules/userguide/guide/debugging.errors.md
new file mode 100644
index 00000000..b0f62455
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/debugging.errors.md
@@ -0,0 +1,22 @@
+# Error/Exception Handling
+
+Kohana provides both an exception handler and an error handler that transforms errors into exceptions using PHP's [ErrorException](http://php.net/errorexception) class. Many details of the error and the internal state of the application is displayed by the handler:
+
+1. Exception class
+2. Error level
+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
+
+## Example
+
+Click any of the links to toggle the display of additional information:
+
+
{{userguide/examples/error}}
+
+## Disabling Error/Exception Handling
+
+If you do not want to use the internal error handling, you can disable it when calling [Kohana::init]:
+
+ Kohana::init(array('errors' => FALSE));
diff --git a/includes/kohana/modules/userguide/guide/debugging.profiling.md b/includes/kohana/modules/userguide/guide/debugging.profiling.md
new file mode 100644
index 00000000..493dd484
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/debugging.profiling.md
@@ -0,0 +1,20 @@
+# Profiling
+
+Kohana provides a very simple way to display statistics about your application:
+
+1. Common [Kohana] method calls
+2. Requests
+3. [Database] queries
+4. Average execution times for your application
+
+## Example
+
+You can display or collect the current [profiler] statistics at any time:
+
+
+
+
+
+## Preview
+
+{{profiler/stats}}
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/es-es/debugging.errors.md b/includes/kohana/modules/userguide/guide/es-es/debugging.errors.md
new file mode 100644
index 00000000..aa124880
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/es-es/debugging.errors.md
@@ -0,0 +1,24 @@
+# Gestión de Errores/Excepciones
+
+Kohana proporciona un gestor de errores y excepciones que transforma errores en excepciones usando la clase [ErrorException](http://php.net/errorexception) de PHP. Se muestran muchos detalles del error y del estado interno de la aplicación:
+
+1. Clase de excepción
+2. Nivel del error
+3. Mensaje de error
+4. Fuente del error, con la línea del error resaltada
+5. Una [depuración hacia atrás](http://php.net/debug_backtrace) del flujo de ejecución
+6. Archivos incluídos, extensiones cargadas y variables globales
+
+## Ejemplo
+
+Haz clic en cualquiera de los enlaces para mostrar la información adicional:
+
+
{{userguide/examples/error}}
+
+## Desactivando la Gestión de Errores/Excepciones
+
+Si no quieres usar la gestión de errores interna, la puedes desactivar cuando se llama a [Kohana::init]:
+
+~~~
+Kohana::init(array('errors' => FALSE));
+~~~
diff --git a/includes/kohana/modules/userguide/guide/es-es/debugging.md b/includes/kohana/modules/userguide/guide/es-es/debugging.md
new file mode 100644
index 00000000..d277e327
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/es-es/debugging.md
@@ -0,0 +1,24 @@
+# Depuración
+
+Kohana incluye varias herramientas útiles que te ayudarán en la depuración de tus aplicaciones.
+
+La más básica de ellas es [Kohana::debug]. Este método simple mostrará cualquier número de variables, similar a [var_export] o [print_r], pero usando HTML para una mejor visualización.
+
+~~~
+// Mostrar el contenido de las variables $foo y $bar
+echo Kohana::debug($foo, $bar);
+~~~
+
+Kohana también proporciona un método para mostrar el código fuente de una línea particular usando [Kohana::debug_source].
+
+~~~
+// Mostrar esta línea del código
+echo Kohana::debug_source(__FILE__, __LINE__);
+~~~
+
+Si quieres mostrar información sobre los archivos de tu aplicación sin exponer el directorio de instalación, puedes usar [Kohana::debug_path]:
+
+~~~
+// Mostrar "APPPATH/cache" en vez de la ruta real
+echo Kohana::debug_file(APPPATH.'cache');
+~~~
diff --git a/includes/kohana/modules/userguide/guide/es-es/debugging.profiling.md b/includes/kohana/modules/userguide/guide/es-es/debugging.profiling.md
new file mode 100644
index 00000000..f38b78ee
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/es-es/debugging.profiling.md
@@ -0,0 +1,22 @@
+# Análisis de Rendimiento
+
+Kohana proporciona una forma muy simple de mostrar estadísticas sobre tu aplicación:
+
+1. Los métodos de [Kohana] más usados
+2. Peticiones
+3. Consultas a la Base de Datos ([Database])
+4. Tiempo de ejecución media de tu aplicación
+
+## Ejemplo
+
+En cualquier momento puedes mostrar o recolectar las estadísticas actuales del analizador ([profiler]):
+
+~~~
+
+
+
+~~~
+
+## Vista previa
+
+{{profiler/stats}}
diff --git a/includes/kohana/modules/userguide/guide/es-es/features.md b/includes/kohana/modules/userguide/guide/es-es/features.md
new file mode 100644
index 00000000..e846d3b0
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/es-es/features.md
@@ -0,0 +1 @@
+Esta página lista las características de Kohana v3
diff --git a/includes/kohana/modules/userguide/guide/es-es/menu.md b/includes/kohana/modules/userguide/guide/es-es/menu.md
new file mode 100644
index 00000000..c6eaed2f
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/es-es/menu.md
@@ -0,0 +1,23 @@
+1. **[Cómo Empezar](start)**
+ - [Convenciones y Estilos](start.conventions)
+ - [Instalación](start.installation)
+ - [Configuración](start.configuration)
+ - [Modelo Vista Controlador](start.mvc)
+ - [Sistema de Archivos](start.filesystem)
+ - [Autocarga](start.autoloading)
+ - [Proceso de las Peticiones](start.flow)
+2. **[Tutoriales](tutorials)**
+ - [Hola, Mundo](tutorials.helloworld)
+ - [Rutas, URLs, y Enlaces](tutorials.urls)
+ - [Bases de Datos](tutorials.databases)
+3. **[Seguridad](security)**
+ - [XSS](security.xss)
+ - [Validación](security.validation)
+ - [Cookies](security.cookies)
+ - [Bases de Datos](security.database)
+4. **[Depuración](debugging)**
+ - [Gestión de Errores](debugging.errors)
+ - [Análisis de Rendimiento](debugging.profiling)
+5. **[Actualizando](upgrading)**
+ - [Desde 2.3](upgrading.23)
+6. **[Explorar API](api)**
diff --git a/includes/kohana/modules/userguide/guide/es-es/security.cookies.md b/includes/kohana/modules/userguide/guide/es-es/security.cookies.md
new file mode 100644
index 00000000..c4bc0da7
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/es-es/security.cookies.md
@@ -0,0 +1,3 @@
+# Seguridad en las Cookies
+
+[!!] inacabado
diff --git a/includes/kohana/modules/userguide/guide/es-es/security.database.md b/includes/kohana/modules/userguide/guide/es-es/security.database.md
new file mode 100644
index 00000000..2d86664b
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/es-es/security.database.md
@@ -0,0 +1,3 @@
+# Seguridad en las Bases de Datos
+
+[!!] inacabado
diff --git a/includes/kohana/modules/userguide/guide/es-es/security.md b/includes/kohana/modules/userguide/guide/es-es/security.md
new file mode 100644
index 00000000..0b1b50aa
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/es-es/security.md
@@ -0,0 +1,3 @@
+# Seguridad
+
+[!!] inacabado
diff --git a/includes/kohana/modules/userguide/guide/es-es/security.validation.md b/includes/kohana/modules/userguide/guide/es-es/security.validation.md
new file mode 100644
index 00000000..440151fe
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/es-es/security.validation.md
@@ -0,0 +1,3 @@
+# Validación
+
+[!!] inacabado
diff --git a/includes/kohana/modules/userguide/guide/es-es/security.xss.md b/includes/kohana/modules/userguide/guide/es-es/security.xss.md
new file mode 100644
index 00000000..0f16deb1
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/es-es/security.xss.md
@@ -0,0 +1,15 @@
+# Seguridad en Cross-Site Scripting (XSS)
+
+El primer paso para prevenir los ataques de [XSS](http://es.wikipedia.org/wiki/Cross-site_scripting) es saber cuando necesitas protegerte a ti mismo. El XSS sólo puede llevarse a cabo cuando se muestra dentro de contenido HTML, muchas veces vía una entrada de formulario o cuando mostramos datos de resultados de la base de datos. Cualquier variable global que contenga información desde el cliente puede ser contaminada. Esto incluye los datos de las variables $_GET, $_POST, y $_COOKIE.
+
+## Prevención
+
+Hay unas pocas reglas simples para proteger el HTML tu aplicación del XSS. La primera es usar el método [Security::xss] para limpiar cualquier dato de entrada que venga de una variable global. Si no necesitas HTML en una variable, usa [strip_tags](http://php.net/strip_tags) para remover todas las etiquetas HTML innecesarias del contenido.
+
+[!!] Si quieres permitir a los usuarios enviar HTML en tu aplicación, es altamente recomendable usar una herramienta de limpieza de HTML como [HTML Purifier](http://htmlpurifier.org/) o [HTML Tidy](http://php.net/tidy).
+
+La segunda es que siempre debemos escapar los datos cuando se insertan en el HTML. La clase [HTML] proporciona generadores para muchas de las principales etiquetas, incluyendo scripts, hojas de estilo, enlaces, imágenes, y email (mailto). Cualquier contenido no verificado debería escaparse usando [HTML::chars].
+
+## Referencias
+
+* [OWASP XSS Cheat Sheet](http://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet)
diff --git a/includes/kohana/modules/userguide/guide/es-es/start.autoloading.md b/includes/kohana/modules/userguide/guide/es-es/start.autoloading.md
new file mode 100644
index 00000000..b115565f
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/es-es/start.autoloading.md
@@ -0,0 +1,17 @@
+# Autocarga
+
+Kohana aprovecha la habilidad de PHP [autocarga](http://docs.php.net/manual/es/language.oop5.autoload.php). Esto elimina la necesidad de llamar a [include](http://php.net/include) o [require](http://php.net/require) antes de usar una clase.
+
+Las clases son cargadas usando el método [Kohana::auto_load], el cual hace una simple conversión del nombre de la clase al nombre del archivo:
+
+1. Las clases son colocadas en el directorio `classes/` del [sistema de archivos](start.filesystem)
+2. Cualquier caracter de barra baja es convertido a barra invertida
+2. El nombre de archivo es todo en minúsculas
+
+Cuando llamamos a una clase que no ha sido cargada (por ejemplo: `Session_Cookie`), Kohana buscará en el sistema de archivos usando [Kohana::find_file] un archivo llamado `classes/session/cookie.php`.
+
+## Autocargadores personalizados
+
+[!!] El autocargador por defecto es activado en `application/bootstrap.php`.
+
+Los cargadores de clases adicionales pueden ser añadidos usando [spl_autoload_register](http://php.net/spl_autoload_register).
diff --git a/includes/kohana/modules/userguide/guide/es-es/start.configuration.md b/includes/kohana/modules/userguide/guide/es-es/start.configuration.md
new file mode 100644
index 00000000..0413403f
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/es-es/start.configuration.md
@@ -0,0 +1,94 @@
+# Configuración General
+
+[!!] por hacer, descripción de los beneficios de las propiedades estáticas para la configuración
+
+## Configuración Principal
+
+La primera tarea de configuración de cualquier nueva instalación de Kohana es cambiar la configuración de inicio [Kohana::init] en `application/bootstrap.php`. Los datos configurables son:
+
+`boolean` errors
+: ¿Usar el gestor de errores y excepciones interno? (Por defecto `TRUE`) Establecer a `FALSE` para desactivar el gestor de errores y excepciones de Kohana.
+
+`boolean` profile
+: ¿Hacer análisis de rendimiento interno? (Por defecto `TRUE`) Establecer a `FALSE` para desactivarlo. En sitios en producción debería estar desactivado para un mejor rendimiento.
+
+`boolean` caching
+: ¿Cachear la localización de los archivos entre peticiones? (Por defecto `FALSE`) Establecer a `TRUE` para cachear la
+ ruta absoluta de los archivos. Esto aumenta dramáticamente la velocidad de [Kohana::find_file] y puede muchas veces
+ tener un impacto dramático en el desempeño. Sólo activar en sitios en producción o para su prueba.
+
+`string` charset
+: Juego de caracteres usado para todas las entradas y salidas. (Por defecto `"utf-8"`) Debería ser un juego de caracteres que sea soportado por [htmlspecialchars](http://php.net/htmlspecialchars) e [iconv](http://php.net/iconv).
+
+`string` base_url
+: URL base de la aplicación. (Por defecto `"/"`) Puede ser una URL completa o parcial. Por ejemplo "http://example.com/kohana/" o sólo "/kohana/" funcionan ambas por igual.
+
+`string` index_file
+: El archivo PHP que inicia la aplicación. (Por defecto `"index.php"`) Establecer a `FALSE` cuando elimines el archivo index con la reescritura de la URL (mod_rewrite y similares).
+
+`string` cache_dir
+: Directorio de la Cache. (Por defecto `"application/cache"`) Debe apuntar a un directorio **escribible**.
+
+## Configuración de las Cookies
+
+Hay varias propiedades estáticas en la clase [Cookie] que deberían establecerse, especialmente en sitios en producción.
+
+`string` salt
+: Cadena que es usada para crear [cookies cifradas](security.cookies)
+
+`integer` expiration
+: Tiempo de expiración en segundos
+
+`string` path
+: Ruta URL para restringir dónde pueden ser accedidas las cookies
+
+`string` domain
+: Dominio URL para restringir dónde pueden ser accedidas las cookies
+
+`boolean` secure
+: Permitir que las cookies sólo sean accedidas por HTTPS
+
+`boolean` httponly
+: Permitir que las cookies sólo sean accedidas por HTTP (también desactiva el acceso por Javascript)
+
+# Archivos de Configuración
+
+La configuración se establece en archivos PHP planos, del estilo de:
+
+~~~
+ 'value',
+ 'options' => array(
+ 'foo' => 'bar',
+ ),
+);
+~~~
+
+Si el archivo de configuración anterior se llamaba `myconf.php`, puedes acceder a él usando:
+
+~~~
+$config = Kohana::config('myconf');
+$options = $config['options'];
+~~~
+
+[Kohana::config] también proporciona una forma corta para acceder a valores individuales del array de configuración usando "rutas con puntos".
+
+Obtener el array "options":
+
+~~~
+$options = Kohana::config('myconf.options');
+~~~
+
+Obtener el valor de "foo" del array "options":
+
+~~~
+$foo = Kohana::config('myconf.options.foo');
+~~~
+
+Los arrays de configuración también pueden ser accedidos como objetos, si prefieres ese método:
+
+~~~
+$options = Kohana::config('myconf')->options;
+~~~
diff --git a/includes/kohana/modules/userguide/guide/es-es/start.conventions.md b/includes/kohana/modules/userguide/guide/es-es/start.conventions.md
new file mode 100644
index 00000000..731f1373
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/es-es/start.conventions.md
@@ -0,0 +1,26 @@
+# Convenciones
+
+## Nombre de las clases y la localización del archivo
+
+Los nombres de las clases en Kohana siguen una forma estricta para facilitar la [autocarga](start.autoloading).
+
+Los nombres de las clases deben tener la primera letra mayúscula con barra baja para separar palabras. Las barras bajas son significativas ya que directamente reflejan la localización del archivo en el sistema de archivos.
+
+ Clase Archivo
+
+ Controller_Template classes/controller/template.php
+ Model_User classes/model/user.php
+ Model_Auth_User classes/model/auth/user.php
+ Auth classes/auth.php
+
+Los nombres de las clases del estilo de PrimeraMayuscula no deberían ser usadas.
+
+Todos los nombres de los archivos de las clases y los directorios van en minúscula.
+
+Todas las clases deben ir en el directorio `classes`. Esto debe ser así en cualquier nivel del [sistema de archivos en cascada](start.filesystem).
+
+Kohana 3 no diferencia entre *tipos* de clases de la misma forma en que Kohana 2.x y otros frameworks lo hacen. No hay diferencia entre una clase tipo 'helper' o una de tipo 'library' - en Kohana 3 cualquier clase puede implementar cualquier interface que necesite o ser estática totalmente (estilo helper), o instanciable, o una mezcla (por ejemplo singleton).
+
+## Estilo de Código
+
+Se recomienda seguir el estilo de código usado en Kohana. Usamos el [estilo BSD/Allman](http://en.wikipedia.org/wiki/Indent_style#BSD.2FAllman_style). ([Descripción más pormenorizada](http://dev.kohanaphp.com/wiki/kohana2/CodingStyle) del estilo de código preferido por Kohana)
diff --git a/includes/kohana/modules/userguide/guide/es-es/start.filesystem.md b/includes/kohana/modules/userguide/guide/es-es/start.filesystem.md
new file mode 100644
index 00000000..bee39919
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/es-es/start.filesystem.md
@@ -0,0 +1,13 @@
+# Sistema de Archivos en Cascada
+
+El sistema de archivos de Kohana se compone de una única estructura de directorios que es repetida a lo largo de todos los directorios, lo que llamamos la ruta de inclusión, que sigue el orden:
+
+1. application
+2. modules, según el orden en que sean añadidos
+3. system
+
+Los archivos que se encuentran en directorios superiores en la lista de las rutas de inclusión tienen preferencia sobre los archivos del mismo nombre pero que están más abajo, lo cual hace posible sobrecargar cualquier archivo colocando otro archivo con el mismo nombre en un directorio superior:
+
+
+
+Si tiene un archivo de Vista llamado layout.php en los directorios application/views y system/views, será devuelto el que se encuentra bajo application cuando se busque por layout.php ya que se encuentra más arriba en la lista de inclusión ordenada. Si elimina ese archivo de application/views, el que se encuentra en system/views será devuelto cuando lo busquemos.
diff --git a/includes/kohana/modules/userguide/guide/es-es/start.flow.md b/includes/kohana/modules/userguide/guide/es-es/start.flow.md
new file mode 100644
index 00000000..401973aa
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/es-es/start.flow.md
@@ -0,0 +1,21 @@
+# Proceso de las Peticiones
+
+Cada aplicación sigue el siguiente proceso:
+
+1. La aplicación empieza desde el archivo `index.php`
+2. Incluye `APPPATH/bootstrap.php`
+3. bootstrap.php llama a [Kohana::modules] con la lista de módulos usados
+ 1. Genera una matriz con las rutas para el sistema de archivos en cascada
+ 2. Comprueba cada módulo para ver si tiene un init.php, y si lo tiene, lo carga
+ * Cada init.php puede definir una serie de rutas a usar, que son cargadas cuando el archivo init.php es incluido
+4. [Request::instance] es llamada para procesar la petición
+ 1. Comprueba cada ruta hasta que se encuentra una coincidencia
+ 2. Carga el controlador y le pasa la petición
+ 3. Llama al método [Controller::before]
+ 4. Llama a la acción del controlador
+ 5. Llama al método [Controller::after]
+5. Muestra la respuesta a la petición ([Request])
+
+La acción del controlador puede ser cambiada por el método [Controller::before] en base a los parámetros de la petición.
+
+[!!] inacabado
diff --git a/includes/kohana/modules/userguide/guide/es-es/start.installation.md b/includes/kohana/modules/userguide/guide/es-es/start.installation.md
new file mode 100644
index 00000000..6599326e
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/es-es/start.installation.md
@@ -0,0 +1,19 @@
+# Instalación
+
+1. Descarga la última versión **estable** de la [web de Kohana](http://kohanaphp.com/)
+2. Descomprime el archivo descargado para crear un directorio `kohana`
+3. Sube el contenido de esta carpeta a tu servidor
+4. Abre `application/bootstrap.php` y haz los cambios siguientes:
+ - Establece la [zona horaria](http://php.net/timezones) por defecto para tu aplicación
+ - Establece `base_url` en la llamada a [Kohana::init] para reflejar la localización de la carpeta de kohana en tu servidor
+6. Comprueba que los directorios `application/cache` y `application/logs` tienen permisos de escritura para todos con `chmod application/{cache,logs} 0777`
+7. Comprueba tu instalación abriendo la url que has establecido en `base_url` en tu navegador favorito
+
+[!!] Dependiendo de tu plataforma, los subdirectorios de la instalación podrían haber perdido sus permisos debido a la descompresión zip. Para cambiarle los permisos a todos ejecutar `find . -type d -exec chmod 0755 {} \;` desde la raíz de la instalación de Kohana.
+
+Deberías ver la página de instalación. Si reporta algún error, debes corregirlo antes de continuar.
+
+
+
+Una vez que la página de instalación reporta que tu entorno está correcto, debes renombrar o borrar el archivo `install.php` del directorio raíz. Entonces deberías ver la página de bienvenida de Kohana (el texto `hello, world!`).
+
diff --git a/includes/kohana/modules/userguide/guide/es-es/start.md b/includes/kohana/modules/userguide/guide/es-es/start.md
new file mode 100644
index 00000000..826102e3
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/es-es/start.md
@@ -0,0 +1,11 @@
+# ¿Qué es Kohana?
+
+Kohana es un [framework web](http://es.wikipedia.org/wiki/Framework_para_aplicaciones_web) [MVC](http://es.wikipedia.org/wiki/Modelo_Vista_Controlador "Modelo Vista Controlador") de código abierto y [orientado a objetos](http://es.wikipedia.org/wiki/Programaci%C3%B3n_orientada_a_objetos) realizado para [PHP5](http://docs.php.net/manual/es/intro-whatis.php "Preprocesador de Hipertexto PHP") por un equipo de voluntarios, y destaca por ser rápido, seguro y ligero.
+
+[!!] Kohana está licenciado bajo una [licencia BSD](http://kohanaphp.com/license), así que puede legalmente usarlo para cualquier tipo de proyecto de código abierto, comercial, o personal.
+
+## ¿Qué hace potente a Kohana?
+
+Cualquier cosa puede ser extendida usando el [sistema de archivos](start.filesystem) de diseño único, poco o nada hay que cambiar en la [configuración](start.configuration), la [gestión de errores](debugging.errors) ayuda a localizar los errores de código rápidamente, y la [depuración](debugging) y [profiling](debugging.profiling) informan sobre la aplicación en sí.
+
+Para que sus aplicaciones sean más seguras, se incluyen herramientas para [eliminar el XSS](security.xss), [validar de los datos de entrada](security.validation), [cookies](security.cookies), [formularios](security.forms) y [HTML](security.html). La capa de la [base de datos](security.database) proporciona protección de [inyección de código SQL](http://es.wikipedia.org/wiki/Inyecci%C3%B3n_SQL). Por supuesto, todo el código oficial ha sido cuidadosamente escrito y revisado pensando en la seguridad.
diff --git a/includes/kohana/modules/userguide/guide/es-es/start.mvc.md b/includes/kohana/modules/userguide/guide/es-es/start.mvc.md
new file mode 100644
index 00000000..496695b3
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/es-es/start.mvc.md
@@ -0,0 +1,5 @@
+# Modelo Vista Controlador
+
+Modelo Vista Controlador (o MVC) es un patrón de diseño popular que separa el origen de tus datos (Modelo) de la presentación/plantillas (Vista) y la lógica de la petición (Controlador).
+
+Esto hace mucho más fácil desarrollar aplicaciones y el sistema es diseñado para maximizar la reutilización de código, lo que se traduce en que ¡no tendrás que escribir mucho!
diff --git a/includes/kohana/modules/userguide/guide/es-es/tutorials.databases.md b/includes/kohana/modules/userguide/guide/es-es/tutorials.databases.md
new file mode 100644
index 00000000..14f29252
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/es-es/tutorials.databases.md
@@ -0,0 +1,3 @@
+# Bases de Datos
+
+[!!] inacabado
diff --git a/includes/kohana/modules/userguide/guide/es-es/tutorials.helloworld.md b/includes/kohana/modules/userguide/guide/es-es/tutorials.helloworld.md
new file mode 100644
index 00000000..cf451c79
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/es-es/tutorials.helloworld.md
@@ -0,0 +1,106 @@
+# Hola, Mundo
+
+Muchos frameworks proporcionan algún ejemplo de tipo hola mundo, de forma que ¡sería muy grosero por nuestra parte romper esa tradición!
+
+Empezaremos creando un hola mundo muy básico, y luego lo ampliaremos para seguir con los principios del patrón MVC.
+
+## Lo esencial
+
+En primer lugar, tenemos que crear un controlador que kohana usará para manejar la petición
+
+Crea el archivo `application/classes/controller/hello.php` en tu directorio **application** y copia dentro el siguiente código:
+
+ template->message = 'hello, world!';
+ }
+ }
+
+`extends Controller_Template`
+: Ahora estamos extendiendo el controlador de plantillas, el cual es más conveniente para usar vistas en nuestro controlador.
+
+`public $template = 'site';`
+: El controlador de plantillas necesita conocer que plantilla queremos usar. Automáticamente cargará la vista definida en esta variable y le asignará el objeto de tipo vista.
+
+`$this->template->message = 'hello, world!';`
+: `$this->template` es una referencia al objeto tipo vista de nuestra plantilla del sitio. Lo que estamos haciendo es asignar a una variable de la vista llamada "message" el valor de "hello, world!"
+
+Ahora intentamos ejecutar nuestro código...
+
+
{{userguide/examples/hello_world_error}}
+
+Por alguna razón kohana lanza una excepción y no muestra nuestro sorprendente mensaje.
+
+Si miramos dentro del mensaje de error podemos ver que la librería View no es capaz de encontrar la plantilla de nuestro sitio, probablemente porque no ha sido creada todavía - ¡*ouch*!
+
+Vamos y creamos el archivo de vista `application/views/site.php` para nuestro mensaje -
+
+
+
+ We've got a message for you!
+
+
+
+
+
We just wanted to say it! :)
+
+
+
+Si luego refrescamos la página podremos ver el fruto de nuestra labor -
+
+
+
+## En resumen
+
+En este tutorial has aprendido cómo crear un controlador y usar una vista para separar la lógica de la presentación.
+
+Esto es obviamente una introducción muy básica al trabajo con kohana y no entra de lleno en el potencial que tienes cuando desarrollas aplicaciones con él.
diff --git a/includes/kohana/modules/userguide/guide/es-es/tutorials.md b/includes/kohana/modules/userguide/guide/es-es/tutorials.md
new file mode 100644
index 00000000..a4363aec
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/es-es/tutorials.md
@@ -0,0 +1,7 @@
+# Tutoriales
+
+[!!] inacabado
+
+- [Hola, Mundo](tutorials.helloworld)
+- [Rutas, URLs, y Enlaces](tutorials.urls)
+- [Bases de Datos](tutorials.databases)
diff --git a/includes/kohana/modules/userguide/guide/es-es/tutorials.urls.md b/includes/kohana/modules/userguide/guide/es-es/tutorials.urls.md
new file mode 100644
index 00000000..21c6912a
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/es-es/tutorials.urls.md
@@ -0,0 +1,3 @@
+# Rutas, URLs, y Enlaces
+
+[!!] inacabado
diff --git a/includes/kohana/modules/userguide/guide/es-es/upgrading.md b/includes/kohana/modules/userguide/guide/es-es/upgrading.md
new file mode 100644
index 00000000..07fefeca
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/es-es/upgrading.md
@@ -0,0 +1,5 @@
+# Actualizando
+
+Obviamente te gustaría actualizar tu código desde la versión 2 a la 3, y para hacer esta transición más fácil hemos recopilado algunos de los principales cambios desde la versión
+
+* [Kohana 2.3](upgrading.23)
diff --git a/includes/kohana/modules/userguide/guide/features.md b/includes/kohana/modules/userguide/guide/features.md
new file mode 100644
index 00000000..25cbb18e
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/features.md
@@ -0,0 +1 @@
+This page lists the features of Kohana v3
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/fr-fr/about.autoloading.md b/includes/kohana/modules/userguide/guide/fr-fr/about.autoloading.md
new file mode 100644
index 00000000..753bc9eb
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/fr-fr/about.autoloading.md
@@ -0,0 +1,17 @@
+# Auto-chargement de classes
+
+Kohana tire partie de la fonctionnalité PHP d'[auto-chargement de classes](http://php.net/manual/fr/language.oop5.autoload.php) permettant de s'affranchir des inclusions manuelles avec [include](http://de.php.net/manual/fr/function.include.php) ou [require](http://de.php.net/manual/fr/function.require.php).
+
+Les classes sont chargées via la méthode [Kohana::auto_load], qui à partir du nom d'une classe, retrouve le fichier associé:
+
+1. Les classes sont placées dans le répertoire `classes/` de l'[arborescence de fichiers](about.filesystem)
+2. Les caractères underscore '_' sont convertis en slashes '/'
+2. Les noms de fichier doivent être en minuscule
+
+Lors de l'appel à une classe non chargée (eg: `Session_Cookie`), Kohana recherchera dans son arboresence via la méthode [Kohana::find_file] le fichier `classes/session/cookie.php`.
+
+## Auto-chargement tiers
+
+[!!] Le mécanisme par défaut d'auto-chargement de classes est défini dans le fichier `application/bootstrap.php`.
+
+Des mécanismes d'auto-chargement supplémentaires peuvent être ajoutés en utilisant [spl_autoload_register](http://php.net/spl_autoload_register).
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/fr-fr/about.configuration.md b/includes/kohana/modules/userguide/guide/fr-fr/about.configuration.md
new file mode 100644
index 00000000..0ac81c6f
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/fr-fr/about.configuration.md
@@ -0,0 +1,95 @@
+# Configuration Générale
+
+[!!] todo, description of benefits of static properties for configuration
+
+## Configuration du noyau
+
+La toute première configuration à modifier lors d'une installation de kohana est de changer les paramètres d'initlalisation [Kohana::init] dans le fichier `application/bootstrap.php`. Ces paramètres sont:
+
+`boolean` errors
+: Utilisation de la gestion des erreurs et des exceptions? (Défaut `TRUE`) Affecter à `FALSE` pour désactiver
+ la gestion des erreurs et exceptions.
+
+`boolean` profile
+: Activer le benchmarking interne? (Défault `TRUE`) Affecter à `FALSE` pour désactiver le benchmarking interne.
+ A desactiver en production pour obtenir de meilleures performances.
+
+`boolean` caching
+: Mettre en cache les chemins des fichiers entre les requêtes? (Défault `FALSE`) Affecter à `TRUE` pour mettre en cache
+ les chemins absolus. Ceci peut améliorer drastiquement les performances de la méthode [Kohana::find_file].
+
+`string` charset
+: Jeu de caractères à utiliser pour toutes les entrées et sorties. (Défault `"utf-8"`) Affecter un jeu de caractères supporté aussi bien par [htmlspecialchars](http://fr.php.net/htmlspecialchars) que [iconv](http://fr.php.net/iconv).
+
+`string` base_url
+: URL racine de l'application. (Défault `"/"`) Peut être une URL complète ou partielle. Par exemple "http://example.com/kohana/" ou "/kohana/" fonctionneraient.
+
+`string` index_file
+: Le fichier PHP qui démarre l'application. (Défault `"index.php"`) Affecter à `FALSE` pour enlever le fichier index de l'URL en utilisant l'URL Rewriting.
+
+`string` cache_dir
+: Répertoire de stockage du cache. (Défault `"application/cache"`) Doit pointer vers un répertoire **inscriptible**.
+
+## Paramètres des Cookies
+
+Il y a plusieurs propriétés statiques dans la classe [Cookie] qui doivent être paramétrées, particuliérement sur les sites en production.
+
+`string` salt
+: La chaîne d'aléa (salt) unique utilisée pour [signer les cookies](security.cookies)
+
+`integer` expiration
+: La durée d'expiration par défaut
+
+`string` path
+: Restreindre l'accès aux cookies par rapport au chemin spécifié
+
+`string` domain
+: Restreindre l'accès aux cookies par rapport au domaine spécifié
+
+`boolean` secure
+: N'autoriser les cookies qu'en HTTPS
+
+`boolean` httponly
+: N'autorise l'accès aux cookies que via HTTP (désactive aussi l'accès javascript)
+
+# Fichiers de configuration
+
+La configuration de Kohana est faite dans des fichiers à plat PHP, qui ressemblent à l'exemple ci-dessous:
+
+~~~
+ 'value',
+ 'options' => array(
+ 'foo' => 'bar',
+ ),
+);
+~~~
+
+Supposons que le fichier ci-dessus soit appelé `myconf.php`, il est alors possible d'y accèder de la manière suivante:
+
+~~~
+$config = Kohana::config('myconf');
+$options = $config['options'];
+~~~
+
+[Kohana::config] fournit aussi un raccourci pour accèder à des clés spécifiques des tableaux de configuration en utilisant des chemins spérarés par le caractère point.
+
+Récupérer le tableau "options":
+
+~~~
+$options = Kohana::config('myconf.options');
+~~~
+
+Récupérer la valeur de la clé "foo" du tableau "options":
+
+~~~
+$foo = Kohana::config('myconf.options.foo');
+~~~
+
+Les tableaux de configuration peuvent aussi être parcourus comme des objets comme suit:
+
+~~~
+$options = Kohana::config('myconf')->options;
+~~~
diff --git a/includes/kohana/modules/userguide/guide/fr-fr/about.conventions.md b/includes/kohana/modules/userguide/guide/fr-fr/about.conventions.md
new file mode 100644
index 00000000..2ac39c69
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/fr-fr/about.conventions.md
@@ -0,0 +1,26 @@
+# Conventions et style de codage
+
+## Nom de classe et emplacement des fichiers
+
+Les noms de classe dans Kohana suivent des règles strictes pour faciliter l'[auto-chargement de classes](about.autoloading).
+
+Ils doivent avoir la première lettre en majuscule, et les mots doivent être séparés par des underscores. Les underscores sont très importants car ils déterminent le chemin d'accès au fichier.
+
+Nom de classe | Chemin
+----------------------|-------------------------------
+Controller_Template | classes/controller/template.php
+Model_User | classes/model/user.php
+Database | classes/database.php
+Database_Query | classes/database/query.php
+
+Les noms de classe ne doivent pas utiliser de syntaxe CamelCase sauf si vous ne souhaitez pas créer un nouveau niveau de répertoire.
+
+Tous les noms de fichier et répertoire sont en minuscule.
+
+Toutes les classes doivent être dans le répertoire `classes`. Elles peuvent néanmoins être sur plusieurs niveaux de répertoire de l'[arborescence](about.filesystem).
+
+Kohana 3 ne différencie pas les *types* de classe comme le fait Kohana 2.x. Il n'y a pas de distinction entre une classe 'helper' ou une 'librairie' – avec Kohana 3 toute classe peut implémenter l'interface que vous souhaitez, qu'elle soit statique (helper), instanciable, ou mixte (e.g. singleton).
+
+## Style de codage
+
+Il est vivement conseillé de suivre les [styles de codage](http://dev.kohanaphp.com/wiki/kohana2/CodingStyle) de Kohana c'est-à-dire le [style BSD/Allman](http://en.wikipedia.org/wiki/Indent_style#BSD.2FAllman_style) pour les accolades, entre autres choses.
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/fr-fr/about.filesystem.md b/includes/kohana/modules/userguide/guide/fr-fr/about.filesystem.md
new file mode 100644
index 00000000..2a306300
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/fr-fr/about.filesystem.md
@@ -0,0 +1,13 @@
+# Arborescence de fichiers en cascade
+
+L'arborescence de fichiers de Kohana est construite autour d'une structure de répertoires unique qui est dupliquée dans tous les répertoires formant ce que l'on appelle l'"include path". Cette structure est composée des répertoires suivants et dans cet ordre:
+
+1. application
+2. modules, dans l'ordre dans lequel ils ont été ajoutés
+3. system
+
+Les fichiers qui sont dans les répertoires les plus haut de l'"include path" sont prioritaires par rapport aux fichiers de même noms dans des répertoires plus bas. Cela rend possible la surcharge de nimporte quel fichier en plaçant un fichier de même nom dans un répertoire de niveau supérieur:
+
+
+
+Par exemple, si vous avez un fichier appelé layout.php dans les répertoires application/views et system/views, alors celui contenu dans le répertoire application sera retourné lors de l'appel à layout.php du fait qu'il est plus haut dans l'"include path". Si vous supprimez le fichier de application/views, alors c'est celui contenu dans system/views qui sera alors retourné.
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/fr-fr/about.flow.md b/includes/kohana/modules/userguide/guide/fr-fr/about.flow.md
new file mode 100644
index 00000000..6f13cb7b
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/fr-fr/about.flow.md
@@ -0,0 +1,21 @@
+# Processus de traitement des requêtes
+
+Toutes les applications suivent le même processus:
+
+1. L'application commence depuis `index.php`
+2. Inclut `APPPATH/bootstrap.php`
+3. L'initialisation (bootstrap) appelle [Kohana::modules] avec une liste de modules à utiliser
+ 1. Génére un tableau de chemins utilisés par l'arborescence en cascade
+ 2. Vérifie la présence du fichier init.php dans chaque module. Si il existe
+ * Chaque fichier init.php peut définir un ensemble de routes à utiliser, elles sont chargées lorsque le fichier init.php est inclut
+4. [Request::instance] est appelé pour traiter la requête
+ 1. Vérifie toutes les routes jusqu'à ce que l'une d'entres elles concorde
+ 2. Charge le controleur et lui transmet la requête
+ 3. Appelle la méthode [Controller::before]
+ 4. Appelle l'action du controleur
+ 5. Appelle la méthode [Controller::after]
+5. Affiche la réponse à la requête
+
+L'action du controleur peut etre changée suivant ses paramètres de la par [Controller::before].
+
+[!!] Stub
diff --git a/includes/kohana/modules/userguide/guide/fr-fr/about.install.md b/includes/kohana/modules/userguide/guide/fr-fr/about.install.md
new file mode 100644
index 00000000..86e533c1
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/fr-fr/about.install.md
@@ -0,0 +1,20 @@
+# Installation
+
+1. Téléchargez la dernière version **stable** depuis [le site web Kohana](http://kohanaphp.com/)
+2. Dézippez l'archive téléchargée pour créer le répertoire `kohana`
+3. Uploadez le contenu de ce répertoire sur votre serveur web
+4. Ouvrez `application/bootstrap.php` et effectuez les changements suivants:
+ - Affecter la [timezone](http://php.net/timezones) par défaut de votre application
+ - Affecter `base_url` dans l'appel à [Kohana::init] afin de faire comprendre à votre serveur ou est situé le répertoire kohana uploadé à l'étape précédente
+6. Vérifiez que les répertoires `application/cache` et `application/logs` sont inscriptibles en tapant la commande `chmod application/{cache,logs} 0777` (Linux).
+7. Testez votre installation en tapant l'URL que vous avez spécifiée dans `base_url` dans votre navigateur préféré
+
+[!!] Suivant votre plateforme, l'extraction de l'archive peut avoir changé les permissions sur les sous répertoires. Rétablissez-les avec la commande suivante: `find . -type d -exec chmod 0755 {} \;` depuis la racine de votre installation Kohana.
+
+Vous devriez alors voir la page d'installation contenant un rapport d'installation. Si une erreur est affichée, vous devez la corriger pour pouvoir continuer.
+
+
+
+Une fois que votre rapport d'installation vous informe que votre environnement est correctement configuré, vous devez soit renommer, soit supprimer le fichier `install.php`. Vous devriez alors voir apparaitre la page de bienvenue de Kohana:
+
+
diff --git a/includes/kohana/modules/userguide/guide/fr-fr/about.kohana.md b/includes/kohana/modules/userguide/guide/fr-fr/about.kohana.md
new file mode 100644
index 00000000..2897852b
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/fr-fr/about.kohana.md
@@ -0,0 +1,11 @@
+# Kohana... Kesako?
+
+Kohana est un [framework web](http://wikipedia.org/wiki/Web_Framework) [PHP5](http://php.net/manual/intro-whatis "PHP Hypertext Preprocessor") open source, [orienté objet](http://wikipedia.org/wiki/Object-Oriented_Programming), adoptant le design pattern [MVC](http://wikipedia.org/wiki/Model–View–Controller "Model View Controller"). Il vise à être rapide, sécurisé et léger.
+
+[!!] Kohana est licencié sous [licence BSD](http://kohanaphp.com/license), donc vous pouvez légalement l'utiliser pour tout projet open source, commercial ou personnel.
+
+## Pourquoi Kohana est-il différent?
+
+Tout peut être surchargé et étendu grâce à son [arborescence de fichiers en cascade](about.filesystem), il y a très peu de [configuration](about.configuration) nécessaire, la [gestion des erreurs](debugging.errors) aide à la localisation rapide de la source des erreurs, et enfin le [debugging](debugging) et le [profiling](debugging.profiling) vous fournissent les statistiques et informations nécessaires sur votre application.
+
+Pour vous aider dans la sécurisation de votre application, Kohana fournit des protections contre les attaques [XSS](security.xss), des méthodes de [filtrage et de validation des données d'entrées](security.validation), de [signature des cookies](security.cookies), de sécurisation de vos [formulaires](security.forms) et de génération [HTML](security.html). La couche [base de données](security.database) fournit des protections contre l'[injection SQL](http://wikipedia.org/wiki/SQL_Injection). Et bien évidemment, le code officiel est écrit et revu consciencieusement.
diff --git a/includes/kohana/modules/userguide/guide/fr-fr/about.mvc.md b/includes/kohana/modules/userguide/guide/fr-fr/about.mvc.md
new file mode 100644
index 00000000..16df839f
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/fr-fr/about.mvc.md
@@ -0,0 +1,5 @@
+# Modèle Vue Controleur
+
+Modèle Vue Controleur (ou MVC) est un design pattern populaire visant à séparer les sources de données (Modèle) de la présentation (Vue) et de l'enchainement logique de traitement de la requête (Controleur).
+
+Il rend plus facile le développement d'application modulaires et réutilisables.
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/fr-fr/about.upgrading.md b/includes/kohana/modules/userguide/guide/fr-fr/about.upgrading.md
new file mode 100644
index 00000000..4f0ec30b
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/fr-fr/about.upgrading.md
@@ -0,0 +1,290 @@
+# Mise à jour depuis 2.x
+
+Kohana v3 fonctionne très différemment de Kohana 2.x, néanmoins vous trouverez ci-dessous une liste d'astuces qui pourront vous aider dans votre tâche de mise à jour.
+
+## Conventions de nommage
+
+La série 2.x différentie les 'types' de classes (i.e. controleur, modele etc.) en utilisant des suffixes. Dans la série 3.0, cette approche a été abandonnée au profit des conventions du framework Zend c'est-à-dire que les noms de classe sont les chemins vers les classes elles-mêmes. Les répertoires du chemin sont séparés par le caractère underscore au lieu du slashe (i.e. `/some/class/file.php` devient `Some_Class_File`).
+
+Pour plus d'informations consultez la documentatation sur les [conventions de nommage](start.conventions).
+
+## Librairie Input
+
+La librairie Input a été supprimée en faveur de l'utilisation directe des variables `$_GET` et `$_POST`.
+
+### Protection XSS
+
+Si vous avez besoin de nettoyer des données contre des attaques XSS, vous pouvez utiliser [Security::xss_clean] de la manière suivante:
+
+ $_POST['description'] = security::xss_clean($_POST['description']);
+
+Vous pouvez aussi utiliser [Security::xss_clean] en tant que filtre via la librairie [Validate]:
+
+ $validation = new Validate($_POST);
+
+ $validate->filter('description', 'Security::xss_clean');
+
+### POST & GET
+
+Une des fonctionnalités très intéressante de la librairie Input était que lors de la tentative de lecture d'une variable superglobale, si celle-ci n'existait pas, il était possible de spécifier la valeur par défaut retournée i.e.:
+
+ $_GET = array();
+
+ // On assigne à $id la valeur 1
+ $id = Input::instance()->get('id', 1);
+
+ $_GET['id'] = 25;
+
+ // On assigne à $id la valeur 25
+ $id = Input::instance()->get('id', 1);
+
+En 3.0 cette fonctionnalité est rendue par la méthode [Arr::get]:
+
+ $_GET = array();
+
+ // On assigne à $id la valeur 1
+ $id = Arr::get($_GET, 'id', 1);
+
+ $_GET['id'] = 42;
+
+ // On assigne à $id la valeur 42
+ $id = Arr::get($_GET, 'id', 1);
+
+## Librairie ORM
+
+De nombreux changements majeurs ont été faits sur la librairie ORM depuis la série 2.x, et voici quelques-uns des problèmes les plus courants que vous pourrez rencontrer:
+
+### Variables de classe
+
+Toutes les variables de classe sont désormais préfixées par un underscore (_) et ne sont plus accessibles via `__get()`. A la place, vous devez appeler une méthode portant le nom de la propriété sans le caractère underscore.
+
+Par exemple, la propriété `loaded` en 2.x devient désormais `_loaded` et est accessible depuis l'extérieur via `$model->loaded()`.
+
+### Relations
+
+En 2.x, l'itération sur les objets liés à un modèle se faisait comme suit:
+
+ foreach($model->{relation_name} as $relation)
+
+Cependant avec la nouvelle librarie 3.0 cela ne fonctionnera pas. En effet en version 2.3, toutes les requêtes sont générées avec une portée globale, c'est-à-dire qu'il est impossible de construire 2 requêtes simultanément. Par exemple:
+
+# TODO: NEED A DECENT EXAMPLE!!!!
+
+La requête échouera car la seconde requête hérite des conditions de la première et fausse donc les filtres.
+
+En 3.0 ce problème a été corrigé car chaque requête à sa propre portée. Cela signifie aussi que certains de vos anciens codes ne fonctionneront plus. Prenez par exemple:
+
+ foreach(ORM::factory('user', 3)->where('post_date', '>', time() - (3600 * 24))->posts as $post)
+ {
+ echo $post->title;
+ }
+
+[!!] (Voir [le tutorial sur la Base de Données](tutorials.databases) pour la nouvelle syntaxe des requêtes)
+
+En 2.3 on reçoit un itérateur sur tous les posts de l'utilisateur d'id 3 et dont la date est dans l'intervalle spécifié. Au lieu de ça, la condition 'where' sera appliquée au modèle 'user' et la requête retournera un objet `Model_Post` avec les conditions de jointure comme spécifié.
+
+Pour obtenir le même résultat qu'en 2.x, en 3.0 la structure de la requête doit être modifiée:
+
+ foreach(ORM::factory('user', 3)->posts->where('post_date', '>', time() - (36000 * 24))->find_all() as $post)
+ {
+ echo $post->title;
+ }
+
+Cela s'applique aussi aux relations `has_one`:
+
+ // Incorrect
+ $user = ORM::factory('post', 42)->author;
+ // Correct
+ $user = ORM::factory('post', 42)->author->find();
+
+### Relations Has and belongs to many
+
+En 2.x vous pouvez spécifier des relations `has_and_belongs_to_many`. En 3.0 cette fonctionnalité a été renommée en `has_many` *through*.
+
+Dans vos modèles vous définissez une relation `has_many` avec les autres modèles et vous ajoutez un attribut `'through' => 'table'`, où `'table'` est le nom de la table de jointure. Par exemple dans la relation posts<>catégories:
+
+ $_has_many = array
+ (
+ 'categories' => array
+ (
+ 'model' => 'category', // Le modèle étranger
+ 'through' => 'post_categories' // La table de jointure
+ ),
+ );
+
+Si vous avez configuré Kohana pour utiliser une prefixe de table vous n'avez pas besoin d'explicitement préfixer la table.
+
+### Clés étrangères
+
+En 2.x, pour surcharger une clé étrangère vous deviez spécifier la relation auquelle elle appartenait et ajouter votre nouvelle clé étrangère dans la propriété `$foreign_keys`.
+
+En 3.0 il faut juste définir une clé `foreign_key` dans la définition de la relation comme suit:
+
+ Class Model_Post extends ORM
+ {
+ $_belongs_to = array
+ (
+ 'author' => array
+ (
+ 'model' => 'user',
+ 'foreign_key' => 'user_id',
+ ),
+ );
+ }
+
+Dans cet exemple on doit aussi avoir un champ `user_id` dans la table 'posts'.
+
+
+
+Dans les relations has_many le champ `far_key` est le champ de la table de jointure qui le lie à la table étrangère et la clé étrangère est le champ de la table de jointure qui lie la table du modèle courant ("this") avec la table de jointure.
+
+Considérez la configuration suivante où les "Posts" appartiennent à plusieurs "Categories" via `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',
+ ),
+ );
+ }
+
+
+Bien sûr l'exemple d'aliasing présenté ci-dessus est un peu exagéré, mais c'est un bon exemple de fonctionnement des clés foreign/far.
+
+### Itérateur ORM
+
+Il est important aussi de noter que `ORM_Iterator` a été renommé en `Database_Result`.
+
+Si vous avez besoin de récupérer un tableau d'objets ORM dont la clé est la clé étrangère de l'objet, vous devez utiliser [Database_Result::as_array], e.g.
+
+ $objects = ORM::factory('user')->find_all()->as_array('id');
+
+où `id` est la clé primaire de la table user.
+
+## Librairie Router
+
+En version 2.x il existe une librairie Router qui se charge du traitement des requêtes. Cela permet de définir des routes basiques dans le fichier`config/routes.php` et d'utiliser des expressions régulières mais au détriment de la flexibilité.
+
+## Routes
+
+Le sytème de routage est plus flexible en 3.0. Les routes sont maintenant définies dans le fichier bootstrap (`application/bootstrap.php`) et dans le cas des modules dans init.php (`modules/module_name/init.php`). Les routes sont évaluées dans l'ordre dans lequel elles sont définies.
+
+Aulieu de définir un tableau de routes, désormais on crée un objet [Route] pour chacunes des routes. Contraitement à la version 2.x, il n'est pas nécessaire d'associer une URI à une autre. Au lieu de ça, il faut spécifier un pattern pour une URI en utilisation des variables pour marquer les segments (i.e. controller, method, id).
+
+Par exemple, en 2.x on créé une route sous forme d'expression régulière comme suit:
+
+ $config['([a-z]+)/?(\d+)/?([a-z]*)'] = '$1/$3/$1';
+
+Cette route associe l'URI `controller/id/method` à `controller/method/id`.
+
+En 3.0 on utilise:
+
+ Route::set('reversed','((/(/)))')
+ ->defaults(array('controller' => 'posts', 'action' => 'index'));
+
+[!!] Chaque URI doit avoir un nom unique (dans l'exemple ci-dessus c'est `reversed`). La raison de ce choix est expliquée dans le [tutorial sur les URLs](tutorials.urls).
+
+Les chevrons sont utilisés pour définir des sections dynamiques qui doivent être transformées en variables. Les parenthèses dénotent une section optionnelle. Si vous ne souhaitez matcher que les URIs commençant par admin, vous pouvez utiliser:
+
+ Rouse::set('admin', 'admin(/(/(/)))');
+
+Et si vous voulez forcer l'utilisateur à spécifier un controleur:
+
+ Route::set('admin', 'admin/(/(/))');
+
+De plus Kohana 3.0 ne définit pas de routes par défaut. Si votre action (méthode) par défaut est 'index', alors vous devez le spécifier comme tel. Cela se fait via la méthode [Route::defaults]. Si vous voulez utiliser des expressions régulières pour des segments de votre URI alors il suffit de passer un tableau associatif `segment => regex` i.e.:
+
+ Route::set('reversed', '((/(/)))', array('id' => '[a-z_]+'))
+ ->defaults(array('controller' => 'posts', 'action' => 'index'))
+
+Cette route force la valeur de `id` à être en minuscule et composée uniquement de caractères alphabétiques et du caractère underscore.
+
+### Actions
+
+Une dernière chose importante à noter est que toute méthode accessible d'un controleur (càd via l'URI) sont appelées "actions", et sont préfixées de 'action_'. Dans l'exemple ci-dessus, `admin/posts/1/edit` appelle l'action `edit` mais la méthode rééllement apelée dans le controleur est `action_edit`. Pour plus d'informations voir [le tutorial sur les URLs](tutorials.urls).
+
+## Sessions
+
+Les méthodes Session::set_flash(), Session::keep_flash() et Session::expire_flash() n'existent plus. A la place la méthode [Session::get_once] peut être utilisée.
+
+## Helper URL
+
+Seules des modifications mineures ont été apportées sur l'helper URL. `url::redirect()` est désormais fait via `$this->request->redirect()` dans les controleurs et via `Request::instance()->redirect()` ailleurs.
+
+`url::current` a été remplacé par `$this->request->uri()`.
+
+## Validation
+
+La syntaxe a subit quelque modifications. Pour valider un tableau il faut maintenant faire:
+
+ $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')
+ ));
+
+La règle 'required' a été renommée en 'not_empty' pour plus de clarté.
+
+## Librairie View
+
+En 2.x, les vues sont rendues dans la portée d'un controleur, vous permettant ainsi d'utiliser `$this` dans la vue comme référence vers le controleur.
+En 3.0 les vues sont rendues sans aucune portée. Si vous souhaitez utiliser `$this` dans vos vues alors vous devez l'affecter par référence avec [View::bind]:
+
+ $view->bind('this', $this)
+
+Néanmoins c'est une mauvaise pratique car cela couple votre vue avec le controleur limitant ainsi la réutilisation du code. Il est vivement recommandé de ne passer que les variables requises par la vue:
+
+ $view = View::factory('my/view');
+
+ $view->variable = $this->property;
+
+ // ou par chainage
+
+ $view
+ ->set('variable', $this->property)
+ ->set('another_variable', 42);
+
+ // NON Recommandé
+ $view->bind('this', $this);
+
+Etant donné qu'une vue n'a pas de portée, la méthode `Controller::_kohana_load_view` est redondante. Si vous avez besoin de modifier la vue avant qu'elle ne soit rendue (par exemple pour ajouter un menu global à toutes vos pages) vous pouvez utiliser [Controller::after].
+
+ Class Controller_Hello extends Controller_Template
+ {
+ function after()
+ {
+ $this->template->menu = '...';
+
+ return parent::after();
+ }
+ }
diff --git a/includes/kohana/modules/userguide/guide/fr-fr/debugging.code.md b/includes/kohana/modules/userguide/guide/fr-fr/debugging.code.md
new file mode 100644
index 00000000..7b7b92cd
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/fr-fr/debugging.code.md
@@ -0,0 +1,24 @@
+# Debugging du code
+
+Kohana fournit plusieurs outils puissants pour debugger vos applications.
+
+Le plus basique d'entre eux est [Kohana::debug]. Cette méthode permet d'afficher toutes variables à la manière de [var_export] ou [print_r], mais en utilisant HTML pour ajouter du formatage supplémentaire.
+
+~~~
+// Affiche le contenu (dump) des variables $foo et $bar
+echo Kohana::debug($foo, $bar);
+~~~
+
+Kohana fournit aussi une méthode pour afficher le code source d'un fichier en particulier en appelant [Kohana::debug_source].
+
+~~~
+// Affiche cette ligne de code source
+echo Kohana::debug_source(__FILE__, __LINE__);
+~~~
+
+Enfin si vous voulez afficher des informations sur les chemins de votre application sans afficher/exposer le chemin d'installation vous pouvez utiliser [Kohana::debug_path]:
+
+~~~
+// Affiche "APPPATH/cache" plutot que le chemin réél
+echo Kohana::debug_file(APPPATH.'cache');
+~~~
diff --git a/includes/kohana/modules/userguide/guide/fr-fr/debugging.errors.md b/includes/kohana/modules/userguide/guide/fr-fr/debugging.errors.md
new file mode 100644
index 00000000..b4f9b508
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/fr-fr/debugging.errors.md
@@ -0,0 +1,24 @@
+# Gestion des Erreurs/Exceptions
+
+Kohana fournit des mécanismes de gestion des exceptions et d'erreurs qui transforment les erreurs en exceptions en utilisant les classes PHP prévues à cet effet [ErrorException](http://php.net/errorexception). De nombreux détails sur l'application ainsi que son état sont affichées :
+
+1. Classe de l'Exception
+2. Niveau de l'erreur
+3. Message de l'erreur
+4. Source de l'erreur, avec la ligne contenant l'erreur surlignée
+5. Une [trace de debug](http://php.net/debug_backtrace) du processus d'exécution
+6. Les fichiers inclus, les extensions chargées et les variables globales
+
+## Exemple
+
+Cliquez sur l'un des liens ci-dessous pour afficher/masquer des informations additionnelles:
+
+
{{userguide/examples/error}}
+
+## Désactiver le support des Exceptions
+
+Si vous ne voulez pas utiliser le mécanisme interne de gestion des exceptions et des erreurs, vous pouvez le désactiver via [Kohana::init]:
+
+~~~
+Kohana::init(array('errors' => FALSE));
+~~~
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/fr-fr/debugging.profiling.md b/includes/kohana/modules/userguide/guide/fr-fr/debugging.profiling.md
new file mode 100644
index 00000000..4dea4b6e
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/fr-fr/debugging.profiling.md
@@ -0,0 +1,22 @@
+# Profiling
+
+Kohana fournit de façon très facile les statistiques de vos applications:
+
+1. Appels de méthodes [Kohana] communes
+2. Requêtes URI
+3. Requêtes de [base de données](tutorials.databases)
+4. Temps moyen d'execution de votre application
+
+## Affichage/Récupération des statistiques
+
+Vous pouvez afficher ou récupérer les statistiques courantes à tout moment en faisant:
+
+~~~
+
+
+
+~~~
+
+## Exemple
+
+{{profiler/stats}}
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/fr-fr/features.md b/includes/kohana/modules/userguide/guide/fr-fr/features.md
new file mode 100644
index 00000000..068341da
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/fr-fr/features.md
@@ -0,0 +1 @@
+Cette page liste les fonctionnalités clés de Kohana v3.
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/fr-fr/menu.md b/includes/kohana/modules/userguide/guide/fr-fr/menu.md
new file mode 100644
index 00000000..2c8bca8a
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/fr-fr/menu.md
@@ -0,0 +1,26 @@
+1. **Bien débuter**
+ - [Qu'est-ce que Kohana?](about.kohana)
+ - [Conventions et style](about.conventions)
+ - [Installation](about.install)
+ - [Mise à jour depuis 2.x](about.upgrading)
+ - [Configuration](about.configuration)
+ - [Modèle Vue Controleur](about.mvc)
+ - [Arborescence de fichier](about.filesystem)
+ - [Auto-chargement](about.autoloading)
+ - [Enchainement des Requetes](about.flow)
+ - [Explorateur API](api)
+2. **Tutoriaux**
+ - [Hello, World](tutorials.helloworld)
+ - [Routes, URLs, et Liens](tutorials.urls)
+ - [Base de données](tutorials.databases)
+ - [ORM](tutorials.orm)
+ - [Travailler avec Git](tutorials.git)
+3. **Securité**
+ - [XSS](security.xss)
+ - [Validation](security.validation)
+ - [Cookies](security.cookies)
+ - [Base de données](security.database)
+4. **Debugging**
+ - [Code](debugging.code)
+ - [Gestion des erreurs](debugging.errors)
+ - [Profiling](debugging.profiling)
diff --git a/includes/kohana/modules/userguide/guide/fr-fr/security.cookies.md b/includes/kohana/modules/userguide/guide/fr-fr/security.cookies.md
new file mode 100644
index 00000000..cd999df1
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/fr-fr/security.cookies.md
@@ -0,0 +1,3 @@
+# Sécurité des Cookies
+
+[!!] stub
diff --git a/includes/kohana/modules/userguide/guide/fr-fr/security.database.md b/includes/kohana/modules/userguide/guide/fr-fr/security.database.md
new file mode 100644
index 00000000..9046f266
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/fr-fr/security.database.md
@@ -0,0 +1,3 @@
+# Sécurité de la Base de données
+
+[!!] stub
diff --git a/includes/kohana/modules/userguide/guide/fr-fr/security.validation.md b/includes/kohana/modules/userguide/guide/fr-fr/security.validation.md
new file mode 100644
index 00000000..9a5b3f97
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/fr-fr/security.validation.md
@@ -0,0 +1,241 @@
+# Validation
+
+La validation peut être effectuée sur tous les tableaux en utilisant la classe [Validate]. Les labels, filtres, règles et callbacks peuvent être attachés à un objet Validate via un tableau de clé, appellées "champs" (field name).
+
+labels
+: Un label est la version lisible (par un humain) d'un nom de champ.
+
+filters
+: Un filtre modifie la valeur d'un champs avant que les règles et callbacks ne soient exécutées.
+
+rules
+: Une règle est une vérification sur un champ qui retourne `TRUE` ou `FALSE`. Si une règle retourne `FALSE`, une erreur sera ajoutée à ce champ.
+
+callbacks
+: Une callback est une méthode spécifique ayant accès à l'ensemble de l'objet Validate.
+ La valeur retournée par une callback est ignorée. A la place, en cas d'erreur une callback doit manuellement ajouter une erreur à un champ en utilisant [Validate::error].
+
+[!!] A noter que les callbacks [Validate] et les [callbacks PHP](http://php.net/manual/language.pseudo-types.php#language.types.callback) ne sont pas pareils.
+
+Utiliser `TRUE` comment nom de champ lors de l'ajout d'un filtre, règle ou callback a pour effet de l'appliquer à tous les champs.
+
+**L'objet [Validate] supprimera tous les champs du tableau qui n'ont pas explicitement été utilisés via un label, un filtre, une règle ou une callback. Ceci pour empêcher tout accès à un champ qui n'a pas été validé et ajouter ainsi une protection de sécurité supplémentaire.**
+
+La création d'un objet de validation est faite en utilsiant la méthode [Validate::factory]:
+
+ $post = Validate::factory($_POST);
+
+[!!] L'objet `$post` sera utilisé pour le reste de ce tutorial dans lequel sera illustré la validation de l'inscription d'un nouveal utilisateur.
+
+### Règles par défaut
+
+La validation supporte les règles par défaut suivantes:
+
+Nom de la règle | Description
+------------------------- |-------------------------------------------------
+[Validate::not_empty] | La valeur ne doit pas être vide
+[Validate::regex] | La valeur respecte l'expression réguliére spécifiée
+[Validate::min_length] | La valeur respecte un nombre minimum de caractères
+[Validate::max_length] | La valeur respecte un nombre maximum de caractères
+[Validate::exact_length] | La valeur fait exactement le nombre de caractéres spécifiés
+[Validate::email] | La valeur doit respecter un format d'email
+[Validate::email_domain] | Le domaine de l'email existe
+[Validate::url] | La valeur entrée doit respecter un format d'URL
+[Validate::ip] | La valeur entrée doit respecter un format d'adresse IP
+[Validate::phone] | La valeur entrée doit respecter un format d'un uméro de téléphone
+[Validate::credit_card] | La valeur entrée doit respecter un format de numéro de carte de crédit
+[Validate::date] | La valeur entrée doit respecter un format de date (et heure)
+[Validate::alpha] | Seuls les caractères alphabétiques sont autorisés
+[Validate::alpha_dash] | Seuls les caractères alphabétiques et le caractère tiret '-' sont autorisés
+[Validate::alpha_numeric] | Seuls les caractères alphabétiques et numériques sont autorisés
+[Validate::digit] | La valeur doit être un chiffre
+[Validate::decimal] | La valeur doit être décimale ou flottante
+[Validate::numeric] | Seuls les caractères numériques sont autorisés
+[Validate::range] | La valeur doit être dans l'intervalle spécifié
+[Validate::color] | La valeur entrée doit respecter un format de couleur hexadécimal
+[Validate::matches] | La valeur doit correspondre à la valeur d'un autre champ
+
+[!!] Toute méthode existante de la classe [Validate] peut être utilisée directement sans utiliser une déclaration de callback compléte. Par exemple, ajouter la règle `'not_empty'` est la même chose que `array('Validate', 'not_empty')`.
+
+## Ajouter des filtres
+
+Tous les filtres de validation sont définis par un nom de champ, une méthode ou une fonction (en utilisant la syntaxe des [callbacks PHP](http://php.net/manual/language.pseudo-types.php#language.types.callback)), ainsi q'un tableau de paramètres:
+
+ $object->filter($field, $callback, $parameter);
+
+Les filtres modifient la valeur d'un filtre avant leur vérification par les règles et callbacks définies.
+
+Par exemple pour convertir un nom d'utilisateur en minuscule, alors il suffit d'écrire:
+
+ $post->filter('username', 'strtolower');
+
+Autre exemple, si l'on souhaite enlever les caratères vides au début et en fin de chaine de tous les champs, alors il faut écrire:
+
+ $post->filter(TRUE, 'trim');
+
+## Ajouter des règles
+
+Toutes les règles de validation sont définies par un nom de champ, une méthode ou une fonction (en utilisant la syntaxe des [callbacks PHP](http://php.net/manual/language.pseudo-types.php#language.types.callback)), ainsi q'un tableau de paramètres:
+
+ $object->rule($field, $callback, $parameter);
+
+Pour commencer notre exemple, nous allons commencer par valider le tableau `$_POST` contenant des informations d'inscription d'un utilisateur.
+
+Pour cela nous avons besoin de traiter les informations POSTées en utilisant [Validate]. Commencons par ajouter quelque régles:
+
+ $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');
+
+Toute fonction PHP existante peut aussi être utilisée comme une règle. Par exemple si l'on souhaite vérifier que l'utilisateur a entré une valeur correcte pour une question, on peut écrire:
+
+ $post->rule('use_ssl', 'in_array', array(array('yes', 'no')));
+
+A noter que les tableaux de paramètres doivent quand même être insérés dans un tableau! Si vous ne mettez pas ce tableau, `in_array` serait appelée via `in_array($value, 'yes', 'no')`, ce qui aboutirait à une erreur PHP.
+
+Toute régle spécifique peut être ajoutée en utilisant une [callback PHP](http://php.net/manual/language.pseudo-types.php#language.types.callback):
+
+ $post->rule('username', array($model, 'unique_username'));
+
+La méthode `$model->unique_username()` ressemblerait alors à:
+
+ public function unique_username($username)
+ {
+ // Vérifie si le nom d'utilisateur existe déjà dans la base de données
+ return ! DB::select(array(DB::expr('COUNT(username)'), 'total'))
+ ->from('users')
+ ->where('username', '=', $username)
+ ->execute()
+ ->get('total');
+ }
+
+[!!] Vous pouvez définir vos propres régles pour faire des vérifications additionnelles. Ces régles peuvent être réutilisés à plusieurs fins. Ces méthodes vont presque toujours exister au sein d'un modèle mais peuvent être définies dans nimporte quelle classe.
+
+## Ajouter des callbacks
+
+Toutes les callbacks de validation sont définies par un nom de champ et une méthode ou une fonction (en utilisant la syntaxe des [callbacks PHP](http://php.net/manual/language.pseudo-types.php#language.types.callback)):
+
+ $object->callback($field, $callback);
+
+[!!] Contrairement aux filtres et aux régles, aucun paramètre n'est passé à une callback.
+
+Le mot de passe utilisateur doit être hashé parès validaiton, nous allons donc le faire avec une callback:
+
+ $post->callback('password', array($model, 'hash_password'));
+
+Cela implique la création de la méthode `$model->hash_password()` de la manière suivante:
+
+ public function hash_password(Validate $array, $field)
+ {
+ if ($array[$field])
+ {
+ // Hasher le mot de passe s'il existe
+ $array[$field] = sha1($array[$field]);
+ }
+ }
+
+# Un exemple complet
+
+TOut d'abord nous avons besoin d'une [Vue] contenant le formulaire HTML que l'on placera dans `application/views/user/register.php`:
+
+
+
+
Des erreurs ont été trouvées, veuillez vérifier les informations entrées.
+
+
+
+
+
+
+
+
+
+
+
+
+
Le mot de passe doit contenir au moins 6 caractères.
+
+
+
+
+
'Toujours', 'no' => 'Seulement si nécessaire'), $post['use_ssl']) ?>
+
Pour des raisons de sécurité, SSL est toujours utilisé pour les paiements.
+
+
+
+
+
+[!!] Cette exemple utilise le helper [Form]. L'utiliser au lieu d'écrire du code HTML vous assure que tous les objets du formulaire vont traiter correctement les caractères HTML. Si vous souhaitez écrire le code HTML directement, veillez à utiliser [HTML::chars] pour filtrer/échaper les informations entrées par les utilisateurs.
+
+Ensuite nous avons besoin d'un controleur et d'une action pour traiter l'inscription des utilisaeurs, fichier qu'on placera dans `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())
+ {
+ // Les données ont éta validées, on inscrit l'utilisateur
+ $user->register($post);
+
+ // Toujours rediriger l'utilisateur après une validation de formulaire réussie afin de ne pas avoir les avertissement de rafraichissement
+ $this->request->redirect('user/profile');
+ }
+
+ // La validation a échoué, récupérons les erreurs
+ $errors = $post->errors('user');
+
+ // Affiche le formulaire d'inscription
+ $this->request->response = View::factory('user/register')
+ ->bind('post', $post)
+ ->bind('errors', $errors);
+ }
+
+ }
+
+Nous avons aussi besoin d'un modèle qui sera placé dans `application/classes/model/user.php`:
+
+ class Model_User extends Model {
+
+ public function register($array)
+ {
+ // Créé un nouvel utilisateur dans la base de données
+ $id = DB::insert(array_keys($array))
+ ->values($array)
+ ->execute();
+
+ // Sauvegarde l'identifiant de l'utilisateur dans un cookie
+ cookie::set('user', $id);
+
+ return $id;
+ }
+
+ }
+
+C'est tout! Nous avons désormais un formulaire d'inscritpion opérationnel et qui vérifie les informations entrées.
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/fr-fr/security.xss.md b/includes/kohana/modules/userguide/guide/fr-fr/security.xss.md
new file mode 100644
index 00000000..374f8fbe
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/fr-fr/security.xss.md
@@ -0,0 +1,17 @@
+# Cross-Site Scripting (XSS)
+
+La première étape pour se prémunir des attaques de type [XSS](http://wikipedia.org/wiki/Cross-Site_Scripting) est de savoir quand il faut le faire. Les attaques XSS ne peuvent être déclenchées que lors de l'affichage de contenu HTML au travers de formulaires ou de données issues de la base de données. Toute variable globale contenant des informations clientes peut être un vecteur d'attaques XSS. Cela inclut les données `$_GET`, `$_POST`, et `$_COOKIE`.
+
+## Prévention
+
+Il existe des règles simples à suivre pour prémunir vos applications de ces attaques.
+
+La première est d'utiliser systématiquement la méthode [Security::xss] pour nettoyer des données d'une variable globale. De plus si vous ne souhaitez pas avoir de HTML dans vos variables, utilisez la méthode [strip_tags](http://php.net/strip_tags) pour supprimer les balises HTML.
+
+[!!] Si vous autorisez les utilisateurs à entrer des données HTML dans votre application, il est vivement recommandé d'utiliser une librairie de nettoyage HTML comme [HTML Purifier](http://htmlpurifier.org/) ou [HTML Tidy](http://php.net/tidy).
+
+La seconde est de toujours échapper les données insérées dans vos pages HTML. La classe [HTML] fournit des générateurs pour de nombreuses balises HTML, incluant scripts et feuilles de style, liens, ancres, images et email. Tout contenu sans confiance doit être échappé avec [HTML::chars].
+
+## Références
+
+* [OWASP XSS Cheat Sheet](http://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet)
diff --git a/includes/kohana/modules/userguide/guide/fr-fr/tutorials.databases.md b/includes/kohana/modules/userguide/guide/fr-fr/tutorials.databases.md
new file mode 100644
index 00000000..f21aefe9
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/fr-fr/tutorials.databases.md
@@ -0,0 +1,242 @@
+# Base de données {#top}
+
+Kohana 3.0 intégre un robuste module permettant de travailler avec les base de données. Par défauut, le module supporte [MySQL](http://php.net/mysql) et [PDO](http://php.net/pdo).
+
+Le module base de données est inclus par défaut dans votre installation de Kohana 3.0 mais n'est pas activé. Pour l'activer, éditez le fichier `application/bootstrap.php` et modifiez l'appel à [Kohana::modules] pour y inclure le module base de données:
+
+ Kohana::modules(array(
+ ...
+ 'database' => MODPATH.'database',
+ ...
+ ));
+
+## Configuration {#configuration}
+
+Aprés activation du module, il vous faut préciser les paramètres de configuration permettant à votre application de se connecter à la base de données. Un exemple de fichier de configuration peut être trouvé sous `modules/database/config/database.php`.
+
+La structure d'un groupe de configuration pour une base de données, appelé instance, est de cette forme:
+
+ 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,
+ ),
+
+[!!] Plusieurs instances différentes de ces configurations peuvent être définies dans le fichier de configuration.
+
+La compréhension de l'ensemble de ces paramètres est importante:
+
+INSTANCE_NAME
+: nom personnalisé de l'instance. Il est obligatoire d'avoir au moins une instance appelée "default".
+
+DATABASE_TYPE
+: type de base de données. Valeurs acceptées: "mysql" et "pdo".
+
+CONNECTION_ARRAY
+: options de connection spécifiques au type de base de données choisis. Ces options sont explicités [plus bas](#connection_settings).
+
+TABLE_PREFIX
+: prefixe qui sera ajouté à tous les noms de table par le [constructeur de requêtes](#query_building).
+
+QUERY_PROFILING
+: activer le [profiling](debugging.profiling) des requêtes.
+
+### Exemple
+
+L'exemple ci-dessous est composé de 2 connections MySQL, la première locale et l'autre distante:
+
+ 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,
+ ),
+ );
+
+### Options de connection {#connection_settings}
+
+Chacun des types de base de données possède des options différentes de connection.
+
+#### MySQL
+
+Les options de connection MySQL sont les suivantes:
+
+Type | Option | Description | Valeur par défaut
+----------|------------|----------------------------|--------------------------
+`string` | hostname | Hôte hébergeant la base | `localhost`
+`integer` | port | Numéro de port | `NULL`
+`string` | socket | Socket UNIX | `NULL`
+`string` | username | Utilisateur | `NULL`
+`string` | password | Mot de passe | `NULL`
+`boolean` | persistent | Connections persistantes | `FALSE`
+`string` | database | Nom de base de la base | `kohana`
+
+#### PDO
+
+Les options de connection PDO sont les suivantes:
+
+Type | Option | Description | Valeur par défaut
+----------|------------|----------------------------|--------------------------
+`string` | dsn | Source PDO | `localhost`
+`string` | username | Utilisateur | `NULL`
+`string` | password | Mot de passe | `NULL`
+`boolean` | persistent | Connections persistantes | `FALSE`
+
+!! Si vous utilisez PDO et n'êtes pas sûr de la valeur du `dsn`, veuillez consulter [PDO::__construct](http://php.net/pdo.construct).
+
+## Connections et Instances {#connections}
+
+Chaque groupe de configuration est accessible en tant qu'instance de base de données. On accède à une instance en appelant [Database::instance]:
+
+ $default = Database::instance();
+ $remote = Database::instance('remote');
+
+Pour se déconnecter de la base de données, il suffit de détruire l'objet correspondant:
+
+ unset($default, Database::$instances['default']);
+
+Si vous souhaitez déconnecter l'ensemble des instances d'un coup alors écrivez:
+
+ Database::$instances = array();
+
+## Ecrire des requêtes {#making_queries}
+
+Il existe 2 manières d'écrire des requêtes dans Kohana. La manière la plus simple est d'utiliser le [constructeur de requête](Query_Builder), via [DB::query]. Ces requêtes sont appelées des "requêtes préparées" ou prepared statements et permettent l'échappement automatique des paramètres de la requête.
+
+La seconde manière est d'appeler directement les méthodes voulues.
+
+[!!] Toutes les requêtes sont executées via la méthode `execute`, qui prend en paramètre un objet base de données ou un nom d'instance. Pour plus d'informations, consultez [Database_Query::execute].
+
+### Requêtes préparées
+
+L'utilisation de requêtes préparées permet d'écrire des requetes SQL manuellement tout en échappant les paramètres de la requête automatiquement permettant ainsi de se prémunir contre les [injections SQL](http://wikipedia.org/wiki/SQL_Injection). La création d'une requête est simple:
+
+ $query = DB::query(Database::SELECT, 'SELECT * FROM users WHERE username = :user');
+
+La méthode [DB::query] créé un objet [Database_Query] et permet un chainage des méthodes. La requête contient un paramètre `:user` que l'on peut assigner comme suit:
+
+ $query->param(':user', 'john');
+
+[!!] Les noms de paramètre peuvent être nimporte quelle chaine de caractères puisqu'elles sont remplacées en utilisant la fonction [strtr](http://php.net/strtr). Il est vivement recommandé de ne **pas** utiliser de signe dollars ($) pour éviter toute confusion.
+
+Si vous souhaitez afficher la requête SQL qui va être exécutée, il vous suffit de caster l'objet en chaine de caractères comme suit:
+
+ echo Kohana::debug((string) $query);
+ // Affichera:
+ // SELECT * FROM users WHERE username = 'john'
+
+Vous pouvez aussi ré-assigner `:user` ultérieurement en appelant [Database_Query::param]:
+
+ $query->param(':user', $_GET['search']);
+
+[!!] Pour assigner plusieurs paramètres à la fois, vous pouvez utiliser [Database_Query::parameters].
+
+Une fois chacuns des paramètres de votre requête assignés, l'exécution de la requête se fait via:
+
+ $query->execute();
+
+Enfin, il est aussi possible d'assigner un paramètre à une [variable passée par référence](http://php.net/language.references.whatdo). Cela peut s'avérer très utile lors de l'exécution de la même requête plusieurs fois avec des paramètres différents:
+
+ $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();
+ }
+
+Dans l'exemple ci-dessus, les variables `$username` and `$password` sont changées à chacune des itérations de la boucle `foreach`. Cela s'avére très puissant et peut vous permettre d'alléger votre code.
+
+### Construction de requêtes {#query_building}
+
+La création dynamique de requêtes en utilisant des objets et des méthodes de classe permet de créér des requêtes sans avoir de connaissances sur le langage SQL. Le constructeur se charge d'échapper les noms de table et colonnes mais aussi les valeurs des paramètres des requêtes.
+
+[!!] A ce jour, Kohana ne dispose pas de moyens de combiner les requêtes préparées et la construction dynamique de requêtes.
+
+#### SELECT
+
+Chaque type de requête en base de données est représenté par une classe, chacunes possédant ses propres méthodes. Par exemple, pour créér une requête SELECT, utilisez [DB::select]:
+
+ $query = DB::select()->from('users')->where('username', '=', 'john');
+
+Par défault, [DB::select] sélectionnera toutes les colonnes (`SELECT * ...`), mais vous pouvez aussi spécifier ces colonnes:
+
+ $query = DB::select('username', 'password')->from('users')->where('username', '=', 'john');
+
+L'exemple ci-dessus illustre aussi la puissance du chainage de méthodes qui permet en une seule ligne de spécifier les paramètres de sélection, la table et les critères de filtrage via la méthode `where`. De la même manière que précédemment, si vous souhaitez afficher la requête SQL qui va être exécutée, il vous suffit de caster l'objet en chaine de caractères comme suit:
+
+ echo Kohana::debug((string) $query);
+ // Affichera:
+ // SELECT `username`, `password` FROM `users` WHERE `username` = 'john'
+
+Notez que tout est échappé correctement et c'est là l'un des grands avantages de l'utilisation du constructeur de requêtes.
+
+La création d'alias `AS` se fait comme ci-dessous:
+
+ $query = DB::select(array('username', 'u'), array('password', 'p'))->from('users');
+ // Requête exécutée:
+ // SELECT `username` AS `u`, `password` AS `p` FROM `users`
+
+#### INSERT
+
+Pour insérer des enregistrements dans la base de données il faut utiliser [DB::insert]:
+
+ $query = DB::insert('users', array('username', 'password'))->values(array('fred', 'p@5sW0Rd'));
+ // Requête exécutée:
+ // INSERT INTO `users` (`username`, `password`) VALUES ('fred', 'p@5sW0Rd')
+
+#### UPDATE
+
+La modification d'un enregistrement en base se fait via [DB::update]:
+
+ $query = DB::update('users')->set(array('username' => 'jane'))->where('username', '=', 'john');
+ // Requête exécutée:
+ // UPDATE `users` SET `username` = 'jane' WHERE `username` = 'john'
+
+#### DELETE
+
+Pour supprimer un enregistrement, il faut utiliser [DB::delete]:
+
+ $query = DB::delete('users')->where('username', 'IN', array('john', 'jane'));
+ // Requête exécutée:
+ // DELETE FROM `users` WHERE `username` IN ('john', 'jane')
+
+#### Fonctions spécifiques {#database_functions}
+
+Il est commun d'utiliser des fonctions spécifiques de base de données telles que `COUNT`. Le constructeur de requête vous permet de les utiliser de 2 manières. La première est la suivante:
+
+ $query = DB::select(array('COUNT("username")', 'total_users'))->from('users');
+
+Ca ressemble beaucoup à l'aliasing `AS` mais notez que le nom de colonne est entouré de doubles quotes. A chaque fois qu'un nom de colonne est entouré de doubles quotes, alors **seules** les parties entourées seront échappées. Cette requête générerait le SQL suivant:
+
+ SELECT COUNT(`username`) AS `total_users` FROM `users`
+
+#### Expressions complexes
+
+De temps à autre on a besoin d'écrire des requêtes contenant des expressions complexes. Dans ce cas, cette expression sera créé via [DB::expr]. Une expression est prise telle quelle par la méthode et de ce fait aucun échappement n'est fait.
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/fr-fr/tutorials.git.md b/includes/kohana/modules/userguide/guide/fr-fr/tutorials.git.md
new file mode 100644
index 00000000..e8cac61f
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/fr-fr/tutorials.git.md
@@ -0,0 +1,117 @@
+# Travailler avec Git
+
+Kohana utilise [git](http://git-scm.com/) comme système de gestion de versions et [github](http://github.com/kohana) pour l'aspect collaboratif. Ce tutorial présente comment utiliser git et github pour mettre en place une application simple.
+
+## Structure initiale
+
+[!!] Ce tutorial prend comme prérequis le fait que votre serveur web est déjà mis en place et que vous être dans l'étape de création d'une nouvelle application située à .
+
+En utilisant votre console, placez vous dans le répertoire `gitorial` et exécutez `git init`. Cela créera la structure du dépôt git.
+
+Ensuite, nous allons créér un [sous-module](http://www.kernel.org/pub/software/scm/git/docs/git-submodule.html) pour le répertoire `system`. Allez à l'URL et copiez l'URL de clonage:
+
+
+
+Maintenant utilisez cette URL pour créér le sous-module `system`:
+
+~~~
+git submodule add git://github.com/kohana/core.git system
+~~~
+
+[!!] Cela créera un lien vers la version stable en développement. La version stable en développement est sûre à utiliser pour vos environnements de production et possède la même API que la version stable en téléchargement à laquelle sont ajoutés les correctifs de bugs.
+
+A partir de là vous pouvez ajouter les modules que vous souhiatez, par exemple le module [Base de données](http://github.com/kohana/database):
+
+~~~
+git submodule add git://github.com/kohana/database.git modules/database
+~~~
+
+Une fois les sous-modules ajoutés, vous devez les initialiser:
+
+~~~
+git submodule init
+~~~
+
+Enfin il faut les commiter:
+
+~~~
+git commit -m 'Added initial submodules'
+~~~
+
+L'étape suivante consiste en la création de la structure des répertoires de votre application kohana. Le minimum requis est:
+
+~~~
+mkdir -p application/classes/{controller,model}
+mkdir -p application/{config,views}
+mkdir -m 0777 -p application/{cache,logs}
+~~~
+
+Si vous lancez la commande linux `find application` vous devez voir:
+
+~~~
+application
+application/cache
+application/config
+application/classes
+application/classes/controller
+application/classes/model
+application/logs
+application/views
+~~~
+
+Puisque l'on ne souhaite pas que les changements sur les logs et les mises en cache soient pris en compte, il faut ajouter un fichier `.gitignore` à chacun de ces répertoires. Cela aura pour effet d'ignorer tous les fichiers non cachés du répertoire:
+
+~~~
+echo '[^.]*' > application/{logs,cache}/.gitignore
+~~~
+
+[!!] Git ignore les répertoires vides, donc le fait d'ajouter le fichier `.gitignore` vous assure que git prendra en compte le répertoire mais pas les fichiers qu'il contient.
+
+Ensuite il faut récupérer les fichiers `index.php` et `bootstrap.php`:
+
+~~~
+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
+~~~
+
+Commiter tous les changements:
+
+~~~
+git add application
+git commit -m 'Added initial directory structure'
+~~~
+
+C'est tout! Vous avez désormais une application gérée sous Git.
+
+## Mettre à jour les sous-modules
+
+Tôt ou tard vous allez sûrement avoir besoin de mettre à jour vos sous-modules. Pour mettre à jour l'ensemble de vos sous-modules à la version la plus récente `HEAD`, entrez:
+
+~~~
+git submodule foreach
+~~~
+
+Pour mettre à jour un seul sous-module, par exemple `system`, entrez:
+
+~~~
+cd system
+git checkout master
+git fetch
+git merge origin/master
+cd ..
+git add system
+git commit -m 'Updated system to latest version'
+~~~
+
+Enfin si vous souhaitez mettre à jour un sous-module par rapport à une révision particulière, entrez:
+
+~~~
+cd modules/database
+git fetch
+git checkout fbfdea919028b951c23c3d99d2bc1f5bbeda0c0b
+cd ../..
+git add database
+git commit -m 'Updated database module'
+~~~
+
+
diff --git a/includes/kohana/modules/userguide/guide/fr-fr/tutorials.helloworld.md b/includes/kohana/modules/userguide/guide/fr-fr/tutorials.helloworld.md
new file mode 100644
index 00000000..c4360b39
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/fr-fr/tutorials.helloworld.md
@@ -0,0 +1,103 @@
+# Hello, World
+
+Tout framework digne de ce nom possède un exemple d'application "Hello World", alors ne dérogeons pas à la régle!
+
+On commencera donc par décrire un "hello word" très très basique puis on détaillera les principes MVC appliqués à l'exemple.
+
+## Au commencement il n'y avait rien...
+
+La première chose à faire est de créer un controleur de telle sorte que Kohana puisse traiter une requête.
+
+Créér le fichier `application/classes/controller/hello.php` dans votre répertoire application et ajoutez-y le code suivant:
+
+ template->message = 'hello, world!';
+ }
+ }
+
+`extends Controller_Template`
+: nous héritons désormais du controleur template qui rend plus facile l'utilisation de vues au sein d'un controleur.
+
+`public $template = 'site';`
+: le controleur template doit connaitre le template que vous souhaitez utiliser. Il chargera alors automatiquement la vue en question et lui assignera l'objet Vue créé.
+
+`$this->template->message = 'hello, world!';`
+: `$this->template` est une référence vers l'objet Vue du template de notre site. Ce que l'on fait ici est assigner à la vue la variable "message" dont la valeur est "hello, world!".
+
+Maintenant actualisez votre navigateur...
+
+
{{userguide/examples/hello_world_error}}
+
+Kohana vous affiche une erreur au lieu du message fascinant qu'il devrait afficher. En regardant de plus près le message d'erreur on peut voir que la librairie View n'a pas été capable de trouver notre template, probablement parceque nous ne l'avons pas encore créé!
+
+Créons donc notre vue en créant le fichier `application/views/site.php` avec le texte suivant:
+
+
+
+ We've got a message for you!
+
+
+
+
+
We just wanted to say it! :)
+
+
+
+Maintenant si vous ré-actualisez, vous devriez voir apparaitre ce qu'il faut:
+
+
+
+## A moi la gloire et l'argent!
+
+Dans ce tutorial on a abordé comment créer un controleur et utiliser une vue pour séparer la logique de la présentation.
+
+Evidemment l'exemple choisi est une introduction basique à Kohana et n'effleure même pas les possibilités infinies de Kohana ;).
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/fr-fr/tutorials.orm.md b/includes/kohana/modules/userguide/guide/fr-fr/tutorials.orm.md
new file mode 100644
index 00000000..ee7d9bbf
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/fr-fr/tutorials.orm.md
@@ -0,0 +1,121 @@
+# ORM {#top}
+
+Kohana 3.0 inclus un module [ORM](http://en.wikipedia.org/wiki/Object-relational_mapping) puissant utilisant le pattern active record et l'introspection de base de données pour déterminer les informations sur les colonnes d'un modèle.
+
+Bien que le module ORM soit inclus par défaut dans vos installations de Kohana 3.0, il est désactivé par défaut. Pour l'activer modifiez le fichier `application/bootstrap.php` et ajoutez à l'appel [Kohana::modules] le module ORM:
+
+ Kohana::modules(array(
+ ...
+ 'orm' => MODPATH.'orm',
+ ...
+ ));
+
+## Configuration {#configuration}
+
+Pour pouvoir utiliser l'ORM, il faut tout d'abord faire hériter vos modèles de la classe ORM comme suit:
+
+ class Model_User extends ORM
+ {
+ ...
+ }
+
+Dans l'exemple ci-dessus, le modele cherchera une table `users` dans la base de données par défaut.
+
+### Propriétés d'un modèle ORM
+
+Les propriétés suivantes peuvent être utilisées pour configurer chacuns de vos modèles:
+
+Type | Option | Description | Valeur par défaut
+----------|-----------------|--------------------------------------| -------------------------
+`string` | _table_name | Nom de la table |
+`string` | _db | Nom de la base de données |`default`
+`string` | _primary_key | Colonne contenant la clé primaire |`id`
+`string` | _primary_val | Colonne contenant la valeur primaire |`name`
+
+## Utiliser l'ORM
+
+### Charger un enregistrement
+
+Pour créér une instance d'un modèle, il faut utiliser la méthode [ORM::factory] ou passer directement par le constructeur:
+
+ $user = ORM::factory('user');
+ // ou
+ $user = new Model_User();
+
+Les 2 méthodes ci-dessus peuvent prendre en argument la valeur de la clé primaire de l'élément que l'on souhaite charger:
+
+ // Charge l'utilisateur d'ID 5
+ $user = ORM::factory('user', 5);
+
+[ORM::loaded] permet de savoir si un modèle donné a été chargé avec succès.
+
+### Rechercher un enregistrement
+
+L'ORM supporte la plupart des méthodes [Base de données](tutorials.databases) pour affiner les recherches sur les données du modèle. Pour avoir une liste complète, référez vous à la propriété `_db_methods`. Les enregistrements sont récupérés lors de l'appel à [ORM::find] ou [ORM::find_all].
+
+ // Le code ci-dessous récupère le premier utilisateur actif prénommé Bob
+ $user = ORM::factory('user')
+ ->where('active', '=', TRUE)
+ ->where('name', '=', 'Bob')
+ ->find();
+
+ // Le code ci-dessous récupère tous les utilisateur actifs Bob
+ $users = ORM::factory('user')
+ ...
+ ->find_all();
+
+Lors de la récupération d'une liste de modèles par la méthode [ORM::find_all], le parcours des éléments se fait comme pour les résultats de base de données:
+
+ foreach ($users as $user)
+ {
+ ...
+ }
+
+### Accès aux propriétés d'un Modèle
+
+Toutes les propriétés d'un modèle sont accessibles en utilisant les méthodes magiques `__get` et `__set`.
+
+ $user = ORM::factory('user', 5);
+
+ // Affiche le nom de l'utilisateur
+ echo $user->name;
+
+ // Change le nom de l'utilisateur
+ $user->name = 'Bob';
+
+Pour stocker des informations/propriétés qui n'ont pas de correspondances dans la table (c'est-à-dire aucune colonnes du même nom que la propriété), il faut utiliser la propriété `_ignored_columns`:
+
+ class Model_User extends ORM
+ {
+ ...
+ protected $_ignored_columns = array('field1', 'field2', ...)
+ ...
+ }
+
+### Créér et sauvegarder des enregistrements
+
+La méthode [ORM::save] est utilisée aussi bien pour créer et sauvegarder de nouveaux enregistrements que pour mettre à jour des enregistrements existants.
+
+ // Création d'un enregistrement
+ $user = ORM::factory('user');
+ $user->name = 'New user';
+ $user->save();
+
+ // Mise à jour d'un enregistrement
+ $user = ORM::factory('user', 5);
+ $user->name = 'User 2';
+ $user->save();
+
+Vous pouvez mettre à jour plusieurs enregistrements à la fois en utilisant la méthode [ORM::save_all]:
+
+ $user = ORM::factory('user');
+ $user->name = 'Bob';
+
+ // Change le nom de tous les utilisateurs actifs à Bob
+ $user->where('active', '=', TRUE)->save_all();
+
+[ORM::saved] permet de vérifier si le modèle a bien été sauvegardé.
+
+### Supprimer des enregistrements
+
+La suppression d'enregistrements se fait avec [ORM::delete] et [ORM::delet_all]. Ces méthodes fonctionnement de manière similaire à celles de sauvegarde détaillées plus haut à l'exception du fait que [ORM::delete] peut prendre en argument l'`id` de l'enregistrment à supprimer.
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/fr-fr/tutorials.urls.md b/includes/kohana/modules/userguide/guide/fr-fr/tutorials.urls.md
new file mode 100644
index 00000000..911a21a4
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/fr-fr/tutorials.urls.md
@@ -0,0 +1,163 @@
+# Routes, URLs et Liens
+
+Ce chapitre fournit les bases permettant de comprendre la logique de traitement des requêtes, de la génération des URLs et des liens.
+
+## Routage
+
+Comment évoqué dans le chapitre [processus de traitement des requêtes](about.flow), une requête est traitée par la classe [Request] qui tente de trouver une [Route] correspondante et charge les méthodes appropriées du controleur qui permettront de traiter la requete.
+
+Si vous regardez le fichier `APPPATH/bootstrap.php` vous pouvez voir le code ci-dessous qui est exécuté juste avant que la requête ne soit traitée par [Request::instance]:
+
+ Route::set('default', '((/(/)))')
+ ->defaults(array(
+ 'controller' => 'welcome',
+ 'action' => 'index',
+ ));
+
+Ce code crée une route appelée `default` dont l'URI doit avoir le format `((/(/)))`. Les éléments entourés par `<>` sont des *clés* et ceux entourés par `()` définissent les parties *optionnelles* de l'URI. Dans le code ci-dessus , l'URI entière est optionnelles ce qui signifie que même une URI vide serait traitée en utilisant les valeurs par défaut spécifiées dans la route. Cela se traduirait par le chargement de la classe `Controller_Welcome` et l'exécution de sa méthode `action_index` pour traiter la requête.
+
+A noter que les routes de Kohana peuvent contenir tous caractères exceptés `()<>`. Dans la route ci-dessus le caractère `/` est utilisé comme séparateur mais tant que l'expression matche l'URI demandée il n'y a aucune restriction sur le format des routes.
+
+### Répertoires
+
+Par soucis d'organisation, il est commun de vouloir organiser certains de vos controleurs dans des sous-répertoires. Par exemple pour grouper votre section d'administration (tout vos controleurs d'administration) de votre site dans un sous-répertoire admin:
+
+ Route::set('admin', 'admin(/(/(/)))')
+ ->defaults(array(
+ 'directory' => 'admin',
+ 'controller' => 'home',
+ 'action' => 'index',
+ ));
+
+Cette route indique qu'il faut que l'URI commence obligatoirement par `admin` pour matcher. Le sous-répertoire est statiquement assigné à `admin` dans les paramètres par défaut. De cette manière, la requête `admin/users/create` chargera la classe `Controller_Admin_Users` et appellera la méthode `action_create`.
+
+### Expressions régulières
+
+Le système de routage de Kohana utilise des expressions régulière compatible Perl. Par défaut les clés (entourées par `<>`) sont matchées par l'expression `[a-zA-Z0-9_]++` mais vous pouvez définir vos propres expressions pour chacunes des clés en passant un tableau associatif de clés et d'expressions comme paramètre additionnel de la méthode [Route::set].
+
+Par exemple, imaginons qu'en plus d'une section administration, votre site contient une section blog dont les controleurs sont situés dans un sous-répertoire blog. Alors vous pouvez soit écrire 2 routes distinctes ou bien tout simplement faire:
+
+ Route::set('sections', '(/(/(/)))',
+ array(
+ 'directory' => '(admin|blog)'
+ ))
+ ->defaults(array(
+ 'controller' => 'home',
+ 'action' => 'index',
+ ));
+
+Cette route vous permet donc d'avoir 2 sections, 'admin' et 'blog' et d'organiser les controleurs dans des sous-répertoires distincts.
+
+### Exemples de routes
+
+Les possibilités sont bien sûres infinies, néanmoins voici quelques exemples courants:
+
+ /*
+ * Raccourcis d'authentification
+ */
+ Route::set('auth', '',
+ array(
+ 'action' => '(login|logout)'
+ ))
+ ->defaults(array(
+ 'controller' => 'auth'
+ ));
+
+ /*
+ * Feeds multi-formats
+ * 452346/comments.rss
+ * 5373.json
+ */
+ Route::set('feeds', '(/).',
+ array(
+ 'user_id' => '\d+',
+ 'format' => '(rss|atom|json)',
+ ))
+ ->defaults(array(
+ 'controller' => 'feeds',
+ 'action' => 'status',
+ ));
+
+ /*
+ * Pages statiques
+ */
+ Route::set('static', '.html',
+ array(
+ 'path' => '[a-zA-Z0-9_/]+',
+ ))
+ ->defaults(array(
+ 'controller' => 'static',
+ 'action' => 'index',
+ ));
+
+ /*
+ * Vous n'aimez pas les slashes?
+ * EditGallery:bahamas
+ * Watch:wakeboarding
+ */
+ Route::set('gallery', '():',
+ array(
+ 'controller' => '[A-Z][a-z]++',
+ 'action' => '[A-Z][a-z]++',
+ ))
+ ->defaults(array(
+ 'controller' => 'Slideshow',
+ ));
+
+ /*
+ * Recherche rapide
+ */
+ Route::set('search', ':', array('query' => '.*'))
+ ->defaults(array(
+ 'controller' => 'search',
+ 'action' => 'index',
+ ));
+
+Les Routes sont évaluées dans l'odre dans lequel elles sont définies. C'est pour cette raison que la route par défaut est définie à la fin de sorte que les routes spécifiques soient testées avant.
+
+De plus cela implique qu'il faut faire attention si vous définissez des routes après le chargement des modules, car les routes incluses dans ceux-ci pourrait entrer en conflit.
+
+### Paramétres des requêtes
+
+Le répertoire (directory), le controleur (controller) et l'action sont accessibles à travers l'instance [Request] d'une des 2 manières suivantes:
+
+ $this->request->action;
+ Request::instance()->action;
+
+Toutes les autres clés spécifiées dans vos routes sont accessibles en utilisant:
+
+ $this->request->param('key_name');
+
+La méthode [Request::param] peut prendre un second paramètre optionnel permettant de spécifier une valeur par défaut à retourner au cas où la clé n'est pas affectée par la route. Si aucun argument n'est passé, toutes les clés sont passés sous forme d'un tableau associatif.
+
+### Convention
+
+La convention est de placer toutes vos routes dans le fichier `MODPATH//init.php` si elles concernent un module et sinon, si elles sont spécifiques à l'application, il faut tout simplement les ajouter au fichier `APPPATH/bootstrap.php` au-dessus de la route par défaut. Bien sûr cela ne vous empêche pas de les inclure depuis un fichier externe ou de les générer dynamiquement.
+
+## URLs
+
+Outre les capacités puissantes de gestion des routes de Kohana, Kohana fournit aussi des méthodes de génération d'URLs pour vos routes. Vous pouvez bien sûr spécifier des URIs en utilisant [URL::site] pour créer une URL complète:
+
+ URL::site('admin/edit/user/'.$user_id);
+
+Cependant, Kohana fournit aussi une méthode permettant de générer les URIs à partir de la définition des routes que vous avez écrites. C'est extrêmement utile si vos routes sont amenées à changer car vous n'aurez pas à remodifier votre code partout où vous avez spécifié des URIs comme ci-dessus. Voici un exemple de génération dynamique qui correspond à la route `feeds` définie dans la liste d'exemples plus haut:
+
+ Route::get('feeds')->uri(array(
+ 'user_id' => $user_id,
+ 'action' => 'comments',
+ 'format' => 'rss'
+ ));
+
+Imaginez que plus tard vous décidez de changer la définition de la route en `feeds/(/).`. Avec le code ci-dessus l'URI générée est toujours valide après ce changement! Lorsqu'une partie de l'URI est entourée de paranthèses et qu'elle représente une clé qui n'est pas fournie dans la génération de l'URI et qui n'a pas de valeur par défaut alors cette partie est enlevée de l'URI. C'est le cas de la partie `(/)` de la route par défaut; elle ne sera pas incluse dans l'URI générée si l'id n'est pas fourni.
+
+Une autre méthode pratique est [Request::uri] qui fait la même chose que la précédente méthode excepté qu'elle utilise la route courante. Si la route courante est la route par défaut dont l'URI est `users/list`, il est possible d'écrire le code suivant pour générer des URIs au format `users/view/$id`:
+
+ $this->request->uri(array('action' => 'view', 'id' => $user_id));
+
+Au sein d'une vue il est préferrable d'utiliser:
+
+ Request::instance()->uri(array('action' => 'view', 'id' => $user_id));
+
+## Liens
+
+[!!] links stub
diff --git a/includes/kohana/modules/userguide/guide/he-il/about.autoloading.md b/includes/kohana/modules/userguide/guide/he-il/about.autoloading.md
new file mode 100644
index 00000000..46064bb2
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/he-il/about.autoloading.md
@@ -0,0 +1,18 @@
+# Autoloading - טעינה אוטומטית
+
+Kohana יודע לנצל את יכולת הטעינה אוטומטית של PHP [autoloading](http://php.net/manual/language.oop5.autoload.php).
+עובדה זו מבטלת את הצורך בשימוש ב [include](http://php.net/include) או [require](http://php.net/require) לפני השימוש בבקר.
+
+הבקרים (Classes) נטענים על ידי מטודת [Kohana::auto_load], אשר יודעת לעשות את ההמרה משם בקר לשם קובץ:
+
+1. בקרים צריכים להיות ממוקמים בתוך תקיית `classes/` השייכים ל [filesystem](about.filesystem)
+2. כל קו תחתי בשם הבקר יהפוך לסלאש '/' ויחפש בתת תקיות בהתאם
+3. שם הקובץ צריך להיות כתוב באותיות קטנות
+
+כאשר קוראים לבקר שלא נטען (לדוגמא: `Session_Cookie`) קוהנה תחפש בעזרת פקודת [Kohana::find_file] את הקובץ `classes/session/cookie.php`.
+
+## Custom Autoloaders - טעינה אוטומטית מותאמת אישית
+
+[!!] הגדרת ברירת המחדל של הטעינה האוטומטית נמצאת בקובץ `application/bootstrap.php`.
+
+בקרים נוספים ניתן להוסיף ע"י שימוש ב [spl_autoload_register](http://php.net/spl_autoload_register).
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/he-il/about.configuration.md b/includes/kohana/modules/userguide/guide/he-il/about.configuration.md
new file mode 100644
index 00000000..6db0d4ae
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/he-il/about.configuration.md
@@ -0,0 +1,99 @@
+# General Configuration - הגדרות כלליות
+
+[!!] Finish translating... (todo: description of benefits of static properties for configuration)
+
+## Core Configuration - הגדרות בסיסיות
+
+ההגדרה הראשונה אותה יש לבצע בכל התקנה של קוהנה היא שינוי ההגדרות של [Kohana::init] ב `application/bootstrap.php`.
+ההגדרות הן:
+
+שגיאות:
+האם להשתמש בטיפול שגיאות ויוצאי דופן פנימי של הקוהנה
+ערך ברירת מחדל - True, יש לשנות ל FLASE במידה ולא מעוניינים
+
+פרופיל:
+האם להשתמש בדף הפרופיל הסטטיסטי
+ערך ברירת מחדל - True
+יש לשנות ל FALSE במידה ולא מעוניינים - מומלץ שלא להשתמש באפשרות זו בגרסה הסופית על מנת להסתיר מידע רגיש וטעינה מהירה יותר של הדפים
+
+caching - זכרון מטמון
+האם לשמור בזכרון מטמון את המיקום של הקבצים בין בקשות?
+ערך ברירת מחדל - True, יש לשנות ל FALSE במידה ולא מעוניינים
+פעולה זו מגבירה באופן דרמטי את מהירות הטעינת דפים [Kohana::find_file] ולכן יכולה להיות בעלת השפעה גדולה על רמת הביצועים הכללית של האפליקציה.
+חשוב להשתמש באופצייה זו רק בגרסה הסופית או בשביל נסיונות.
+
+`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 the URL 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 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:
+
+~~~
+ '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;
+~~~
diff --git a/includes/kohana/modules/userguide/guide/he-il/about.filesystem.md b/includes/kohana/modules/userguide/guide/he-il/about.filesystem.md
new file mode 100644
index 00000000..6372ee09
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/he-il/about.filesystem.md
@@ -0,0 +1,18 @@
+# Cascading Filesystem - מערכת קבצים מדורגת
+
+מערכת הקבצים של Kohana בנוייה ממבנה בסיסי יחיד אשר משוכפל לכל התקיות הנמצאות בנתיב המכונה
+include path. להלן צורת המבנה:
+
+1. application - אפליקציה
+2. modules, in order added - מודולים, לפי סדר ההופעה
+3. system - מערכת
+
+קבצים הנמצאים בתקיות שמעל ה include path מקבלים קדימות על קבצים עם שם זהה, עובדה המאפשרת
+לטעון ולדרוס פעולות קבצים על ידי טעינה של קבצים זהים לקבצים הקיימים, רק במיקום גבוה יותר יחסית. לדוגמא:
+
+
+
+אם יש מצב בו יש לנו קובץ מבט (view) בשם layout.php הממוקם בתקייה application/views וגם קיים בתקייה system/views
+הקובץ הממוקם בתקייה application יהיה זה שיוחזר ברגע שננסה לגשת ל layout.php
+בגלל שהוא נמצא גבוה יותר בסדר האינקלוד (include path order).
+ואם נמחוק את הקובץ הנמצא בתקייה application/views, אז הקובץ שיוחזר יהיה הקובץ השני שממוקם ב system/views.
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/he-il/about.flow.md b/includes/kohana/modules/userguide/guide/he-il/about.flow.md
new file mode 100644
index 00000000..a94d08bb
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/he-il/about.flow.md
@@ -0,0 +1,22 @@
+# Request Flow - זרימת תהליך בקשה מהשרת
+
+כל אפליקציה שרצה על קוהנה עוברת תהליך זהה בעת ביצוע בקשה של טעינת דף מהשרת
+
+1. האפליקציה נטענת ע"י הרצת הדף הראשי `index.php`
+2. מכלילה בתוכה את הדף `APPPATH/bootstrap.php`
+3. ה bootstrap קורא ל [Kohana::modules] עם רשימה של המודולים שבשימוש
+ 1. נוצר מערך עם הנתיבים של כל התקיות והקבצים המכילים את המודול
+ 2. בדיקה האם למודול יש קובץ init.php ובמידה וכן לטעון אותו
+ * כל קובץ init.php יכול לכלול בתוכו routes (ניתובים) חדשים אשר נטענים למערכת
+4. [Request::instance] רץ על מנת לבצע את הקריאה
+ 1. בדיקה מול ה routes הקיימים על מנת למצוא את המתאים
+ 2. טעינה של בקר (controller) והעברת הבקשה אליו
+ 3. קריאה לפונקציה [Controller::before] של הבקר המתאים
+ 4. קריאה לפעולה של הבקר לפי ה route
+ 5. קריאה לפונקציה [Controller::after] של הבקר המתאים
+5. הצגה של התוצאה
+
+
+יש אפשרות לשנות את אופן פעולת הבקר עצמו על ידי הפונקציה [Controller::before] בהסתמך על המשתנים בבקשה
+
+[!!] Stub
diff --git a/includes/kohana/modules/userguide/guide/he-il/about.kohana.md b/includes/kohana/modules/userguide/guide/he-il/about.kohana.md
new file mode 100644
index 00000000..7103c9d3
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/he-il/about.kohana.md
@@ -0,0 +1,21 @@
+# מה זה Kohana?
+
+Kohana היא מערכת בקוד פתוח,
+ [תשתית פיתוח לרשת](http://wikipedia.org/wiki/Web_Framework)
+[מונחה עצמים](http://wikipedia.org/wiki/Object-Oriented_Programming) [MVC](http://wikipedia.org/wiki/Model-View-Controller "Model View Controller")
+שנבנתה בשימוש עם
+[PHP5](http://php.net/manual/intro-whatis "PHP Hypertext Preprocessor")
+ע"י צוות מתנדבים שמטרתה להיות מהירה, מאובטחת, וקטנה.
+
+[!!] Kohana רשומה תחת רישיון ה [BSD license](http://kohanaframework.org/license), אי לכך באפשרותך לעשות כל שימוש באם הוא קוד פתוח, מסחרי, או פרוייקט אישי בלי שום מגבלות משפטיות.
+
+## מה עושה את Kohana כל-כך מצויין?
+
+
+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.
+
+## המדריך הזה מעפן!
+
+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).
diff --git a/includes/kohana/modules/userguide/guide/he-il/about.mvc.md b/includes/kohana/modules/userguide/guide/he-il/about.mvc.md
new file mode 100644
index 00000000..131e43bd
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/he-il/about.mvc.md
@@ -0,0 +1,29 @@
+# מודל Model View Controller
+
+תבנית Model-View-Controller (בקיצור MVC) היא תבנית עיצוב בהנדסת תוכנה המשמשת להפשטת יישום כלשהו. התבנית מתארת טכניקה לחלוקת היישום לשלושה חלקים, מודל, מבט ובקר, המחוברים ביניהם בצימוד רפוי מונחה אירועים. בדרך זו, התלות הדדית בין ממשק המשתמש לשאר חלקי התוכנה פוחתת, ואת החלקים השונים ניתן לפתח באופן בלתי-תלוי. בנוסף, קל יותר לתחזק את התוכנה וכן לעשות שימוש חוזר בחלקי היישום שהופרדו.
+
+#תיאור התבנית
+
+מקובל לחלק יישום תוכנה למספר שכבות נפרדות: שכבת התצוגה (ממשק משתמש), שכבת התחום העסקי (לעתים נקראת גם "שכבת הלוגיקה העסקית") ושכבת הגישה לנתונים. בתבנית MVC, שכבת התצוגה מחולקת בנוסף למבט ובקר. יש המחשיבים את התבנית כתבנית עיצוב, אך בהשוואה לתבניות עיצוב אחרות, MVC עוסקת במבנים בקנה מידה בינוני-גדול ולכן נחשבת גם כתבנית ארכיטקטורה.
+
+מודל
+ המודל הוא יצוג מסוים, מוכוון תחום עסקי, של המידע עליו פועל היישום. המודל, למרות הדעה הרווחת, אינו שם אחר לשכבת התחום העסקי והוא נפרד ממנה. תבנית MVC אינה מזכירה במפורש את שכבת הגישה לנתונים, מכיוון ששכבה זו היא מתחת למודל, או נעטפת על ידו.
+
+מבט
+ תפקידו להמיר את נתוני המודל לייצוג המאפשר למשתמש לבצע פעולת גומלין כלשהי. לרוב מדובר על המרה לממשק משתמש כלשהו. תבנית MVC משמשת רבות ביישומי Web, בהם המבט הוא דף HTML והקוד האוסף מידע דינמי לדף.
+
+בקר
+ תפקידו לעבד ולהגיב לאירועים המתרחשים במבט, לרוב, כתגובה לפעולה של המשתמש. בעיבוד האירועים, הבקר עשוי לשנות את המידע במודל, באמצעות שפעול שירותים המוגדרים בו. בקרים מורכבים מתבססים לרוב על יישום של תבנית Command.
+
+
+#אופן הפעולה
+
+ניתן ליישם את תבנית העיצוב MVC בדרכים רבות, אך לרוב היא מיושמת כך:
+
+- הבקר נרשם כ-Event Handler או Callback במבט, בדרך כלל סמוך ליצירת הבקר. כלומר, יישום של שיטת היפוך הפיקוח (IoC). משמע, הבקר יקבל פיקוח כאשר יתרחש אירוע קלט בממשק המשתמש.
+- המשתמש מבצע פעולת גומלין כלשהי עם הממשק. לדוגמה, מקליק על כפתור 'הוסף מוצר לעגלה'.
+- הבקר שנרשם על המבט מקבל פיקוח ומשפעל שירותים המוגדרים במודל, כדי לשקף את הפעולה שביצע המשתמש. לדוגמה, עדכון 'עגלת הקניות' של המשתמש בפריט נוסף.
+- המבט מקבל בצורה עקיפה את החוכמת התצוגה שלו מהבקר, בדרך כלל באמצעות יישום של תבנית Strategy.
+- המבט משתמש במודל כדי ליצור את ממשק המשתמש. לדוגמה, המבט מפיק רשימה של הפריטים בעגלה, כפי שאלה מיוצגים כרגע במודל. בין השניים אין קשר הדוק, והמודל אינו מודע לכך שהמבט ניגש למידע המאוחסן בו.
+- לעתים, המודל עשוי להודיע על שינויים המתחוללים בו לצדדים שלישיים נוספים, בדרך כלל באמצעות יישום של תבנית Observer.
+- ממשק המשתמש ממתין לפעולות נוספות של המשתמש, וכשאלה מתרחשות, התהליך חוזר על עצמו.
diff --git a/includes/kohana/modules/userguide/guide/he-il/debugging.code.md b/includes/kohana/modules/userguide/guide/he-il/debugging.code.md
new file mode 100644
index 00000000..edeb38db
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/he-il/debugging.code.md
@@ -0,0 +1,24 @@
+# Debugging - דיבוג
+
+קוהנה כוללת מספר כלים חזקים על מנת לעזור לך לדבג את האפליקציה שלך.
+הכלי הבסיסי הוא [Kohana::debug].
+כלי זה יציג את כל המשתנים או משתנה מסויים מכל סוג שהוא, בדומה ל [var_export](http://php.net/var_export) או [print_r](http://php.net/print_r), רק שקוהנה יודעת להשתמש ב HTML להצגה נוחה יותר
+
+~~~
+// הצג נתונים אודות המשתנים $foo ו- $bar
+echo Kohana::debug($foo, $bar);
+~~~
+
+קוהנה גם מאפשרת בקלות לצפות בקוד המקור של קובץ מסויים ע"י שימוש ב [Kohana::debug_source].
+
+~~~
+// הצגה של שורה מסויימת מקובץ מסויים
+echo Kohana::debug_source(__FILE__, __LINE__);
+~~~
+
+במידה ואתה מעוניין להציג מידע על האפליקציה מבלי לחשוף את התקיית התקנה, ניתן להשתמש ב [Kohana::debug_path]:
+
+~~~
+// מציג "APPPATH/cache" במקום הנתיב האמיתי
+echo Kohana::debug_file(APPPATH.'cache');
+~~~
diff --git a/includes/kohana/modules/userguide/guide/he-il/debugging.errors.md b/includes/kohana/modules/userguide/guide/he-il/debugging.errors.md
new file mode 100644
index 00000000..2369aea0
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/he-il/debugging.errors.md
@@ -0,0 +1,29 @@
+# Error/Exception Handling - טיפול בשגיאות וחריגים
+
+Kohana מאפשרת לנו טיפול נוח בשגיאות וחריגים על ידי הפיכת השגיאות לחריגים בעזרת ה
+[ErrorException](http://php.net/errorexception) של PHP.
+Kohana יודעת להציג נתונים רבים אודות השגיאות והחריגים שזיהתה:
+
+1. Exception class -
+2. Error level - רמת השגיאה
+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 - קבצים שנכללו, סיומות שנטענו ומשתנים גלובאלים
+
+## דוגמא להודעת שגיאה
+
+לחץ על אחד הקישורים הממוספרים על מנת להציג או להסתיר את המידע הנוסף
+
+
{{userguide/examples/error}}
+
+## Disabling Error/Exception Handling - ביטול הטיפול בשגיאות וחריגים
+
+במידה וברצונך לבטל את הטיפול בשגיאות, ניתן לעשות זאת בעת הקריאה
+ [Kohana::init] בצורה הבאה:
+
+~~~
+Kohana::init(array('errors' => FALSE));
+~~~
+
+חשוב לזכור שבדרך כלל נרצה שהשגיאות המפורטות יופיעו רק בעבודה לוקאלית ולא באתר אונליין
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/he-il/debugging.profiling.md b/includes/kohana/modules/userguide/guide/he-il/debugging.profiling.md
new file mode 100644
index 00000000..bd7663b1
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/he-il/debugging.profiling.md
@@ -0,0 +1,21 @@
+# Profiling - פרופיל סטטיסטי לכל דף
+
+קוהנה מאפשרת בקלות לצפות בסטטיסטיקה אודות האפליקציה:
+
+1. קריאות למטודות קוהנה
+2. בקשות
+3. שאילתות שבוצעו על מסד הנתונים
+4. ממוצע זמני פעולה של האפליקציה
+
+## דוגמא
+
+ניתן לאסוף ולהציג את הסטטיסטיקות בכל רגע נתון:
+~~~
+
+
+
+~~~
+
+## התוצאה:
+
+{{profiler/stats}}
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/he-il/menu.md b/includes/kohana/modules/userguide/guide/he-il/menu.md
new file mode 100644
index 00000000..5b027976
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/he-il/menu.md
@@ -0,0 +1,26 @@
+1. **מתחילים**
+ - [ מה זה Kohana?](about.kohana)
+ - [מוסכמות וסיגנון](about.conventions)
+ - [התקנה](about.install)
+ - [שידרוג](about.upgrading)
+ - [הגדרות](about.configuration)
+ - [Model View Controller הסבר על](about.mvc)
+ - [מערכת קבצים](about.filesystem)
+ - [Autoloading - טעינה אוטומטית](about.autoloading)
+ - [Request זרימת](about.flow)
+ - [API דפדפן](api)
+2. **ערכות לימוד**
+ - [Hello, World](tutorials.helloworld)
+ - [Routes, URLs, and Links](tutorials.urls)
+ - [Databases](tutorials.databases)
+ - [ORM](tutorials.orm)
+ - [עבודה עם Git](tutorials.git)
+3. **אבטחה**
+ - [XSS](security.xss)
+ - [Validation - ואלידציה](security.validation)
+ - [Cookies - עוגיות](security.cookies)
+ - [Database - מסד נתונים](security.database)
+4. **ניפוי באגים**
+ - [קוד](debugging.code)
+ - [טיפול בשגיאות](debugging.errors)
+ - [פרופיל](debugging.profiling)
diff --git a/includes/kohana/modules/userguide/guide/menu.md b/includes/kohana/modules/userguide/guide/menu.md
new file mode 100644
index 00000000..a5aacb86
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/menu.md
@@ -0,0 +1,31 @@
+1. **Getting Started**
+ - [What is Kohana?](about.kohana)
+ - [Conventions and Style](about.conventions)
+ - [Model View Controller](about.mvc)
+ - [Cascading Filesystem](about.filesystem)
+ - [Request Flow](about.flow)
+ - [Installation](about.install)
+ - [Upgrading](about.upgrading)
+ - [API Browser](api)
+3. **Basic Usage**
+ - [Configuration](using.configuration)
+ - [Loading Classes](using.autoloading)
+ - [Views and HTML](using.views)
+ - [Sessions and Cookies](using.sessions)
+ - [Messages](using.messages)
+4. **Debugging**
+ - [Code](debugging.code)
+ - [Error Handling](debugging.errors)
+ - [Profiling](debugging.profiling)
+5. **Security**
+ - [XSS](security.xss)
+ - [Validation](security.validation)
+ - [Cookies](security.cookies)
+ - [Database](security.database)
+6. **Tutorials**
+ - [Hello, World](tutorials.helloworld)
+ - [Routes, URLs, and Links](tutorials.urls)
+ - [Clean URLs](tutorials.removeindex)
+ - [Databases](tutorials.databases)
+ - [ORM](tutorials.orm)
+ - [Working with Git](tutorials.git)
diff --git a/includes/kohana/modules/userguide/guide/nl/about.conventions.md b/includes/kohana/modules/userguide/guide/nl/about.conventions.md
new file mode 100644
index 00000000..65094928
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/about.conventions.md
@@ -0,0 +1,316 @@
+# Conventies
+
+Het is aanbevolen om Kohana's [manier van coderen](http://dev.kohanaframework.org/wiki/kohana2/CodingStyle) te gebruiken. Dit gebruikt de [BSD/Allman stijl](http://en.wikipedia.org/wiki/Indent_style#BSD.2FAllman_style) van haakjes, en nog andere dingen.
+
+## Class namen en locaties van bestanden {#classes}
+
+Class namen in Kohana volgen een strikte conventie om [autoloading](using.autoloading) gemakkelijker te maken. Class namen zouden met een hoofdletter moeten beginnen en een underscore gebruiken om woorden af te scheiden van elkaar. Underscores zijn belangrijk omdat ze de locatie van het bestand weerspiegelen in de folderstructuur.
+
+De volgende conventies worden gebruikt:
+
+1. CamelCased class namen worden niet gebruikt, alleen maar als het onnodig is om een nieuw folderniveau aan te maken.
+2. Alle class bestandsnamen en foldernamen zijn met kleine letters geschreven.
+3. Alle classes zitten in de `classes` folder. Dit kan op ieder niveau in het [cascading filesystem](about.filesystem).
+
+[!!] In tegenstelling tot Kohana v2.x, is er geen afscheiding tussen "controllers", "models", "libraries" en "helpers". Alle classes worden in de folder "classes/" geplaatst, of het nu static "helpers" of object "libraries" zijn. Ieder design pattern is mogelijk voor het maken van classes: static, singleton, adapter, etc.
+
+## Voorbeelden
+
+Onthoud dat in een class, een underscore een folder betekent. Bekijk de volgende voorbeelden:
+
+Class Naam | Locatie File
+----------------------|-------------------------------
+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
+
+## Coding Standaarden {#coding_standards}
+
+Om zeer consistente broncode te schrijven, vragen we dat iedereen de coding standaarden zo nauw mogelijk probeert na te volgen.
+
+### Gekrulde Haakjes (Brackets)
+
+Gebruik aub [BSD/Allman Stijl](http://en.wikipedia.org/wiki/Indent_style#BSD.2FAllman_style) van bracketing.
+
+### Naam conventies
+
+Kohana gebruikt underscore namen, geen camelCase.
+
+#### Classes
+
+ 5) ? ($bar + $foo) : strlen($bar);
+
+Bij het scheiden van complexe ternaries (ternaries waarbij het eerste deel meer dan ~80 karakters bevat) in meerdere regels, moet je spaties gebruiken om operators op te lijnen, deze plaats je in het begin van de opeenvolgende lijnen:
+
+ $foo = ($bar == $foo)
+ ? $foo
+ : $bar;
+
+### Type Casting
+
+Type casting wordt gedaan met spatie langs elke kant van de cast:
+
+ // Correct:
+ $foo = (string) $bar;
+ if ( (string) $bar)
+
+ // Niet correct:
+ $foo = (string)$bar;
+
+Indien mogelijk, gebruik dan in plaats van type casting ternaire operators:
+
+ // Correct:
+ $foo = (bool) $bar;
+
+ // Niet correct:
+ $foo = ($bar == TRUE) ? TRUE : FALSE;
+
+Bij het casten van een integer of een boolean gebruik je het korte formaat:
+
+ // Correct:
+ $foo = (int) $bar;
+ $foo = (bool) $bar;
+
+ // Incorrect:
+ $foo = (integer) $bar;
+ $foo = (boolean) $bar;
+
+### Constanten
+
+Gebruik altijd hoofdletters voor constanten:
+
+ // Correct:
+ define('MY_CONSTANT', 'my_value');
+ $a = TRUE;
+ $b = NULL;
+
+ // Niet correct:
+ define('MyConstant', 'my_value');
+ $a = True;
+ $b = null;
+
+Plaats constant vergelijkingen aan het einde van de tests:
+
+ // Correct:
+ if ($foo !== FALSE)
+
+ // Niet correct:
+ if (FALSE !== $foo)
+
+Dit is een enigszins een controversiële keuze, dus is een uitleg op zijn plaats. Als we het vorige voorbeeld in gewoon taal schrijven, zou het goede voorbeeld als volgt te lezen zijn:
+
+ if variable $foo is not exactly FALSE
+
+En het foute voorbeeld zou als volgt te lezen zijn:
+
+ if FALSE is not exactly variable $foo
+
+En aangezien we van links naar rechts lezen, is het logischer om de constante als laatste te plaatsen.
+
+### Commentaren
+
+#### Commentaren op één lijn
+
+Gebruik //, best boven de lijn met je code waar je de commentaar voor wilt schrijven. Laat een spatie tussen en start met een hoofdletter. Gebruik nooit #
+
+ // Correct
+
+ //Niet correct
+ // niet correct
+ # Niet correct
+
+### Reguliere expressies
+
+Bij het coderen van reguliere expressies gebruik je beter PCRE in plaats van POSIX. PCRE zou krachtiger en sneller zijn.
+
+ // Correct:
+ if (preg_match('/abc/i'), $str)
+
+ // Incorrect:
+ if (eregi('abc', $str))
+
+Gebruik enkele aanhalingstekens rond uw reguliere expressies in plaats van dubbele aanhalingstekens. Enkele aanhalingstekens worden gemakkelijker door hun eenvoud in gebruik. In tegenstelling tot de dubbele aanhalingstekens ondersteunen ze niet variabele interpolatie, noch geïntegreerde backslash sequenties zoals \n of \t, enz.
+
+ // Correct:
+ preg_match('/abc/', $str);
+
+ // Incorrect:
+ preg_match("/abc/", $str);
+
+Bij het uitvoeren van een reguliere expressie zoeken en vervangen, gebruik dan de $n notatie voor terugverwijzingen. Dit verdient de voorkeur boven \\n.
+
+ // Correct:
+ preg_replace('/(\d+) dollar/', '$1 euro', $str);
+
+ // Incorrect:
+ preg_replace('/(\d+) dollar/', '\\1 euro', $str);
+
+Tot slot, let wel dat het $-teken voor de eindpositie van de lijn aan te geven toelaat om een newline-karakter als volgend karakter te gebruiken. Gebruik de D modifier om dit te verhelpen indien nodig. [Meer informatie](http://blog.php-security.org/archives/76-Holes-in-most-preg_match-filters.html).
+
+ $str = "email@example.com\n";
+
+ preg_match('/^.+@.+$/', $str); // TRUE
+ preg_match('/^.+@.+$/D', $str); // FALSE
diff --git a/includes/kohana/modules/userguide/guide/nl/about.filesystem.md b/includes/kohana/modules/userguide/guide/nl/about.filesystem.md
new file mode 100644
index 00000000..f2a1ddee
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/about.filesystem.md
@@ -0,0 +1,76 @@
+# Cascading Filesystem
+
+Het Kohana filesysteem heeft hiërarchie van folder-structuur. Wanneer een bestand wordt ingeladen door [Kohana::find_file], dan wordt het gezocht in de volgend volgorde:
+
+Application pad
+: Gedefineerd als `APPPATH` in `index.php`. De standaard value hiervan is `application`.
+
+Module paden
+: Dit is ingesteld als een associatieve array met behulp van [Kohana::modules] in `APPPATH/bootstrap.php`. Elk van de waarden van de array zal worden gezocht in de volgorde waarin de modules worden toegevoegd.
+
+System pad
+: Gedefineerd als `SYSPATH` in `index.php`. De standaard value hiervan is `system`. Alle belangrijkste of "core"-bestanden en classes zijn hier gedefinieerd.
+
+Bestanden die zich hoger bevinden in de volgorde van het inladen van bestanden hebben voorrang op bestanden met dezelfde naam die zich lager bevinden in de volgorde van inladen, dit maakt het mogelijk om ieder bestand te overloaden door een bestand met dezelfde naam in een "hogere" folder te plaatsen:
+
+
+
+Als je een view bestand hebt met de naam `welcome.php` in de `APPPATH/views` en `SYSPATH/views` folders, dan zal hetgeen uit application worden gereturned als `welcome.php` wordt ingeladen omdat het "hoger" staat in de folderstructuur.
+
+## Types bestanden
+
+De top level folders van de application, module en systeem paden hebben volgende standaard folders:
+
+classes/
+: Alle classes dat je wilt [automatisch inladen](using.autoloading) moeten zich hier
+ bevinden. Dit houdt in controllers, models, en alle andere classes. Alle classes moeten
+ de [class naam conventies](about.conventions#classes) volgen.
+
+config/
+: Configuratie bestanden geven een associatieve array van opties terug die je kunt
+ inladen via [Kohana::config]. Zie [gebruik van configuratie](using.configuration) voor
+ meer informatie.
+
+i18n/
+: Vertalingsbestanden geven een associatieve array van strings terug. Vertalen wordt
+ gedaan door de `__()` methode te gebruiken. Om "Hello, world!" te vertalen in het
+ Spaans zou je de methode `__('Hello, world!')` oproepen met [I18n::$lang] ingesteld op "es-es".
+ Zie [gebruik van vertaling](using.translation) voor meer informatie.
+
+messages/
+: Berichtenbestanden geven een associatieve array van strings terug die ingeladen kunnen
+ worden via [Kohana::message]. Messages en i18n bestanden verschillen erin dat messages
+ niet worden vertaald, maar altijd geschreven worden in de standaard taal en verwezen worden
+ via een enkelvoudige key. Zie [gebruik van messages](using.messages) voor meer informatie.
+
+views/
+: Views zijn plain PHP files die worden gebruikt om HTML of een ander formaat te genereren. Het view bestand wordt
+ ingeladen in in een [View] object en toegewezen variabelen, die het dan zal omzetten naar een HTML fractie. Het is mogelijk om meerder views in elkaar te gebruiken.
+ Zie [gebruik van views](usings.views) voor meer informatie.
+
+## Vinden van betanden
+
+Het pad naar eender welk bestand in de folderstructuur kan worden gevonden door het gebruik van [Kohana::find_file]:
+
+ // Vind het volledige pad naar "classes/cookie.php"
+ $path = Kohana::find_file('classes', 'cookie');
+
+ // Vind het volledige pad naar "views/user/login.php"
+ $path = Kohana::find_file('views', 'user/login');
+
+
+# Vendor Extensions
+
+Extensies die niet specifiek zijn aan Kohana noemen we "vendor" extensions.
+Bijvoorbeeld, als je [DOMPDF](http://code.google.com/p/dompdf) wilt gebruiken,
+dan moet je het kopiëren naar `application/vendor/dompdf` en de DOMPDF
+autoloading class inladen:
+
+ require Kohana::find_file('vendor', 'dompdf/dompdf/dompdf_config.inc');
+
+Nu kan je DOMPDF gebruiken zonder inladen van andere bestanden:
+
+ $pdf = new DOMPDF;
+
+[!!] Indien je views wilt omzetten in PDFs via DOMPDF, probeer dan de
+[PDFView](http://github.com/shadowhand/pdfview) module.
diff --git a/includes/kohana/modules/userguide/guide/nl/about.flow.md b/includes/kohana/modules/userguide/guide/nl/about.flow.md
new file mode 100644
index 00000000..b9194e0d
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/about.flow.md
@@ -0,0 +1,73 @@
+# Request Flow
+
+Iedere applicatie volgt de zelfde flow:
+
+1. Applicatie start vanaf `index.php`.
+2. De application, module, en system paden worden ingesteld.
+3. Error reporting niveaus worden ingesteld.
+4. Install file wordt geladen, als het bestaat.
+5. De [Kohana] class wordt ingeladen.
+6. Het bootstrap bestand, `APPPATH/bootstrap.php`, wordt geinclude.
+7. [Kohana::init] wordt aangeroepen, deze stelt error handling, caching, en logging in.
+8. [Kohana_Config] lezers en [Kohana_Log] schrijvers worden toegevoegd.
+9. [Kohana::modules] wordt aangeroepen om additionele modules te activeren.
+ * Module paden worden toegevoegd aan het [cascading filesystem](about.filesystem).
+ * Includen van `init.php` bestand, als het bestaat.
+ * Het `init.php` bestand kan een extra omgevingsinstellingen instellen, waaronder het toevoegen van routes.
+10. [Route::set] wordt verschillende keren opgeroepen om de [applicaties routes](using.routing) te definiëren.
+11. [Request::instance] wordt opgeroepen om het request-proces te starten.
+ 1. Iedere route controleren dat is ingesteld tot er een overeenkomst is gevonden.
+ 2. Conroller instantie wordt gecreeërd en het request wordt doorgeven eraan.
+ 3. De [Controller::before] methode wordt aangeroepen.
+ 4. De controller action wordt aangeroepen, deze genereerd de request response.
+ 5. De [Controller::after] methode wordt aangeroepen.
+ * De 5 bovenstaande stappen kunnen verschillende keren worden herhaald wanneer je [HMVC sub-requests](about.mvc) gebruikt.
+12. De basis [Request] response wordt getoond
+
+## index.php
+
+Kohana volgt een [front controller] pattern, dit betekent dat alle requests worden gezonden naar `index.php`. Dit laat een zeer eenvoudig [bestandsstructuur](about.filesystem) design toe. In `index.php` zijn er enkele zeer basis configuratie opties mogelijk. je kan de `$application`, `$modules`, en `$system` paden veranderen en het error reporting level instellen.
+
+De `$application` variabele laat je toe om de folder in te stellen die al je application bestanden bevat. Standaard is dit `application`. De `$modules` variabele laat je toe om de folder in te stellen die alle module bestanden bevat. De `$system` variabele laat je toe om de folder in te stellen die alle Kohana bestanden bevat.
+
+Je kan deze drie folders overal naartoe verplaatsen. Bijvoorbeeld, als je folderstructuur zo is ingesteld:
+
+ www/
+ index.php
+ application/
+ modules/
+ system/
+
+Dan kan je de folders uit de webroot verplaatsen:
+
+ application/
+ modules/
+ system/
+ www/
+ index.php
+
+Dan moet je de instellingen in `index.php` veranderen naar:
+
+ $application = '../application';
+ $modules = '../modules';
+ $system = '../system';
+
+Nu kan geen enkele van deze folders worden bereikt via de webserver. Het is niet noodzakelijk om deze verandering te maken, maar het maakt het wel mogelijk om de folders te delen met meerdere applicaties, de mogelijkheden zijn enorm.
+
+[!!] Er is een veiligheidscontrole bovenaan elke Kohana file om te voorkomen dat het wordt uitgevoerd zonder het gebruik van de front controller. Maar natuurlijk is het veiliger om de application, modules, en system folders te verplaatsen naar een locatie dat niet kan worden bereikt via het web.
+
+### Error Reporting
+
+Standaard toont Kohana alle errors, zo ook strikte warnings. Dit wordt ingesteld door [error_reporting](http://php.net/error_reporting):
+
+ error_reporting(E_ALL | E_STRICT);
+
+Als je applicatie live staat en in productie is, een meer conversatieve instelling is aangeraden, zoals het negeren van notices:
+
+ error_reporting(E_ALL & ~E_NOTICE);
+
+Als je een wit scherm krijgt wanneer een error is opgetreden, dan zal uw host waarschijnlijk het tonen van errors hebben uitgeschakeld. Je kan dit terug aanzetten door deze lijn toe te voegen juist achter je `error_reporting` call:
+
+ ini_set('display_errors', TRUE);
+
+Errors zouden **altijd** moeten worden getoond, zelf in productie, omdat het je toelaat om [exception en error handling](debugging.errors) te gebruiken om een mooie error pagina te tonen in plaats van een wit scherm als een error voorkomt.
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/nl/about.install.md b/includes/kohana/modules/userguide/guide/nl/about.install.md
new file mode 100644
index 00000000..71a566d5
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/about.install.md
@@ -0,0 +1,94 @@
+# Installatie
+
+1. Download de laatste **stabiele** release van de [Kohana website](http://kohanaframework.org/).
+2. Unzip het gedownloade pakket om de `kohana` folder aan te maken.
+3. Upload de inhoud van deze folder naar je webserver.
+4. Open `application/bootstrap.php` en maak de volgende aanpassingen:
+ - Stel de standaard [timezone](http://php.net/timezones) in voor je applicatie
+ - Stel de `base_url` in de [Kohana::init] methode in om te verwijzen naar de locatie van de kohana folder op je server
+6. Zorg ervoor dat de `application/cache` en `application/logs` folders schrijfrechten hebben voor de web server
+7. Test je installatie door de URL te openen in je favoriete browser dat je hebt ingesteld als `base_url`
+
+[!!] Afhankelijk van je platform is het mogelijk dat de installatie subfolders hun rechten verloren hebben tijdens de zip extractie. Chmod ze allemaal met 755 door het commando `find . -type d -exec chmod 0755 {} \;` uit te voeren in de root van je Kohana installatie.
+
+Je zou de installatie pagina moeten zien. Als het errors toont, zal je ze moeten aanpassen vooraleer je verder kunt gaan.
+
+
+
+Eens je installatie pagina zegt dat je omgeving goed is ingesteld dan moet je de `install.php` pagina hernoemen of verwijderen in de root folder. Je zou nu de de Kohana welcome pagina moeten zien:
+
+
+
+## Een productie-omgeving opzetten
+
+Er zijn enkele dingen dat je best doet met je applicatie vooraleer je deze in productie plaatst:
+
+1. Bekijk de [configuratie pagina](about.configuration) in de documentatie.
+ Dit omvat het grootste gedeelte van de globale instellingen dat zouden moeten veranderen bij andere omgevingen.
+ Als algemene regel, zet je best caching aan en zet je profiling uit ([Kohana::init] settings) voor sites in productie.
+ [Route caching](api/Route#cache) kan ook helpen als je heel wat routes hebt.
+2. Catch alle exceptions in `application/bootstrap.php`, zodat gevoelige gegevens niet kan worden gelekt door stack traces.
+ Zie onderstaand voorbeeld van Shadowhand's [wingsc.com broncode](http://github.com/shadowhand/wingsc).
+3. Zet APC of een andere soort opcode caching aan. Dit is het enige en eenvoudigste manier om de performantie te verbeteren dat je kunt doen in PHP zelf. Hoe complexer je applicatie, hoe groter het voordeel van opcode caching.
+
+[!!] Opmerking: De standaard bootstrap zal Kohana::$environment = $_ENV['KOHANA_ENV'] instellen indien ingesteld. Documentatie hoe je deze variable moet invullen kan je vinden in je webservers documentatie (e.g. [Apache](http://httpd.apache.org/docs/1.3/mod/mod_env.html#setenv), [Lighttpd](http://redmine.lighttpd.net/wiki/1/Docs:ModSetEnv#Options), [Nginx](http://wiki.nginx.org/NginxHttpFcgiModule#fastcgi_param))). Deze manier wordt als beste beschouwd in vergelijking met de alternatieve manieren om Kohana::$environment in te stellen.
+
+ /**
+ * Stel de omgeving in aan de hand van het domein (standaard Kohana::DEVELOPMENT).
+ */
+ Kohana::$environment = ($_SERVER['SERVER_NAME'] !== 'localhost') ? Kohana::PRODUCTION : Kohana::DEVELOPMENT;
+ /**
+ * Initialiseer Kohana op basis van de omgeving
+ */
+ Kohana::init(array(
+ 'base_url' => '/',
+ 'index_file' => FALSE,
+ 'profile' => Kohana::$environment !== Kohana::PRODUCTION,
+ 'caching' => Kohana::$environment === Kohana::PRODUCTION,
+ ));
+
+ /**
+ * Voer de algemene request uit met PATH_INFO. Als er geen URI is gespecifeerd,
+ * dan zal de URI automatisch worden gedetecteerd.
+ */
+ $request = Request::instance($_SERVER['PATH_INFO']);
+
+ try
+ {
+ // Propeer het request uit te voeren
+ $request->execute();
+ }
+ catch (Exception $e)
+ {
+ if (Kohana::$environment == Kohana::DEVELOPMENT)
+ {
+ // Just re-throw the exception
+ throw $e;
+ }
+
+ // De error loggen
+ Kohana::$log->add(Kohana::ERROR, Kohana::exception_text($e));
+
+ // Maak een 404 uitvoer
+ $request->status = 404;
+ $request->response = View::factory('template')
+ ->set('title', '404')
+ ->set('content', View::factory('errors/404'));
+ }
+
+ if ($request->send_headers()->response)
+ {
+ // Verkrijg totaal aantal geheugen en snelheids tijd
+ $total = array(
+ '{memory_usage}' => number_format((memory_get_peak_usage() - KOHANA_START_MEMORY) / 1024, 2).'KB',
+ '{execution_time}' => number_format(microtime(TRUE) - KOHANA_START_TIME, 5).' seconds');
+
+ // Stel de totalen in, in de uitvoer
+ $request->response = str_replace(array_keys($total), $total, $request->response);
+ }
+
+
+ /**
+ * Toon de uitvoer dan het request.
+ */
+ echo $request->response;
diff --git a/includes/kohana/modules/userguide/guide/nl/about.kohana.md b/includes/kohana/modules/userguide/guide/nl/about.kohana.md
new file mode 100644
index 00000000..1371b381
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/about.kohana.md
@@ -0,0 +1,15 @@
+# Wat is Kohana?
+
+Kohana is een open source, [objectgeoriënteerd](http://nl.wikipedia.org/wiki/Objectgeori%C3%ABnteerd) [MVC](http://wikipedia.org/wiki/Model–View–Controller "Model View Controller") [web framework](http://wikipedia.org/wiki/Web_Framework) gebouwd met [PHP5](http://php.net/manual/intro-whatis "PHP Hypertext Preprocessor") door een aantal vrijwilligers die veiligheid, snelheid en een kleine voetafdruk nastreven.
+
+[!!] Kohana is gelicentieerd onder een [BSD license](http://kohanaframework.org/license), zodat je het framework legaal kunt gebruiken voor allerlei projecten: open source, commercieel of persoonlijk.
+
+## Waarom is Kohana zo goed?
+
+Alles kan worden uitgebreid door het unieke design van het [filesystem](about.filesystem), er is geen of weinig [configuratie](about.configuration) voor nodig, [error handling](debugging.errors) helpt je vlug de oorzaak te vinden van je fouten en [debuggen](debugging) en [profiling](debugging.profiling) zorgen voor een beter inzicht in je applicatie.
+
+Om je te helpen je applicatie te beveiligen zijn er tools voor [XSS te verwijderen](security.xss), [input validatie](security.validation), [gesigneerde cookies](security.cookies), [formulieren](security.forms) en [HTML](security.html) generators toegevoegd aan het systeem. De [database](security.database) layer voorkomt [SQL injectie](http://wikipedia.org/wiki/SQL_Injection). En natuurlijk, alle officiële code is met zorg geschreven en herbekeken inzake veiligheid.
+
+## Vul mee deze documentatie aan
+
+We zijn keihard en volop bezig om je van een complete documentatie te voorzien. Om deze documentatie te helpen verbeteren, gelieve dan de userguide te [forken](http://github.com/kohana/userguide), uw aanpassingen te doen en een pull request te sturen. Als je nog geen ervaring hebt met git kan je altijd een [feature request](http://dev.kohanaframework.org/projects/kohana3/issues) aanmaken (vereist registratie).
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/nl/about.mvc.md b/includes/kohana/modules/userguide/guide/nl/about.mvc.md
new file mode 100644
index 00000000..2e94ba8f
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/about.mvc.md
@@ -0,0 +1,7 @@
+# (Hiërarchische) Model View Controller
+
+Model View Controller (of MVC afgekort) is een populair design pattern dat de data (Model) afscheid van de presentatie/templates (View) en de request-logica (Controller).
+
+Het maakt ontwikkelen van applicaties een stuk gemakkelijker omdat het systeem zo gedesignd is om code meermaals te hergebruiken, wat wil zeggen dat je er minder moet schrijven!
+
+[!!] Stub
diff --git a/includes/kohana/modules/userguide/guide/nl/about.translation.md b/includes/kohana/modules/userguide/guide/nl/about.translation.md
new file mode 100644
index 00000000..00c3f529
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/about.translation.md
@@ -0,0 +1,4 @@
+# Translation
+
+[!!] This article is a stub!
+
diff --git a/includes/kohana/modules/userguide/guide/nl/about.upgrading.md b/includes/kohana/modules/userguide/guide/nl/about.upgrading.md
new file mode 100644
index 00000000..d843e996
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/about.upgrading.md
@@ -0,0 +1,288 @@
+# Upgraden vanaf 2.3.x
+
+De code van Kohana v3 werkt grotendeels anders dan Kohana 2.3, hier is een lijst van de meeste valkuilen en tips om succesvol te upgraden.
+
+## Naming conventies
+
+In 2.x versies onderscheiden de verschillende soorten van classes (zoals controller, model, ...) zich met elkaar met behulp van achtervoegsels. Mappen in de model / controller mappen hadden geen invloed op de naam van de class.
+
+In 3.0 werd aanpak geschrapt ten gunste van de Zend framework bestandssysteem conventies, waar de naam van de class het pad is naar de class zelf, gescheiden door een underscore in plaats van slashes (dus `/some/class/file.php` bekomt `Some_Class_File`).
+Zie de [conventies documentatie](start.conventions) voor meer informatie.
+
+## Input Library
+
+De Input Library is verwijderd in 3.0, er wordt nu aanbevolen om gewoon `$_GET` en `$_POST` te gebruiken.
+
+### XSS Protectie
+
+Als je invoer van gebruikers wilt filteren op XSS kan je [Security::xss_clean] gebruiken om:
+
+ $_POST['description'] = security::xss_clean($_POST['description']);
+
+Je kan ook altijd [Security::xss_clean] gebruiken als filter met de [Validate] library:
+
+ $validation = new Validate($_POST);
+
+ $validate->filter('description', 'Security::xss_clean');
+
+### POST & GET
+
+Eén van de grootste functies van de Input Library was als je probeerde een waarde uit een superglobale array te halen en deze bestond bestond niet, dan zou de Input Library een standaard waarde teruggeven dat je kon instellen:
+
+ $_GET = array();
+
+ // $id heeft de waarde 1 gekregen
+ $id = Input::instance()->get('id', 1);
+
+ $_GET['id'] = 25;
+
+ // $id heeft de waarde 25 gekregen
+ $id = Input::instance()->get('id', 1);
+
+In 3.0 kan je deze functionaliteit nabootsen door [Arr::get] te gebruiken:
+
+ $_GET = array();
+
+ // $id heeft de waarde 1 gekregen
+ $id = Arr::get($_GET, 'id', 1);
+
+ $_GET['id'] = 42;
+
+ // $id heeft de waarde 42 gekregen
+ $id = Arr::get($_GET, 'id', 1);
+
+## ORM Library
+
+Er zijn redelijk veel grote wijzingingen aangebracht in ORM sedert 2.3. Hier is een lijst met de meest voorkomende upgrade problemen.
+
+### Member variablen
+
+Alle member variablen hebben nu een voorvoegsel gekregen met een underscore (_) en zijn niet langer bereikbaar via `__get()`. Nu moet je een functie aanroepen met de naam van de property zonder de underscore.
+
+Bijvoorbeeld, in 2.3 had je `loaded` en in 3.x is dat nu `_loaded` en heb je nu toegang van buiten de class via `$model->loaded()`.
+
+### Relaties
+
+Als je in 2.3 de gerelateerde objecten van een model wilde herhalen, kon je dat zo doen:
+
+ foreach($model->{relation_name} as $relation)
+
+Maar in 3.0 zal dit niet werken. In de 2.3 serie werden alle queries die gegenereerd werden met behulp van de Database Library gegeneeerd in een globale omgeving, wat betekent dat je niet kon proberen en maken van twee queries. Bijvoorbeeld:
+
+# TODO: GOED VOORBEELD!!!!
+
+Deze query zou mislukken doordat de tweede, inner query alle voorwaarden zou overerven van de eerste, wat zou leiden tot het mislukken.
+In 3.0 is dit aangepast door iedere query te laten genereren in zijn eigen omgeving. Let wel dat sommige dingen hierdoor niet gaan werken zoals je verwacht. Bijvoorbeeld:
+
+ foreach(ORM::factory('user', 3)->where('post_date', '>', time() - (3600 * 24))->posts as $post)
+ {
+ echo $post->title;
+ }
+
+[!!] (Zie [de Database tutorial](tutorials.databases) voor de nieuwe query syntax)
+
+In 2.3 zou je verwachten dat dit iterator teruggeeft van alle berichten van een gebruiker met `id` 3 met een `post_date` binnenin de 24 uren, maar in de plaats daarvan zal de WHERE conditie toegepast worden op het user-model en een `Model_Post` worden teruggevens met de joining conditities gespecifieerd.
+
+Om hetzelfde effect te verkrijgen zoals in 2.3, moet je de structuur iets aanpassen:
+
+ foreach(ORM::factory('user', 3)->posts->where('post_date', '>', time() - (36000 * 24))->find_all() as $post)
+ {
+ echo $post->title;
+ }
+
+Dit is ook van toepassing op de `has_one` relaties:
+
+ // Niet correct
+ $user = ORM::factory('post', 42)->author;
+ // Correct
+ $user = ORM::factory('post', 42)->author->find();
+
+### Has and belongs to many relaties
+
+In 2.3 kon je `has_and_belongs_to_many` relaties specifieren. In 3.0 is deze functionaliteit herschreven naar `has_many` *through*.
+
+In het model definieer je een `has_many` relatie met een ander model maar dan voeg je nog een `'through' => 'table'` attribuut aan toe, waar `'table'` de naam is van de trough tabel. Bijvoorbeeld (in de context van posts<>categories):
+
+ $_has_many = array
+ (
+ 'categories' => array
+ (
+ 'model' => 'category', // The foreign model
+ 'through' => 'post_categories' // The joining table
+ ),
+ );
+
+Als je Kohana hebt opgezet om een tabel voorvoegsel te gebruiken, dan hoef je geen zorgen te maken om dit voorvoegsel hier te gebruiken bij de tabelnaam.
+
+### Foreign keys
+
+Als je in Kohana 2.x's ORM een foreign key wilde overschrijven moest je de relatie specificeren waaraan het toebehoorde, en de nieuwe foreign key instellen in de member variabele `$foreign_keys`.
+
+In 3.0 moet je nu een `foreign_key` definiëren in de relatie-definitie, zoals hier:
+
+ Class Model_Post extends ORM
+ {
+ $_belongs_to = array
+ (
+ 'author' => array
+ (
+ 'model' => 'user',
+ 'foreign_key' => 'user_id',
+ ),
+ );
+ }
+
+In dit voorbeeld zouden we een `user_id` veld moeten hebben in de tabel posts.
+
+
+
+In has_many relaties is de `far_key` het veld in de trough tabel die linkt naar de foreign tabel en is de `foreign key` het veld in de trough tabel die "this" model's tabel linkt naar de trough table.
+
+Stel je de volgende opstelleing voor: "Posts" hebben en behoren tot vele "Categories" via `posts_sections` ("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',
+ ),
+ );
+ }
+
+
+Uiteraard is de aliasing setup hier een beetje gek, onnodig, maar het is een goed voorbeeld om te tonen hoe het foreign/far key systeem werkt.
+
+### ORM Iterator
+
+Het is ook best te melden dat `ORM_Iterator` nu herschreven is naar `Database_Result`.
+
+Als je een array van ORM objecten met hun keys als index van de array wilt verkrijgen, moet je [Database_Result::as_array] gebruiken, bijvoorbeeld:
+
+
+ $objects = ORM::factory('user')->find_all()->as_array('id');
+
+Waar `id` de primary key is in de user tabel.
+
+## Router Library
+
+In versie 2 was er een Router library die de main request afhandelde. Het liet je de basisroutes instellen in het `config/routes.php` bestand en het liet je toe om zelfgeschreven regex te gebruiken voor routes, maar het was niet echt flexibel als je iets radicaal wou veranderen.
+
+## Routes
+
+Het routing systeem (nu wordt verwezen naar het request systeem) is een stuk flexibeler in 3.0. Routes zijn nu gedefinieerd in het boostrap bestand (`application/bootstrap.php`) en de de module's init.php (`modules/module_name/init.php`). Het is ook interessant te weten dat routes worden geëvalueerd in de volgorde dat ze worden gedefinieerd. In plaats daarvan specifieer je een patroon voor elke uri, je kan variabelen gebruiken om segmenten aan te duiden (zoals een controller, methode, id).
+
+Bijvoorbeeld, in 2.x zouden deze regexes:
+
+ $config['([a-z]+)/?(\d+)/?([a-z]*)'] = '$1/$3/$1';
+
+de uri `controller/id/method` linken aan `controller/method/id`. In 3.0 gebruik je dit:
+
+ Route::set('reversed','((/(/)))')
+ ->defaults(array('controller' => 'posts', 'action' => 'index'));
+
+[!!] Iedere uri moet een unieke naam hebben (in dit geval `reversed`), de reden hiervoor wordt nader uitgelegd in de [url tutorial](tutorials.urls).
+
+Slashes geven dynamische secties weer die zouden moeten worden ontleed in variabelen. Haakjes geven optionele secties aan die niet vereist zijn. Als je met een route enkel uris die beginnen met admin wilt aanspreken kan je dit gebruiken:
+
+ Rouse::set('admin', 'admin(/(/(/)))');
+
+En als je wilt een dat een gebruiker een controller specificeert:
+
+ Route::set('admin', 'admin/(/(/))');
+
+Kohana maakt geen gebruik van `default defaults`. Als je wilt dat Kohana ervan uit gaat dat de standaard actie 'index' is, dan moet je dat ook zo instellen! Dit kan je doen via [Route::defaults]. Als je zelfgeschreven regex wilt gebruiken voor uri segmenten dan moet je ene array met `segment => regex` meegeven, bijvoorbeeld:
+
+ Route::set('reversed', '((/(/)))', array('id' => '[a-z_]+'))
+ ->defaults(array('controller' => 'posts', 'action' => 'index'))
+
+Dit zou de `id` waarde forceren om te bestaan uit kleine letters van a tot z en underscores.
+
+### Actions
+
+Nog één ding dat belangrijk is om te melden, is dat methoden in een controller die toegankelijk moeten zijn via een url nu "actions" worden genoemd. Ze krijgen een voorvoegsel 'action_'. Bijvoorbeeld in het bovenstaande voorbeeld, als de user de url `admin/posts/1/edit` aanroept dan is de actie `edit` maar is de methode die wordt aangeroepen in de controller `action_edit`. Zie de [url tutorial](tutorials.urls) voor meer informatie.
+
+## Sessies
+
+De volgende methoden worden niet meer ondersteund: Session::set_flash(), Session::keep_flash() or Session::expire_flash(), inde plaats daarvan gebruik je nu [Session::get_once].
+
+## URL Helper
+
+Er zijn maar een aantal kleinere dingen veranderd in de url helper. `url::redirect()` werd vervangen door `$this->request->redirect()` (binnenin controllers) en `Request::instance()->redirect()`.
+
+`url::current` werd nu vervangen door `$this->request->uri()`
+
+## Valid / Validation
+
+Deze twee classes zijn nu samengevoegd in één enkele class met de naam `Validate`.
+
+De syntax om arrays te valideren is een klein beetje gewijzigd:
+
+ $validate = new Validate($_POST);
+
+ // Pas een filter toe op alle waarden in de array
+ $validate->filter(TRUE, 'trim');
+
+ // Om enkel rules te definiëren gebruik je rule()
+ $validate
+ ->rule('field', 'not_empty')
+ ->rule('field', 'matches', array('another_field'));
+
+ // Om meerdere rules te definiëren voor een veld gebruik je rules(), je geeft een array mee met `passing an array of rules => params als tweede argument
+ $validate->rules('field', array(
+ 'not_empty' => NULL,
+ 'matches' => array('another_field')
+ ));
+
+De 'required' rule is ook verandert van naam. Nu wordt voor de duidelijkheid 'not_empty' gebruikt.
+
+## View Library
+
+Er zijn enkele kleine wijzigingen aangebracht aan de View library die de moeite zijn om even te melden.
+
+In 2.3 werden views gegenereerd binnenin de scope van de controller, dit liet je toe om `$this` te gebruiken als referentie naar de controller vanuit je view, dit is verandert in 3.0. Views worden nu gegenereerd in een lege scope. Als je nog `$this` wilt gebruiken in je view, moet je een referentie leggen via [View::bind]: `$view->bind('this', $this)`.
+
+Het moet wel gezegd worden dat dit een *erg* slechte manier van werken is omdat het je view koppelt aan de controller wat tegenhoud om deze view opnieuw te gebruiken. Het is aan te raden om de noodzakelijke variabelen voor je view als volgt door te sturen:
+
+ $view = View::factory('my/view');
+
+ $view->variable = $this->property;
+
+ // OF als je dit wilt "chainen"
+
+ $view
+ ->set('variable', $this->property)
+ ->set('another_variable', 42);
+
+ // NIET aangeraden
+ $view->bind('this', $this);
+
+Omdat de view gegenereerd wordt in een lege scope, is `Controller::_kohana_load_view` nu overtollig. Als je de view moet aanpassen vooraleer het word gegenereerd (bijvoorbeeld om een menu te gereneren over de gehele site) kan je [Controller::after] gebruiken.
+
+ Class Controller_Hello extends Controller_Template
+ {
+ function after()
+ {
+ $this->template->menu = '...';
+
+ return parent::after();
+ }
+ }
diff --git a/includes/kohana/modules/userguide/guide/nl/debugging.code.md b/includes/kohana/modules/userguide/guide/nl/debugging.code.md
new file mode 100644
index 00000000..6013ec72
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/debugging.code.md
@@ -0,0 +1,18 @@
+# Debuggen
+
+Kohana heeft verschillende goede tools om je te helpen met het debuggen van je applicatie.
+
+De meest gebruikte is [Kohana::debug]. Deze eenvoudige methode geef alle variablen terug, vergelijkbaar met [var_export](http://php.net/var_export) of [print_r](http://php.net/print_r), maar het gebruikt HTML voor extra opmaak.
+
+ // Toon een dump van de variabelen $foo en $bar
+ echo Kohana::debug($foo, $bar);
+
+Kohana biedt ook een methode aan om de broncode van een bepaald bestand te tonen via [Kohana::debug_source].
+
+ // Toon deze lijn van de broncode
+ echo Kohana::debug_source(__FILE__, __LINE__);
+
+Als je informatie wilt tonen over uw applicatie bestanden zonder te vertellen wat de installatie folder is, kan je [Kohana::debug_path] gebruiken:
+
+ // Toont "APPPATH/cache" in plaats van het echte path
+ echo Kohana::debug_path(APPPATH.'cache');
diff --git a/includes/kohana/modules/userguide/guide/nl/debugging.errors.md b/includes/kohana/modules/userguide/guide/nl/debugging.errors.md
new file mode 100644
index 00000000..aba56ac6
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/debugging.errors.md
@@ -0,0 +1,22 @@
+# Error/Exception Handling
+
+Kohana biedt zowel een exception handler als een error handler aan die errors transformeert in exceptions met behulp van PHP's [ErrorException](http://php.net/errorexception) class. Veel details over de error en de interne toestand van de applicatie wordt weergegeven door de handler:
+
+1. Exception class
+2. Error niveau
+3. Error bericht
+4. Bron van de error, met de errorlijn gehighlight
+5. Een [debug backtrace](http://php.net/debug_backtrace) van de uitvoerings flow
+6. Ingeladen bestanden, extensies en globale variablen
+
+## Voorbeeld
+
+Klik op een van de links om extra informatie te tonen:
+
+
{{userguide/examples/error}}
+
+## Error/Exception Handling uitzetten
+
+Als je niet de interne error handling wilt gebruiken, kan je deze uitschakelen wanneer je [Kohana::init] aanroept:
+
+ Kohana::init(array('errors' => FALSE));
diff --git a/includes/kohana/modules/userguide/guide/nl/debugging.profiling.md b/includes/kohana/modules/userguide/guide/nl/debugging.profiling.md
new file mode 100644
index 00000000..9c82f7dc
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/debugging.profiling.md
@@ -0,0 +1,20 @@
+# Profiling
+
+Kohana biedt een zeer eenvoudige manier aan om statistieken over uw aanvraag te tonen:
+
+1. Gewone [Kohana] methodes dat aangeroepen worden
+2. Requests
+3. [Database] queries
+4. Gemiddelde uitvoeringstijden voor uw applicatie
+
+## Voorbeeld
+
+Je kan op elk tijdstip de huidige [profiler] statistieken tonen of opvragen:
+
+
+
+
+
+## Voorbeeld
+
+{{profiler/stats}}
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/nl/features.md b/includes/kohana/modules/userguide/guide/nl/features.md
new file mode 100644
index 00000000..25cbb18e
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/features.md
@@ -0,0 +1 @@
+This page lists the features of Kohana v3
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/nl/menu.md b/includes/kohana/modules/userguide/guide/nl/menu.md
new file mode 100644
index 00000000..3aa787c1
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/menu.md
@@ -0,0 +1,31 @@
+1. **Ga aan de slag**
+ - [Wat is Kohana?](about.kohana)
+ - [Conventies and Codeerstijl](about.conventions)
+ - [Model View Controller](about.mvc)
+ - [Cascading Filesystem](about.filesystem)
+ - [Request Flow](about.flow)
+ - [Installatie](about.install)
+ - [Upgraden](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. **Debuggen**
+ - [Code](debugging.code)
+ - [Error Handling](debugging.errors)
+ - [Profiling](debugging.profiling)
+5. **Beveiliging**
+ - [XSS](security.xss)
+ - [Validatie](security.validation)
+ - [Cookies](security.cookies)
+ - [Database](security.database)
+6. **Tutorials**
+ - [Hello, World](tutorials.helloworld)
+ - [Routes, URLs en Links](tutorials.urls)
+ - [URLs opschonen](tutorials.removeindex)
+ - [Databases](tutorials.databases)
+ - [ORM](tutorials.orm)
+ - [Werken met Git](tutorials.git)
diff --git a/includes/kohana/modules/userguide/guide/nl/security.cookies.md b/includes/kohana/modules/userguide/guide/nl/security.cookies.md
new file mode 100644
index 00000000..174800f6
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/security.cookies.md
@@ -0,0 +1,3 @@
+# Cookie Veiligheid
+
+[!!] Nog niet beschikbaar
diff --git a/includes/kohana/modules/userguide/guide/nl/security.database.md b/includes/kohana/modules/userguide/guide/nl/security.database.md
new file mode 100644
index 00000000..aa5135b3
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/security.database.md
@@ -0,0 +1,3 @@
+# Database Veiligheid
+
+[!!] Nog niet beschikbaar
diff --git a/includes/kohana/modules/userguide/guide/nl/security.validation.md b/includes/kohana/modules/userguide/guide/nl/security.validation.md
new file mode 100644
index 00000000..80947b94
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/security.validation.md
@@ -0,0 +1,244 @@
+# Validatie
+
+Validatie kan uitgevoerd worden op elke array met behulp van de [Validate] class. Labels, filters, regels en callbacks kunnen aan het Validate object worden toegevoegd via een array key, zogenaamd een "veldnaam".
+
+labels
+: Een label is een gebruiksvriendelijke leesbare versie van de veldnaam.
+
+filters
+: Een filter wijzigt de waarde van een veld voordat de regels en callbacks worden uitgevoerd.
+
+rules
+: Een regel is een controle op een veld dat "TRUE" of "FALSE" teruggeeft. A rule is a check on a field that returns `TRUE` or `FALSE`. Als een regel `FALSE` teruggeeft, wordt er een error toegevoegd aan het veld.
+
+callbacks
+: Een callback is een zelfgeschreven methode die het gehele Validate object tot zijn beschikking heeft. De return value van een callback wordt genegeerd. In plaats daarvan moet de callback handmatig een error toevoegen aan het object met behulp van [Validate::error] wanneer een fout optreedt.
+
+[!!] Merk op dat de [Validate] callbacks en de [PHP callbacks](http://php.net/manual/language.pseudo-types.php#language.types.callback) niet hetzelfde zijn.
+
+Waneer je `TRUE` als veldnaam gebruikt bij het toevoegen van een filter, regel of callback, dan zal deze worden toegepast op alle velden met een naam.
+
+**Het [Validate] object zal alle velden verwijderen van de array wanneer deze niet specifiek een naam hebben gekregen via een label, filter, regel of callback. Dit voorkomt toegang tot velden die niet gevalideerd zijn als een veiligheidsmaatregel.**
+
+Een validatie object maken wordt gedaan door de [Validate::factory] methode:
+
+ $post = Validate::factory($_POST);
+
+[!!] Het `$post` object zal worden gebruikt voor de rest van deze tutorial. Deze tutorial zal je tonen hoe je een registratie van een nieuwe gebruiker valideert.
+
+### Standaard regels
+
+Validatie heeft standaard altijd enkele regels:
+
+Naam van de regel | Functie
+------------------------- |---------------------------------------------------
+[Validate::not_empty] | Waarde moet een niet-lege waarde zijn
+[Validate::regex] | Waarde moet voldoen aan de reguliere expressie
+[Validate::min_length] | Minimum aantal karakters voor een waarde
+[Validate::max_length] | Maximum aantal karakters voor een waarde
+[Validate::exact_length] | Waarde moet een exact aantal karakters bevatten
+[Validate::email] | Een emailadres is vereist
+[Validate::email_domain] | Controleer of het domein van het email bestaat
+[Validate::url] | Waarde moet een URL zijn
+[Validate::ip] | Waarde moet een IP address zijn
+[Validate::phone] | Waarde moet een telefoonnummer zijn
+[Validate::credit_card] | Waarde moet een credit card zijn
+[Validate::date] | Waarde moet een datum (en tijd) zijn
+[Validate::alpha] | Alleen alpha karakters toegelaten
+[Validate::alpha_dash] | Alleen alpha karakters en koppeltekens toegelaten
+[Validate::alpha_numeric] | Alleen alpha karakters en nummers toegelaten
+[Validate::digit] | Waarde moet een geheel getal zijn
+[Validate::decimal] | Waarde moet een decimaal of float getal zijn
+[Validate::numeric] | Alleen nummers toegelaten
+[Validate::range] | Waarde moet zich bevinden binnenin een range
+[Validate::color] | Waarde moet een geldige HEX kleurencode zijn
+[Validate::matches] | Waarde moet gelijk zijn aan een ander veld
+
+[!!] Iedere methode dat bestaat binnenin de [Validate] class kan gebruikt worden als validatie-regel zonder een volledige callback te definiëren. Bijvoorbeeld, `'not_empty'` toevoegen is hetzelfde als `array('Validate', 'not_empty')`.
+
+## Toevoegen van filters
+
+Alle validatie-filters worden gedefineerd als een veldnaam, een methode of een functie (gebruik makend van [PHP callback](http://php.net/manual/language.pseudo-types.php#language.types.callback) syntax) en een array van parameters:
+
+ $object->filter($field, $callback, $parameter);
+
+Filters veranderen de waarde van een veld vooraleer deze gecontoleerd zijn via regels of callbacks.
+
+Indien we het veld "username" willen omvormen naar kleine letters:
+
+ $post->filter('username', 'strtolower');
+
+Als we alle witruimtes voor en na de waarde willen verwijderen voor *alle* velden:
+
+ $post->filter(TRUE, 'trim');
+
+## Toevoegen van regels
+
+Alle validatieregels worden gedefineerd als een veldnaam, een methode of een functie (gebruik makend van [PHP callback](http://php.net/manual/language.pseudo-types.php#language.types.callback) syntax) en een array van parameters:
+
+ $object->rule($field, $callback, $parameter);
+
+Om ons voorbeeld te starten, zullen we validatie uitvoeren op een `$_POST` array die gebruikers registratie gegevens bevat:
+
+ $post = Validate::factory($_POST);
+
+Vervolgens moeten we de POST-informatie met behulp van [Validate] doorlopen. Om te beginnen moeten we een aantal regels toevoegen:
+
+ $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');
+
+Iedere bestaande PHP functie kan worden gebruikt als regel. Bijvoorbeeld, als we willen controleren of een gebruiker een correcte waarde heeft ingevuld als antwoord op de SSL question:
+
+ $post->rule('use_ssl', 'in_array', array(array('yes', 'no')));
+
+Merk op dat alle array parameters steeds moeten "verpakt" worden door een array! Zonder die array, `in_array` zou worden aangeroepen als `in_array($value, 'yes', 'no')`, wat een PHP error zou teruggeven.
+
+Je kan eigen regels toevoegen met behulp van een [PHP callback](http://php.net/manual/language.pseudo-types.php#language.types.callback]:
+
+ $post->rule('username', 'User_Model::unique_username');
+
+[!!] Momenteel (v3.0.7) is het niet mogelijk om een object te gebruiken als rule, enkel statische methodes en functies.
+
+De methode `User_Model::unique_username()` zal ongeveer gedefinieerd worden als:
+
+ public static function unique_username($username)
+ {
+ // Controleer of de username al bestaat in de database
+ return ! DB::select(array(DB::expr('COUNT(username)'), 'total'))
+ ->from('users')
+ ->where('username', '=', $username)
+ ->execute()
+ ->get('total');
+ }
+
+[!!] Zelfgeschreven regels laten toe om de vele extra controles te hergebruiken voor verschillende doeleinden. Deze functies zullen meestal bestaan in een model, maar kunnen gedefinieerd worden in elke class.
+
+## Toevoegen van callbacks
+
+Alle validatie-callbacks worden gedefineerd als een veldnaam en een methode of een functie (gebruik makend van [PHP callback](http://php.net/manual/language.pseudo-types.php#language.types.callback) syntax):
+
+ $object->callback($field, $callback);
+
+[!!] In tegenstelling tot filters en regels, kunnen geen parameters worden meegestuurd naar een callback.
+
+Het gebruikers wachtwoord moet gehashed worden indien het gevalideerd is, dus zulen we dit doen met een callback:
+
+ $post->callback('password', array($model, 'hash_password'));
+
+Dit in de veronderstelling dat de `$model->hash_password()` methode er gelijkaardig uitzien als:
+
+ public function hash_password(Validate $array, $field)
+ {
+ if ($array[$field])
+ {
+ // Hash het wachtwoord als het bestaat
+ $array[$field] = sha1($array[$field]);
+ }
+ }
+
+# Een volledig voorbeeld
+
+Eerst hewwen we een [View] nodig met daarin een HTML formulier, die we plaatsen in `application/views/user/register.php`:
+
+
+
+
Er zijn enkele fouten opgelopen, gelieve je ingevoerde gegevens opnieuw te bekijken.
Voor uw veiligheid wordt SSL altijd gebruik bij betalingen.
+
+
+
+
+
+[!!] Dit voorbeeld maakt veel gebruik van de [Form] helper. Het gebruik van [Form] in plaats van HTML schrijven zorgt ervoor dat alle formuliervelden overweg kunnen met ingevoerde waardes die HTML karakters bevatten. Indien je liever zelf HTML schrijft, gebruik dan zeker [HTML::chars] om gebruikersgegevens te "escapen".
+
+Vervolgens hebben we een controller nodig en een actie om de registratie uit te voeren, we plaatsen dit 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 is gevalideerd, registreer de gebruiker
+ $user->register($post);
+
+ // Altijd een redirect uitvoeren na een succesvolle POST om herladingsberichten te voorkomen.
+ $this->request->redirect('user/profile');
+ }
+
+ // Validatie is fout gelopen, verzamel alle errors
+ $errors = $post->errors('user');
+
+ // Toon het registratieformulier
+ $this->request->response = View::factory('user/register')
+ ->bind('post', $post)
+ ->bind('errors', $errors);
+ }
+
+ }
+
+We hebben ook een user-model nodig, we plaatsen dit in `application/classes/model/user.php`:
+
+ class Model_User extends Model {
+
+ public function register($array)
+ {
+ // Maak een nieuw gebruikerslijn aan in de database
+ $id = DB::insert(array_keys($array))
+ ->values($array)
+ ->execute();
+
+ // Bewaar het nieuwe gebruikers id in een cookie
+ cookie::set('user', $id);
+
+ return $id;
+ }
+
+ }
+
+Dat is het, we hebben een volledig gebruikersregistratie voorbeeld afgewerkt dat zorgvuldig ingevoerde gegevens controleert.
diff --git a/includes/kohana/modules/userguide/guide/nl/security.xss.md b/includes/kohana/modules/userguide/guide/nl/security.xss.md
new file mode 100644
index 00000000..05f5ed00
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/security.xss.md
@@ -0,0 +1,15 @@
+# Cross-Site Scripting (XSS) Veiligheid
+
+De eerste stap om [XSS](http://wikipedia.org/wiki/Cross-Site_Scripting)-aanvallen te voorkomen is weten wanneer je jezelf moet beschermen. XSS kan enkel worden geactiveerd wanneer het wordt weergegeven in de HTML-inhoud, dit kan soms via een formulier-veld of worden getoond van database resultaten. Elke globale variabele dat gebruikersgegevens bevat kan worden aangetast. Dit omvat `$ _GET`, `$ _POST` en `$ _COOKIE` gegevens.
+
+## Het voorkomen
+
+Er zijn maar een paar eenvoudige regels te volgen om uw applicatie HTML te beschermen tegen XSS. De eerste stap is om de [Security::xss] methode te gebruiken om alle ingevoerde gegevens op te kuisen die afkomstig zijn van een globale variabele. Als je geen HTML wilt in een variable, gebruik dan [strip_tags](http://php.net/strip_tags) om alle ongewenste HTML tags te verwijderen van de ingevoerde waarde.
+
+[!!] Als je gebruikers toelaat om HTML in te voeren in je applicatie, dan is het streng aanbevolen om een HTML "opkuis-tool" te gebruiken zoals [HTML Purifier](http://htmlpurifier.org/) of [HTML Tidy](http://php.net/tidy).
+
+De tweede stap is om altijd de ingevoerde HTML te escapen. De [HTML] class voorziet generatoren voor veelvoorkomende tags, zo ook script en stylesheet links, ankers, afbeeldingen en e-mail (mailto) links. Elke niet-vertrouwde inhoud moet worden ge-escaped met [HTML::chars].
+
+## Referenties
+
+* [OWASP XSS Cheat Sheet](http://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet)
diff --git a/includes/kohana/modules/userguide/guide/nl/tutorials.databases.md b/includes/kohana/modules/userguide/guide/nl/tutorials.databases.md
new file mode 100644
index 00000000..44f40d8e
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/tutorials.databases.md
@@ -0,0 +1,248 @@
+# Databases {#top}
+
+Kohana 3.0 heeft een goede module ingebouwd om te kunnen werken met databases. Standaard ondersteund de database module drivers voor [MySQL](http://php.net/mysql) en [PDO](http://php.net/pdo).
+
+De database module zit bij de Kohana 3.0 installatie maar het moet nog worden ingesteld vooraleer je het kan gebruiken. In je `application/bootstrap.php` bestand moet je de aanroep naar [Kohana::modules] aanpassen en de database module eraan toevoegen:
+
+ Kohana::modules(array(
+ ...
+ 'database' => MODPATH.'database',
+ ...
+ ));
+
+## Configuratie {#configuration}
+
+Nadat de module is ingesteld moet je een configuratie bestand aanmaken zodat de module weet hoe het moet connecteren met je database. Een voorbeeld configuratie bestand kan je vinden in `modules/database/config/database.php`.
+
+De structuur van een database configuratie groep, genoemd "instantie", ziet er als volgt uit:
+
+ 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,
+ ),
+
+[!!] Meerdere instanties van deze instellingen kunnen worden gedefinieerd binnen het configuratie bestand.
+
+Het verstaan van elk van deze instellingen is belangrijk.
+
+INSTANCE_NAME
+: Connecties kunnen elke naam hebben, maar je moet minstens één connectie hebben met de naam "default".
+
+DATABASE_TYPE
+: Eén van de geïnstalleerde database drivers. Kohana heeft standaard de "mysql" en "pdo" drivers.
+
+CONNECTION_ARRAY
+: Specifieke driver opties om te connecteren naar je database. (Driver opties worden uitgelegd [beneden](#connection_settings).)
+
+TABLE_PREFIX
+: Voorvoegsel dat wordt toegevoegd aan al je tabelnamen door de [query builder](#query_building).
+
+QUERY_PROFILING
+: Zet [profiling](debugging.profiling) aan van database queries.
+
+### Voorbeeld
+
+Het voorbeeld bestand hieronder toont 2 MySQL connecties, een lokale en één op afstand (=remote).
+
+ return array
+ (
+ 'default' => array
+ (
+ 'type' => 'mysql',
+ 'connection' => array(
+ 'hostname' => 'localhost',
+ 'username' => 'dbuser',
+ 'password' => 'mijnwachtwoord',
+ 'persistent' => FALSE,
+ 'database' => 'mijn_db_naam',
+ ),
+ 'table_prefix' => '',
+ 'charset' => 'utf8',
+ 'profiling' => TRUE,
+ ),
+ 'remote' => array(
+ 'type' => 'mysql',
+ 'connection' => array(
+ 'hostname' => '55.55.55.55',
+ 'username' => 'remote_user',
+ 'password' => 'mijnwachtwoord',
+ 'persistent' => FALSE,
+ 'database' => 'mijn_remote_db_naam',
+ ),
+ 'table_prefix' => '',
+ 'charset' => 'utf8',
+ 'profiling' => TRUE,
+ ),
+ );
+
+### Connectie instellingen {#connection_settings}
+
+Iedere database driver heeft verschillende connectie instellingen.
+
+#### MySQL
+
+Een MySQL database accepteert de volgende opties in de `connection` array:
+
+Type | Optie | Omschrijving | Standaard Waarde
+----------|------------|----------------------------| -------------------------
+`string` | hostname | Hostname van de database | `localhost`
+`integer` | port | Poort nummer | `NULL`
+`string` | socket | UNIX socket | `NULL`
+`string` | username | Database gebruikersnaam | `NULL`
+`string` | password | Database wachtwoord | `NULL`
+`boolean` | persistent | Persistente connecties | `FALSE`
+`string` | database | Database naam | `kohana`
+
+#### PDO
+
+Een PDO database accepteert de volgende opties in de `connection` array:
+
+Type | Optie | Omschrijving | Standaard Waarde
+----------|------------|----------------------------| -------------------------
+`string` | dsn | PDO data source identifier | `localhost`
+`string` | username | Database gebruikersnaam | `NULL`
+`string` | password | Database wachtwoord | `NULL`
+`boolean` | persistent | Persistente connecties | `FALSE`
+
+[!!] Als je PDO gebruikt en je bent niet zeker wat je moet gebruiken voor de `dsn` optie, bekijk dan [PDO::__construct](http://php.net/pdo.construct).
+
+## Connecties en Instanties {#connections}
+
+Iedere configuratie groep verwijst naar een database instantie. Iedere instantie kan worden aangesproken via [Database::instance]:
+
+ $default = Database::instance();
+ $remote = Database::instance('remote');
+
+Om de database los te koppelen, moet je gewoonweg het object vernietigen:
+
+ unset($default, Database::$instances['default']);
+
+Om all database instanties in één keer los te koppelen, gebruik je:
+
+ Database::$instances = array();
+
+## Het maken van Queries {#making_queries}
+
+Er zijn twee verschillende manieren om queries te maken. De eenvoudigste manier om een query te maken is het gebruik van [Database_Query], via [DB::query]. Deze queries worden "prepared statements" genoemd en laat je toe om query parameters instellen die automatisch worden "geescaped". De tweede manier om een query te maken is door deze op te bouwen met behulp van methode-aanroepen. Dit wordt gedaan met behulp van de [query builder](#query_building).
+
+[!!] Alle queries worden uitgevoerd via de `execute` methode, deze verwacht een [Database] object of een instantienaam. Zie [Database_Query::execute] voor meer informatie.
+
+### Prepared Statements
+
+Het gebruik van prepared statements laat je toe om SQL queries manueel te schrijven terwijl de query waarden nog steeds automatisch worden "geescaped" om [SQL injectie](http://wikipedia.org/wiki/SQL_Injection) te voorkomen. Een query aanmaken is relatief gemakkelijk:
+
+ $query = DB::query(Database::SELECT, 'SELECT * FROM users WHERE username = :user');
+
+De [DB::query] factory methode creëert een nieuwe [Database_Query] class voor ons, zodat "methode-chaining" mogelijk is. De query bevat een `:user` parameter, die we kunnen toewijzen aan een waarde:
+
+ $query->param(':user', 'john');
+
+[!!] Parameter namen kunnen elke string zijn aangezien worden vervangen via het gebruik van [strtr](http://php.net/strtr). Het wordt ten zeerste aanbevolen om **geen** dollar tekens te gebruiken als parameter namen om verwarring te voorkomen.
+
+Als je de SQL wilt tonen dat zal worden uitgevoerd, moet je het object gewoonweg casten naar een string:
+
+ echo Kohana::debug((string) $query);
+ // Zou moeten tonen:
+ // SELECT * FROM users WHERE username = 'john'
+
+Je kan ook altijd de `:user` parameter aanpassen door de [Database_Query::param] opnieuw aan te roepen:
+
+ $query->param(':user', $_GET['search']);
+
+[!!] Indien je meerdere paramters in één keer wilt instellen kan je dat doen met [Database_Query::parameters].
+
+Eénmaal je iets hebt toegewezen aan elke parameter, kan je de query uitvoeren:
+
+ $query->execute();
+
+Het is ook mogelijk om een parameter te "verbinden" met een variabele, door het gebruik van een [variabele referentie]((http://php.net/language.references.whatdo)). Dit kan extreem gebruikvol zijn wanneer je dezelfde query meerdere keren moet uitvoeren:
+
+ $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 het bovenstaand voorbeeld worden de variabelen `$username` en `$password` gewijzigd in iedere loop van het `foreach` statement. Wanneer de parameter verandert, veranderen infeite de `:user` en `:pass` query parameters. Het zorgvuldig gebruik van parameter binding kan een pak code besparen.
+
+### Query Building {#query_building}
+
+Het maken van dynamische queries via objecten en methodes zorgt ervoor dat queries zeer snel kunnen worden geschreven op een agnostische manier. Query building voegt ook identifier (tabel en kolom naam) en value quoting toe.
+
+[!!] Op dit moment, is het niet mogelijk om query building te combineren met prepared statements.
+
+#### SELECT
+
+Elk type database query wordt vertegenwoordigd door een andere class, elk met hun eigen methoden. Bijvoorbeeld, om een SELECT-query te maken, gebruiken we [DB::select]:
+
+ $query = DB::select()->from('users')->where('username', '=', 'john');
+
+Standaard zal [DB::select] alle kolommen selecteren (`SELECT * ...`), maar je kan ook specificeren welke kolommen je wilt teruggeven:
+
+ $query = DB::select('username', 'password')->from('users')->where('username', '=', 'john');
+
+Neem nu een minuut de tijd om te kijken wat deze methode-keten doet. Eerst maken we een selectie object met behulp van [DB::select]. Vervolgens stellen we tabel(len) in door de `from` methode te gebruiken. Als laatste stap zoeken we voor specifieke records door gebruik te maken van de `where` methode. We kunnen de SQL tonen dat zal worden uitgevoerd door deze te casten naar een string:
+
+ echo Kohana::debug((string) $query);
+ // Zou moeten tonen:
+ // SELECT `username`, `password` FROM `users` WHERE `username` = 'john'
+
+Merk op hoe de kolom en tabel namen automatisch worden "geescaped", eveneens de waarden? Dit is een van de belangrijkste voordelen van het gebruik van de query builder.
+
+Het is mogelijk om `AS` aliassen te maken wanneer je iets selecteert:
+
+ $query = DB::select(array('username', 'u'), array('password', 'p'))->from('users');
+
+Deze query zal de volgende SQL genereren:
+
+ SELECT `username` AS `u`, `password` AS `p` FROM `users`
+
+#### INSERT
+
+Om records aan te maken in de database gebruik je [DB::insert] om een INSERT query aan te maken:
+
+ $query = DB::insert('users', array('username', 'password'))->values(array('fred', 'p@5sW0Rd'));
+
+Deze query zal de volgende SQL genereren:
+
+ INSERT INTO `users` (`username`, `password`) VALUES ('fred', 'p@5sW0Rd')
+
+#### UPDATE
+
+Om een bestaande record aan te passen gebruik je [DB::update] om een UPDATE query aan te maken:
+
+ $query = DB::update('users')->set(array('username' => 'jane'))->where('username', '=', 'john');
+
+Deze query zal de volgende SQL genereren:
+
+ UPDATE `users` SET `username` = 'jane' WHERE `username` = 'john'
+
+#### DELETE
+
+Om een bestaande record te verwijderen gebruik je [DB::delete] om een DELETE query aan te maken:
+
+ $query = DB::delete('users')->where('username', 'IN', array('john', 'jane'));
+
+Deze query zal de volgende SQL genereren:
+
+ DELETE FROM `users` WHERE `username` IN ('john', 'jane')
+
+#### Database Functies {#database_functions}
+
+Uiteindelijk zal je waarschijnlijk uitdraaien in een situatie waar je beroep moet doen op `COUNT` of een andere database functie binnenin je query. De query builder ondersteunt deze functies op twee manieren. De eerste mogelijkheid is met behulp van aanhalingstekens binnenin de aliassen:
+
+ $query = DB::select(array('COUNT("username")', 'total_users'))->from('users');
+
+Dit ziet er bijna precies hetzelfde uit als een standaard "AS" alias, maar let op hoe de kolom naam is verpakt in dubbele aanhalingstekens. Iedere keer als er een waarde met dubbele aanhalingstekens verschijnt binnenin een kolom naam, wordt **alleen** het gedeelte binnen de dubbele aanhalingstekens "geescaped". Deze query zal de volgende SQL genereren:
+
+ SELECT COUNT(`username`) AS `total_users` FROM `users`
+
+#### Complexe Expressies
+
+Aliassen met aanhalingstekens zullen de meeste problemen oplossen, maar van tijd tot tijd kan je in een situatie komen waar je een complexe expressie kunt gebruiken. In deze gevallen moet je een database expressie gebruiken die je kan creëren met [DB::expr]. Een database expressie wordt als directe input genomen en er wordt geen "escaping" uitgevoerd.
diff --git a/includes/kohana/modules/userguide/guide/nl/tutorials.git.md b/includes/kohana/modules/userguide/guide/nl/tutorials.git.md
new file mode 100644
index 00000000..0595654b
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/tutorials.git.md
@@ -0,0 +1,149 @@
+# Werken met Git
+
+Kohana gebruikt [git](http://git-scm.com/) als versie controle systeem en [github](http://github.com/kohana) voor community-bijdragen. Deze tutorial zal je tonen hoe je beide platformen kunt gebruiken om een applicatie op te zetten.
+
+## Het installeren en instellen van Git op uw computer
+
+### Installeren van Git
+
+- OSX: [Git-OSX](http://code.google.com/p/git-osx-installer/)
+- Windows: [Msygit](http://code.google.com/p/msysgit/)
+- Of download het van de [git-site](http://git-scm.com/) en installeer het manueel (zie de git website)
+
+### Basis globale instellingen
+
+ git config --global user.name "Uw Naam"
+ git config --global user.email "uwemail@website.com"
+
+### Extra, maar aan te raden instellingen
+
+Om een beter visueel overzicht te hebben van de git commando's en repositories in je console stel je best volgende in:
+
+ git config --global color.diff auto
+ git config --global color.status auto
+ git config --global color.branch auto
+
+### Automatische aanvulling installeren
+
+[!!] Deze lijnen code zijn enkel van toepassing voor OSX
+
+Deze lijnen code doen al het vuile werk voor je zodat automatische aanvulling kan werken voor uw git-omgeving
+
+ 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
+
+### Gebruik altijd LF als regeleinden
+
+Dit is de conventie die we maken met Kohana. Stel deze instellingen voor uw eigen goed en vooral als je wilt bijdragen aan de Kohana community.
+
+ git config --global core.autocrlf input
+ git config --global core.savecrlf true
+
+[!!] Meer informatie over regeleinden kan je vinden op [github](http://help.github.com/dealing-with-lineendings/)
+
+### Meer informatie op je op weg te zetten
+
+- [Git Screencasts](http://www.gitcasts.com/)
+- [Git Reference](http://gitref.org/)
+- [Pro Git book](http://progit.org/book/)
+
+## Initile structuur
+
+[!!] Deze tutorial zal ervan uitgaan dat uw webserver al is ingesteld, en dat je een nieuwe applicatie zal maken op .
+
+Met behulp van je console, ga naar de lege map `gitorial` en voer `git init` uit. Dit zal een ruwe structuur voor een nieuwe git repository aanmaken.
+
+Vervolgend zullen we een [submodule](http://www.kernel.org/pub/software/scm/git/docs/git-submodule.html) maken voor de `system` folder. Ga naar en kopieer de "Clone URL":
+
+
+
+Gebruik nu de URL om de submodule aan te maken voor `system`:
+
+ git submodule add git://github.com/kohana/core.git system
+
+[!!] Dit creert een link naar de huidige ontwikkelingsversie voor de volgende stabiele uitgave. De ontwikkelingsversie is meestal veilig om te gebruiken, het heeft dezelfde API als de huidige stabiele download maar met bugfixes al toegepast.
+
+Voeg nu elke submodule toe dat je wil. Bijvoorbeeld als je de [Database] module nodig hebt:
+
+ git submodule add git://github.com/kohana/database.git modules/database
+
+Nadat de submodules zijn toegevoegd, moet je ze nog initialiseren:
+
+ git submodule init
+
+Nu dat de submodules zijn toegevoegd en geinitialiseerd, kan je ze commit'en:
+
+ git commit -m 'Added initial submodules'
+
+Vervolgens creren we de applicatie folder structuur. Hier is een absoluut minimum vereist:
+
+ mkdir -p application/classes/{controller,model}
+ mkdir -p application/{config,views}
+ mkdir -m 0777 -p application/{cache,logs}
+
+Als je nu `find application` uitvoert, moet je dit zien:
+
+ application
+ application/cache
+ application/config
+ application/classes
+ application/classes/controller
+ application/classes/model
+ application/logs
+ application/views
+
+We willen niet dat git de log of cache bestanden volgt dus voegen we een `.gitignore` bestand toe aan deze folders. Dit zal alle niet-verborgen bestanden negeren:
+
+ echo '[^.]*' > application/{logs,cache}/.gitignore
+
+[!!] Git negeert lege folders, dus het toevoegen van een `.gitignore` bestand zorgt er voor dat git de folder volgt maar niet de bestanden er in.
+
+Nu hebben we nog de `index.php` en `bootstrap.php` bestanden nodig:
+
+ 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 deze veranderingen ook:
+
+ git add application
+ git commit -m 'Added initial directory structure'
+
+Dit is alles wat je nodig hebt. Je hebt nu een applicatie dat Git gebruikt als versiesysteem.
+
+## Updaten van Submodules
+
+Op een gegeven moment zal je waarschijnlijk ook je submodules willen upgraden. Om al je submodules te updaten naar de laatste "HEAD" versie:
+
+ git submodule foreach 'git checkout master && git pull origin master'
+
+Om een enkele submodule te update, bijvoorbeel `system`:
+
+ cd system
+ git checkout master
+ git pull origin master
+ cd ..
+ git add system
+ git commit -m 'Updated system to latest version'
+
+Als je een enkele submodule wilt updaten naar een specifieke commit:
+
+ cd modules/database
+ git pull origin master
+ git checkout fbfdea919028b951c23c3d99d2bc1f5bbeda0c0b
+ cd ../..
+ git add database
+ git commit -m 'Updated database module'
+
+Merk op dat je ook een commit kunt uitchecken via een tag, zoals een officieel versie, bijvoorbeeld:
+
+ git checkout 3.0.6
+
+Voer gewoon `git tag` uit zonder parameters om een lijst van alle tags te krijgen.
+
+U weet nu "alles" over git!
diff --git a/includes/kohana/modules/userguide/guide/nl/tutorials.helloworld.md b/includes/kohana/modules/userguide/guide/nl/tutorials.helloworld.md
new file mode 100644
index 00000000..7392571b
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/tutorials.helloworld.md
@@ -0,0 +1,106 @@
+# Hello, World
+
+Aangezien bijna ieder framework een soort van "hello world" voorbeeld heeft, zou het onbeleefd van ons zijn om deze traditie te doorbreken!
+
+We gaan starten met het maken van een zeer basis hello world, om vervolgens uit te breiden om het MVC principe te volgen.
+
+## Tot op het bot
+
+Eerst moeten we een controller maken dat Kohana kan gebruiken om de request af te handelen.
+
+Maak het bestand `application/classes/controller/hello.php` in uw applicatie folder en zorg voor deze code erin:
+
+ template->message = 'hello, world!';
+ }
+ }
+
+`extends Controller_Template`
+: We breiden nu uit van de template controller, dit maakt het meer logisch om views te gebruiken in onze controller.
+
+`public $template = 'site';`
+: De template controller moet weten welke template we willen gebruiken. Het zal automatisch de view inladen die gedefinieerd is in deze variabele en het view object eraan toewijzen.
+
+`$this->template->message = 'hello, world!';`
+: `$this->template` is een referentie naar het view object voor onze site template. Wat we hier doen is een variabele "message", met waarde "hello, world", toewijzen aan de view.
+
+Laten we nu proberen onze code uit te voeren...
+
+
{{userguide/examples/hello_world_error}}
+
+Voor de één of andere reden geeft Kohana een error en toont het niet ons cool bericht.
+
+Als we kijken naar het error-bericht kunnen we zien dat de View library onze site template niet kon vinden, waarschijnlijk omdat we er nog geen aangemaakt hebben - *doh*!
+
+Laten we het view bestand `application/views/site.php` aanmaken voor ons bericht:
+
+
+
+ We've got a message for you!
+
+
+
+
+
We just wanted to say it! :)
+
+
+
+Als we de pagina vernieuwen dan kunnen we de vruchten zien van ons *zwaar" werk:
+
+
+
+## Stage 3 – Profit!
+
+In deze tutorial heb je geleerd hoe je een controller maakt en een view gebruikt om je logica te scheiden van het visuele.
+
+Dit is natuurlijk een zeer elementaire inleiding over het werken met Kohana en toont zelfs niet de sterkte van het framework voor wanneer je applicaties hiermee ontwikkelt.
diff --git a/includes/kohana/modules/userguide/guide/nl/tutorials.orm.md b/includes/kohana/modules/userguide/guide/nl/tutorials.orm.md
new file mode 100644
index 00000000..bc5be9f2
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/tutorials.orm.md
@@ -0,0 +1,298 @@
+# ORM {#top}
+
+Kohana 3.0 bevat een krachtige ORM-module die het "active record"-patroon gebruikt en database introspectie gebruikt om kolominformatie te bepalen van een model.
+
+De ORM-module is opgenomen in de Kohana 3.0 installatie maar moet worden ingeschakeld vooraleer je het kunt gebruiken. In je `application/bootstrap.php` bestand moet je de oproen naar [Kohana::modules] aanpassen en de ORM-module insluiten:
+
+ Kohana::modules(array(
+ ...
+ 'orm' => MODPATH.'orm',
+ ...
+ ));
+
+## Configuratie {#configuration}
+
+ORM vergt weinig configuratie om aan de slag te kunnen. Breid uw model classes uit met ORM om de module te kunnen gebruiken:
+
+ class Model_User extends ORM
+ {
+ ...
+ }
+
+In het voorbeeld hierboven zal het model zoeken naar een tabel `users` in de standaard database.
+
+### Model Configuratie Properties
+
+De volgende eigenschappen worden gebruikt om ieder model te configureren:
+
+Type | Eigenschap | Omschrijving | Standaard waarde
+----------|---------------------|--------------------------------------| -------------------------
+`string` | _table_name | Tabelnaam om te gebruiken | `singular model name`
+`string` | _db | Naam van de database om te gebruiken | `default`
+`string` | _primary_key | Kolom die dient als primary key | `id`
+`string` | _primary_val | Kolom die dient als primary value | `name`
+`bool` | _table_names_plural | Zijn de tabelnamen meervoudig? | `TRUE`
+`array` | _sorting | Array met kolom => volgorde | `primary key => ASC`
+`string` | _foreign_key_suffix | Achtervoegsel voor foreign keys | `_id`
+
+## Het gebruik van ORM
+
+### Een Record inladen
+
+Om een instantie van een model aan te maken, kan je de [ORM::factory] methode of [ORM::__construct] gebruiken:
+
+ $user = ORM::factory('user');
+ // of
+ $user = new Model_User();
+
+De constructor en factory methodes accepteren ook een primary key waarde om het gegeven model's data in te laden:
+
+ // Laad gebruiker met ID 5
+ $user = ORM::factory('user', 5);
+
+ // Kijk of de gebruiker succesvol werd ingeladen
+ if ($user->loaded()) { ... }
+
+Je kan optioneel een array met keys => value paren meegeven om een data object in te laden die voldoet aan de gegeven criteria:
+
+ // Laad een gebruiker met email joe@example.com
+ $user = ORM::factory('user', array('email' => 'joe@example.com'));
+
+### Zoeken naar één of meerdere records
+
+ORM ondersteunt de meeste krachtige [Database] methoden voor het doorzoeken van gegevens van uw model. Zie de `_db_methods` eigenschap voor een volledige lijst van ondersteunde methode oproepen. Records worden opgehaald met behulp van de [ORM::find] en [ORM::find_all] functies.
+
+ // Dit zal de eerste actieve gebruiker nemen met de naam Bob
+ $user = ORM::factory('user')
+ ->where('active', '=', TRUE)
+ ->where('name', '=', 'Bob')
+ ->find();
+
+ // Dit zal alle gebruikers nemen met de naam Bob
+ $users = ORM::factory('user')
+ ->where('name', '=', 'Bob')
+ ->find_all();
+
+Wanneer je een lijst van modellen ontvangt met behulp van [ORM::find_all], kan je deze doorlopen zoals je doet met database resultaten:
+
+ foreach ($users as $user)
+ {
+ ...
+ }
+
+Een zeer handige functie van ORM is de [ORM::as_array] methode die het record zal teruggeven als een array. Indien je dit gebruikt met [ORM::find_all], zal een array van alle records worden teruggegeven. Een goed voorbeeld van wanneer dit nuttig is, is voor select in het HTML formulier:
+
+ // Toon een dropdown/select met daarin alle gebruikersnamen (id als value van de options)
+ echo Form::select('user', ORM::factory('user')->find_all()->as_array('id', 'username'));
+
+### Het aantal records tellen
+
+Gebruik [ORM::count_all] om het aantal records terug te geven voor een bepaalde query.
+
+ // Aantal actieve gebruikers
+ $count = ORM::factory('user')->where('active', '=', TRUE)->count_all();
+
+Als je het totaal aantal gebruikers wilt tellen voor een bepaalde query, terwijl je enkel een bepaalde set van deze gebruikers wilt tonen, gebruik dan de [ORM::reset] methode met `FALSE` vooraleer je `count_all` gebruikt:
+
+ $user = ORM::factory('user');
+
+ // Totaal aantal gebruikers (reset FALSE zorgt ervoor dat het query object dat het query object niet geleegd wordt)
+ $count = $user->where('active', '=', TRUE)->reset(FALSE)->count_all();
+
+ // Geef enkel de eerste 10 resultaten terug van deze resultaten
+ $users = $user->limit(10)->find_all();
+
+### Properties van een model aanspreken
+
+Alle model properties zijn toegankelijk via de `__get` en `__set` magic methodes.
+
+ $user = ORM::factory('user', 5);
+
+ // Geef de gebruikersnaam terug
+ echo $user->name;
+
+ // Verander de gebruiker zijn naam
+ $user->name = 'Bob';
+
+Voor het opslaan van gegevens/properties die niet bestaan in de tabel van het model, kan je gebruik maken van het `_ignored_columns` data member. De gegevens zullen worden opgeslagen in het interne `_object` member, maar zal worden genegeerd op database-niveau.
+
+ class Model_User extends ORM
+ {
+ ...
+ protected $_ignored_columns = array('field1', 'field2', …);
+ ...
+ }
+
+Meerdere key => value paren kunnen worden ingesteld door gebruik te maken van de [ORM::values] methode.
+
+ $user->values(array('username' => 'Joe', 'password' => 'bob'));
+
+### Aanmaken en opslaan van records
+
+De methode [ORM::save] wordt gebruikt om zowel nieuwe records aan te maken als het upaten van bestaande.
+
+ // Nieuw record aanmaken
+ $user = ORM::factory('user');
+ $user->name = 'Nieuwe gebruiker';
+ $user->save();
+
+ // Aanpassen van een bestaand record
+ $user = ORM::factory('user', 5);
+ $user->name = 'Gebruiker 2';
+ $user->save();
+
+ // Controleer of het record opgeslagen is
+ if ($user->saved()) { ... }
+
+Je kan meerdere records tegelijk veranderen met de [ORM::save_all] methode:
+
+ $user = ORM::factory('user');
+ $user->name = 'Bob';
+
+ // Verander bij alle actieve gebruikers de naam naar 'Bob'
+ $user->where('active', '=', TRUE)->save_all();
+
+#### Gebruik `Updated` en `Created` kolommen
+
+De `_updated_column` en `_created_column` members staan ter beschikking om automatisch aangepast te worden wanneer een model wordt gecreëerd of aangepast. Ze worden standaard niet gebruikt. Om ze te gebruiken:
+
+ // date_created is de kolom die wordt gebruikt om de aanmaak datum op te slaan. Gebruik format => TRUE om een timestamp op te slaan
+ protected $_created_column = array('date_created', 'format' => TRUE);
+
+ // date_modified is de kolom die wordt gebruikt om de datum op te slaan wanneer het item is aangepast. In dit geval wordt een string gebruikt om een date() formaat te specificeren
+ protected $_updated_column = array('date_modified', 'format' => 'm/d/Y');
+
+### Verwijderen van records
+
+Records worden verwijderd met [ORM::delete] en [ORM::delete_all]. Deze methoden werken op dezelfde manier als het opslaan van records zoals hierboven beschreven, met de uitzondering dat [ORM::delete] nog een optionele parameter heeft, het `id` van het record om te verwijderen. Anders wordt het huidig ingeladen record verwijderd.
+
+### Relaties
+
+ORM ondersteunt zeer goed relateies. Ruby heeft een [goede tutorial omtrent relaties](http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html).
+
+#### Belongs-To en Has-Many
+
+We gaan er van uit dat we werken met een school dat veel (has many) studenten heeft. Iedere student kan enkel maar tot één school behoren (belong to). Dan zullen de relaties als volgt gedefinieerd worden:
+
+ // In het model "school"
+ protected $_has_many = array('students' => array());
+
+ // In het model "student"
+ protected $_belongs_to = array('school' => array());
+
+Om een student zijn school te verkrijgen gebruik je:
+
+ $school = $student->school;
+
+Om een school zijn studenten te verkrijgen gebruik je:
+
+ // Merk op dat find_all is vereist na "students"
+ $students = $school->students->find_all();
+
+ // Om resultaten te "filteren":
+ $students = $school->students->where('active', '=', TRUE)->find_all();
+
+Standaard zal ORM willen zoeken naar een `school_id` model in de studenten tabel. Dit kan worden overschreven door gebruik te maken van het `foreign_key` attribuut:
+
+ protected $_belongs_to = array('school' => array('foreign_key' => 'schoolID'));
+
+De foreign key moet overschreven worden in zowel het student als school model.
+
+#### Has-One
+
+Has-One is een speciale versie van Has-Many, het enige verschil is dat er maar één enkel record is. In het bovenstaande voorbeeld zou iedere school maar één student hebben (al is dat wel een slecht voorbeeld).
+
+ // In het model "school"
+ protected $_has_one = array('student' => array());
+
+Je moet niet zoals bij Belongs-To de `find` methode gebruiken wanneer je verwijst naar een het Has-One gerelateerd object, dit gebeurt automatisch.
+
+#### Has-Many "Through"
+
+De Has-Many "through" relatie (ook bekend als Has-And-Belongs-To-Many) wordt gebruikt in het geval dat één object gerelateerd is met meerdere objecten van verschillende types en omgekeerd. Bijvoorbeeld, een student kan verschillende klassen volgen en een klass kan verschillende studenten hebben. In dit geval wordt een derde tabel gebruikt en een model die dienst doet als `pivot`. In dit geval noemen we het pivot object/model `enrollment` (=inschrijving).
+
+ // In het model "student"
+ protected $_has_many = array('classes' => array('through' => 'enrollment'));
+
+ // In het model "class"
+ protected $_has_many = array('students' => array('through' => 'enrollment'));
+
+De inschrijvingstabel (`enrollment`) moet twee foreign keys hebben, een voor `class_id` en de andere voor `student_id`. Deze kunnen worden overschreven door `foreign_key` en `far_key` te gebruiken bij het definiëren van de relatie. Bijvoorbeeld:
+
+ // In het model "student" (de foreign key verwijst naar dit model [student], terwijl de far key verwijst naar het andere model [class])
+ protected $_has_many = array('classes' => array('through' => 'enrollment', 'foreign_key' => 'studentID', 'far_key' => 'classID'));
+
+ // In het model "class"
+ protected $_has_many = array('students' => array('through' => 'enrollment', 'foreign_key' => 'classID', 'far_key' => 'studentID'));
+
+Het inschrijvings model (enrollment) zal als volgt gedefinieerd worden:
+
+ // Het model "enrollment" hoort bij zowel "student" als "class"
+ protected $_belongs_to = array('student' => array(), 'class' => array());
+
+Om de gerelateerde objecten te bereiken, gebruik je:
+
+ // Om de klassen van een student te verkrijgen
+ $student->classes->find_all();
+
+ // Om studenten te verkrijven vanuit de klas
+ $class->students->find_all();
+
+### Validatie
+
+ORM werkt nauw samen met de [Validate] library. ORM biedt de volgende members aan voor validatie
+
+* _rules
+* _callbacks
+* _filters
+* _labels
+
+#### `_rules`
+
+ protected $_rules = array
+ (
+ 'username' => array('not_empty' => array()),
+ 'email' => array('not_empty' => array(), 'email' => array()),
+ );
+
+`username` zal gecontroleerd worden om zeker niet leeg te zijn. `email` zal ook gecontroleerd worden om te verzekeren dat het een geldig emailadres is. De lege arrays die als values worden meegestuurd, kunnen worden gebruikt om optionele parameters mee te geven aan deze functie aanroepen.
+
+#### `_callbacks`
+
+ protected $_callbacks = array
+ (
+ 'username' => array('username_unique'),
+ );
+
+`username` zal worden meegestuurd naar een callback methode `username_unique`. Als de methode bestaat in het huidige model, zal het worden gebruikt, anders zal een globale functie worden opgeroepen. Hier is een voorbeeld van z'n methode:
+
+ public function username_unique(Validate $data, $field)
+ {
+ // Logica om te controleren of de gebruikersnaam uniek is
+ ...
+ }
+
+#### `_filters`
+
+ protected $_filters = array
+ (
+ TRUE => array('trim' => array()),
+ 'username' => array('stripslashes' => array()),
+ );
+
+`TRUE` slaat erop dat de `trim` filter wordt gebruikt voor alle velden. `username` zal ook gefilterd worden door `stripslashes` vooraleer het gevalideerd wordt. De lege arrays die als values worden meegestuurd, kunnen worden gebruikt om optionele parameters mee te geven aan deze filter-functie aanroepen.
+
+#### Controleren of een Object Valid is
+
+Gebruik [ORM::check] om te kijken of het object momenteel valid is.
+
+ // Een object zijn values instellen en dan controleren of het valid is
+ if ($user->values($_POST)->check())
+ {
+ $user->save();
+ }
+
+Je kan de `validate()` methode gebruiken om een model zijn validatie object aan te roepen.
+
+ // Een optionele filter manueel toevoegen
+ $user->validate()->filter('username', 'trim');
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/nl/tutorials.removeindex.md b/includes/kohana/modules/userguide/guide/nl/tutorials.removeindex.md
new file mode 100644
index 00000000..dc8864c5
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/tutorials.removeindex.md
@@ -0,0 +1,88 @@
+# `index.php` verwijderen uit de URL
+
+Om uw URLs proper te houden, wil je hoogtswaarschijnlijk je applicatie kunnen benaderen zonder /index.php/` in uw URL te gebruiken. Er zijn twee stappen om `index.php` te verwijderen uit de URL.
+
+1. Het bootstrap bestand aanpassen
+2. Herschrijven van URL's instellen
+
+# Configuratie van de Bootstrap
+
+Het eerste dat je moet veranderen is de `index_file` instelling van [Kohana::init]:
+
+ Kohana::init(array(
+ 'base_url' => '/myapp/',
+ 'index_file' => FALSE,
+ ));
+
+Nu zullen alle links die gegeneerd worden met [URL::site], [URL::base] en [HTML::anchor] niet meer "index.php" gebruiken in de URL. Alle gegenereerde links zullen starten met `/myapp/` in plaats van `/myapp/index.php/`.
+
+# URL Herschrijven
+
+Het herschrijven van URL kan verschillen, naargelang je web server.
+
+## Apache
+
+Hernoem `example.htaccess` naar `.htaccess` en verander de volgende regel code:
+
+ RewriteBase /kohana/
+
+Dit moet gelijk zijn met de `base_url` instelling van [Kohana::init]:
+
+ RewriteBase /myapp/
+
+In de meeste gevallen is dit het enige dat je moet veranderen.
+
+### Er loopt iets fout!
+
+Als je een "Internal Server Error" of "No input file specified" error krijgt, probeer dan hetvolgende te veranderen:
+
+ RewriteRule ^(?:application|modules|system)\b - [F,L]
+
+Door enkel een slash te gebruiken:
+
+ RewriteRule ^(application|modules|system)/ - [F,L]
+
+Als het nog steeds niet werkt, probeer dan hetvolgende te veranderen:
+
+ RewriteRule .* index.php/$0 [PT]
+
+Naar iets simpeler:
+
+ RewriteRule .* index.php [PT]
+
+### Nog steeds niet loopt het fout!
+
+Als je nog steeds fouten krijgt, controleer dan zeker dat je host wel URL `mod_rewrite` ondersteund. Als je de Apache configuratie kunt aanpassen, voeg dan deze lijnen toe aan de configuratie, meestal in `httpd.conf`:
+
+
+ Order allow,deny
+ Allow from all
+ AllowOverride All
+
+
+## NGINX
+
+Het is moeilijk om voorbeelden te geven van een nginx configuratie, maar hier is een voorbeeld voor een 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;
+ }
+
+Er zijn twee dingen te melden: het gebruik van [try_files](http://wiki.nginx.org/NginxHttpCoreModule#try_files) en [fastcgi_split_path_info](http://wiki.nginx.org/NginxHttpFcgiModule#fastcgi_split_path_info).
+
+[!!] Dit in de veronderstelling dat je PHP draait als een FastCGI server op poort 9000 en dat je nginx v0.7.31 of later gebruikt.
+
+Als je problemen hebt om dit te laten werken, zet dan het deub level logging aan in nginx en controleer de toegangs- en foutenlogs.
diff --git a/includes/kohana/modules/userguide/guide/nl/tutorials.urls.md b/includes/kohana/modules/userguide/guide/nl/tutorials.urls.md
new file mode 100644
index 00000000..e13ea751
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/tutorials.urls.md
@@ -0,0 +1,159 @@
+# Routes, URLs en Links
+
+Dit onderdeel zal je een basis idee geven achter Kohana's request routing, de generatie van url's en links.
+
+## Routing
+
+Zoals gezegd in de [Request Flow](about.flow) sectie, wordt een request afgehandeld door de [Request] class die een juiste [Route] vindt en de juiste controller inlaadt om het request af te handelen. Dit systeem biedt veel flexibiliteit en een logische manier van werken.
+
+Als je kijkt in `APPPATH/bootstrap.php` zal je zien dat de volgende code onmiddelijk wordt aangeroepen vooraleer de request wordt toegewezen aan [Request::instance]:
+
+ Route::set('default', '((/(/)))')
+ ->defaults(array(
+ 'controller' => 'welcome',
+ 'action' => 'index',
+ ));
+
+Dit stelt de `default` route in voor een uri met het formaat `((/(/)))`. De karakters omringd met `<>` zijn *keys* en de karakters omringd met `()` zijn optionele onderdelen van de uri. In dit geval is de gehele uri optioneel, zodat bij een lege uri de standaard controller en actie worden uitgevoerd wat ervoor zou zorgen dat de `Controller_Welcome` class wordt ingeladen en eventueel wordt de methode `action_index` aangeroepen om de request af te handelen.
+
+Merk op dat in Kohana routes, alle karakters zijn toegestaan behalve `()<>` en de `/`, die hebben namelijk een speciale betekenis. In de standaard route wordt de "/" gebruikt als scheidingsteken, maar zolang de reguliere expressie logisch en doordacht is, kan je kiezen hoe je routes er laat uitzien.
+
+### Folders
+
+Om je controllers wat meer te gaan organiseren kan je ervoor kiezen om ze te plaatsen in subfolders. Een veel voorkomend geval is voor een backend van je website:
+
+ Route::set('admin', 'admin(/(/(/)))')
+ ->defaults(array(
+ 'directory' => 'admin',
+ 'controller' => 'home',
+ 'action' => 'index',
+ ));
+
+Deze route vereist dat de uri moet beginnen met `admin` en dat de folder statisch wordt toegewezen aan `admin` in de standaard instellingen van de route. Een request naar `admin/users/create` zal nu de `Controller_Admin_Users` class laden en de methode `action_create` aanroepen.
+
+### Patronen
+
+Het Kohana route systeem gebruikt perl compatibele reguliere expressies in zijn vergelijkings proces. Standaar worden de *keys* (omringd door `<>`) vergeleken met `[a-zA-Z0-9_]++` maar je kan je eigen patronen definiren voor elke key door een associatieve array mee te geven als extra argument aan [Route::set] met daarin de keys and patronen. We kunnen het vorige voorbeeld uitbreiden met een admin sectie en een filialen (affliates) sectie. Je kan deze in verschillende routes specificeren of je kan iets doen zoals dit:
+
+ Route::set('sections', '(/(/(/)))',
+ array(
+ 'directory' => '(admin|affiliate)'
+ ))
+ ->defaults(array(
+ 'controller' => 'home',
+ 'action' => 'index',
+ ));
+
+Dit zorgt voor twee secties van uw site, 'admin' en 'affiliate', deze laten je toe om de controllers te organiseren in subfolders voor elk maar dat ze nog steeds blijven werken als de standaard route.
+
+### Meer Route voorbeelden
+
+Er zijn oneindig veel andere mogelijkheden voor routes. Hier zijn er enkele:
+
+ /*
+ * Authenticatie
+ */
+ Route::set('auth', '',
+ array(
+ 'action' => '(login|logout)'
+ ))
+ ->defaults(array(
+ 'controller' => 'auth'
+ ));
+
+ /*
+ * Multi-formaat feeds
+ * 452346/comments.rss
+ * 5373.json
+ */
+ Route::set('feeds', '(/).',
+ array(
+ 'user_id' => '\d+',
+ 'format' => '(rss|atom|json)',
+ ))
+ ->defaults(array(
+ 'controller' => 'feeds',
+ 'action' => 'status',
+ ));
+
+ /*
+ * Statische pagina's
+ */
+ Route::set('static', '.html',
+ array(
+ 'path' => '[a-zA-Z0-9_/]+',
+ ))
+ ->defaults(array(
+ 'controller' => 'static',
+ 'action' => 'index',
+ ));
+
+ /*
+ * Je houdt niet van slashes?
+ * EditGallery:bahamas
+ * Watch:wakeboarding
+ */
+ Route::set('gallery', '():',
+ array(
+ 'controller' => '[A-Z][a-z]++',
+ 'action' => '[A-Z][a-z]++',
+ ))
+ ->defaults(array(
+ 'controller' => 'Slideshow',
+ ));
+
+ /*
+ * Vlug zoeken
+ */
+ Route::set('search', ':', array('query' => '.*'))
+ ->defaults(array(
+ 'controller' => 'search',
+ 'action' => 'index',
+ ));
+
+Routes worden vergeleken in de gespecifieerde volgorde dus wees er van bewust dat als je routes insteld nadat de modules zijn ingeladen, een module een route kan specifiren dat voor een conflict zorgt met een route van jezelf. Dit is ook de reden waarom de standaard route als laatste wordt ingesteld, zodat zelfgeschreven routes eerst worden getest.
+
+### Request Parameters
+
+De directory, controller en action kunnen worden benaderd via de [Request] instantie op de volgende manieren:
+
+ $this->request->action;
+ Request::instance()->action;
+
+Alle andere gespecifieerde keys in een route kunnen worden benaderd van binnenin de controller via:
+
+ $this->request->param('key_name');
+
+De [Request::param] methode heeft een optioneel tweede argument om een standaard waarde terug te geven indien de key niet is ingesteld door de route. Indien er geen argumenten worden gegeven, worden alle keys als teruggegeven als een associatieve array.
+
+### Conventie
+
+De gebruikelijke conventie is je eigen routes te plaatsen in het `MODPATH//init.php` bestand van je module als de routes bij een module horen, of gewoonweg te plaatsen in het `APPPATH/bootstrap.php` bestand boven de standaard route als de routes specifiek voor de applicatie zijn. Natuurlijk kunnen ze ook worden geimporteerd vanuit een extern bestand of zelfs dynamisch gegenereerd worden.
+
+## URLs
+
+Naast Kohana's sterke routing mogelijkheden zitten er ook enkele methodes in om URLs te genereren voor je routes' uris. Je kan je uris altijd specificeren als een string door gebruik te maken van [URL::site] om een volledige URL te maken:
+
+ URL::site('admin/edit/user/'.$user_id);
+
+Kohana biedt echter ook een methode om de URI genereren op basis van de route's definitie. Dit is zeer handig als je routing ooit zou veranderen omdat het je zou verlossen van om overal uw code te veranderen waar je de URI als string hebt gespecificeerd. Hier is een voorbeeld van dynamische generatie die overeenkomt met het `feeds`-route voorbeeld van hierboven:
+
+ Route::get('feeds')->uri(array(
+ 'user_id' => $user_id,
+ 'action' => 'comments',
+ 'format' => 'rss'
+ ));
+
+Laten we zeggen dat je later zou besluiten om die route definitie meer verstaanbaar te maken door ze te veranderen in `feeds/(/).`. Wanneer je je code hebt geschreven met de uri generatie methode van hierboven dan zal je niets moeten veranderen aan je code! Wanneer een deel van de URI tussen haakjes staat en waarvoor er geen waarde is meegegeven voor uri generatie en er geen standaard waarde is meegegeven in de route, dan zal dat stuk verwijderd worden van de uri. Een voorbeeld hiervan is het `(/)` deel van de standaard route, dit zal niet worden opgenomen in de gegenereerde uri als er geen id is voorzien.
+
+De methode [Request::uri] zal er n zijn dat je regelmatig zult gebruiken, het heeft dezelfde functionaliteit als hierboven maar het gaat gebruikt de huidige route, directory, controller en action. Als onze huidige route de standaard route is en de uri `users/list` is, dan kunnen we het volgende doen om uris te genereren in het formaat `users/view/$id`:
+
+ $this->request->uri(array('action' => 'view', 'id' => $user_id));
+
+Of een meer aangeraden methode voor in een view:
+
+ Request::instance()->uri(array('action' => 'view', 'id' => $user_id));
+
+## Links
+
+[!!] Nog geen informatie beschikbaar.
diff --git a/includes/kohana/modules/userguide/guide/nl/using.autoloading.md b/includes/kohana/modules/userguide/guide/nl/using.autoloading.md
new file mode 100644
index 00000000..e92d5831
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/using.autoloading.md
@@ -0,0 +1,95 @@
+# Laden van Classes
+
+Kohana maakt dankbaar gebruik van PHP [autoloading](http://php.net/manual/language.oop5.autoload.php). Dit zorgt ervoor dat je niet [include](http://php.net/include) of [require](http://php.net/require) moet gebruiken vooraleer je de klasse kunt gebruiken. Bijvoorbeeld als je de [Cookie::set] method wilt gebruiken doe je:
+
+ Cookie::set('mycookie', 'any string value');
+
+Of om een [Encrypt] instantie in te laten, gewoon [Encrypt::instance] aanroepen:
+
+ $encrypt = Encrypt::instance();
+
+Classes worden ingeladen via de [Kohana::auto_load] methode, deze maakt een simpele conversie van de class naam naar de naam van het bestand:
+
+1. Classes worden geplaatst in de `classes/` folder van het [bestandssysteem](about.filesystem)
+2. Ieder underscore karakter wordt omgezet naar een slash.
+2. De bestandsnaam is met kleine letters
+
+Wanneer je een class aanroept die nog niet is ingeladen (vb. `Session_Cookie`), zal Kohana zoeken in het bestandssysteem via [Kohana::find_file] voor een bestand met de naam `classes/session/cookie.php`.
+
+## Zelfgeschreven Autoloaders
+
+De standaard autoloader wordt ingesteld in `application/bootstrap.php` via [spl_autoload_register](http://php.net/spl_autoload_register):
+
+ spl_autoload_register(array('Kohana', 'auto_load'));
+
+Dit laat [Kohana::auto_load] toe om te proberen eender welke class in te laden dat nog niet bestaat wanneer de class voor het eerst wordt gebruikt.
+
+# Transparante Class Uitbreiding {#class-extension}
+
+Het [cascading bestandssyteem](about.filesystem) laat transparante class uitbreiding toe. Bijvoorbeeld, de class [Cookie] is gedefinieerd in `SYSPATH/classes/cookie.php` als:
+
+ class Cookie extends Kohana_Cookie {}
+
+De standaard Kohana classes, en vele uitbreidingen, gebruiken deze manier van definiren zodat bijna alle classes kunnen worden uitgebreid. Je kan elke class transparant uitbreiden, door een eigen class te definiren in `APPPATH/classes/cookie.php` om je eigen methodes toe te voegen.
+
+[!!] Je past best **nooit** bestanden aan die standaard in Kohana zitten. Maak aanpassingen aan classes altijd door ze uit te breiden om upgrade-problemen te vermijden.
+
+Bijvoorbeeld, als je een methode wilt maken dat gecodeerde cookies maakt via de [Encrypt] class:
+
+ encode((string) $value);
+
+ parent::set($name, $value, $expiration);
+ }
+
+ /**
+ * Krijg de inhoud van een gecodeerde 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
+
+Als je nu `Cookie::encrypt('secret', $data)` aanroept zal die een een gecodeerde cookie aanmaken die je kan decoderen met `$data = Cookie::decrypt('secret')`.
+
+## Meerdere niveau's van uitbreidingen {#multiple-extensions}
+
+Als je een Kohana class in een module uitbreidt, maak je best gebruik van transparante uitbreidingen. In plaats van de [Cookie] uitbreiding Kohana te laten uitbreiden, kan je `MODPATH/mymod/encrypted/cookie.php` aanmaken:
+
+ class Encrypted_Cookie extends Kohana_Cookie {
+
+ // Gebruik de encrypt() en decrypt() methodes van hierboven
+
+ }
+
+En maak `MODPATH/mymod/cookie.php` aan:
+
+ class Cookie extends Encrypted_Cookie {}
+
+Dit laat nog steeds toe om gebruikers hun eigen uitbreidingen te laten doen op [Cookie] zodat jouw uitbreidingen nog behouden blijven. Let wel, de volgende uitbreiding van [Cookie] zal `Encrypted_Cookie` moeten uitbreiden in plaats van `Kohana_Cookie`.
diff --git a/includes/kohana/modules/userguide/guide/nl/using.configuration.md b/includes/kohana/modules/userguide/guide/nl/using.configuration.md
new file mode 100644
index 00000000..c9aa35db
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/using.configuration.md
@@ -0,0 +1,57 @@
+# Algemene Configuratie
+
+Kohana gebruikt zowel static properties als bestanden worden gebruikt voor de configuratie. Static properties zijn worden meestal gebruikt voor static classes, zoals [Cookie], [Security] en [Upload]. Bestanden worden meestal gebruikt voor objecten zoals [Database], [Encrypt] en [Session].
+
+Static properties kunnen ingesteld worden in `APPPATH/bootstrap.php` of door [class uitbreding](using.autoloading#class-extension). Het voordeel van static properties is dat er geen extra bestanden moeten worden ingeladen. Het probleem met deze methode is dat de class ingeladen word wanneer een property is ingesteld, als je geen uitbreiding gebruikt. Echter, met gebruik van uitbreidingen worden uitbreidingen uit modules overladen. Het is aanbevolen om static property te gebruiken voor configuraties in de bootstrap.
+
+[!!] Wanneer je opcode caching gebruikt, zoals [APC](http://php.net/apc) of [eAccelerator](http://eaccelerator.net/), dan is het inladen van classes merkbaar vermindert. Het is dan ook streng aanbevolen om opcode caching te bruiken bij *elke* website in productie, of die nu groot of klein is.
+
+## Noodzakelijke instellingen
+
+Bij iedere nieuwe Kohana installatie is het vereist om de [Kohana::init] instellingen aan te passen in `APPPATH/bootstrap.php`. Iedere instelling die niet specifiek is ingesteld zal de standaard instelling gebruiken. Deze instellingen kunnen aangeroepen worden en/of aangepast worden op een later tijdstip door de static property van de [Kohana] class te gebruiken. Bijvoorbeeld, om de huidige karakterset te verkrijgen lees je de [Kohana::$charset] property in.
+
+## Veiligheids instellingen
+
+Er zijn verschillende instellingen dat je moet veranden om Kohana veilig te maken. De belangrijkste is [Cookie::$salt], deze wordt gebruikt om een "handtekening" te maken op cookies zodat ze niet kunnen worden aangepast van buiten Kohana.
+
+Als je de [Encrypt] class wilt gebruiken, maak je best ook een `encrypt` configuratie bestand en stel je een encryption `key` in. Deze key bevat best letters, nummers en symbolen om de veiligheid te optimaliseren.
+
+[!!] **Gebruik geen hash als encryption key!** Indien je dit doet zal de encryption key gemakkelijker te kraken zijn.
+
+# Configuratie bestanden {#config-files}
+
+Configuratie bestanden zijn licht anders dan andere bestanden in het [cascading bestandssyteem](about.filesystem). Configuratie bestanden worden **gemerged** in plaats van overladen. Dit wil zeggen dat alle configuratie bestanden hetzelfde path worden gecombineerd om configuratie te vormen. Wat wil zeggen dat je *individuele* instellingen kan overladen in plaats van een volledig bestand te dupliceren.
+
+Configuratie bestanden zijn pure PHP bestanden, opgeslaan in de `config/` folder, die een associatieve array teruggeven:
+
+ 'value',
+ 'options' => array(
+ 'foo' => 'bar',
+ ),
+ );
+
+Als het bovenstaande bestand `myconf.php` werd genoemd, dan kon je deze benaderen via:
+
+ $config = Kohana::config('myconf');
+ $options = $config['options'];
+
+[Kohana::config] biedt ook een shortcut om individuele keys van configuratie arrays te benaderen door gebruik te maken van "dot paths".
+
+Verkrijg de "options" array:
+
+ $options = Kohana::config('myconf.options');
+
+Verkrijg de "foo" key van de "options" array:
+
+ $foo = Kohana::config('myconf.options.foo');
+
+Configuratie arrays kunnen ook worden benaderd als objecten, indien je deze manier wilt gebruiken:
+
+ $options = Kohana::config('myconf')->options;
+
+Let wel, je kan enkel keys op het bovenste niveau aanspreken als object properties, alle lagere keys moeten benaderd worden via de standaard array syntax:
+
+ $foo = Kohana::config('myconf')->options['foo'];
diff --git a/includes/kohana/modules/userguide/guide/nl/using.messages.md b/includes/kohana/modules/userguide/guide/nl/using.messages.md
new file mode 100644
index 00000000..2ba9a715
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/using.messages.md
@@ -0,0 +1,26 @@
+# Berichten (Messages): de basis
+
+Kohana berichten zijn mensvriendelijke stukjes tekst voorgesteld door een korter woord of zin, een "key" genaamd. Berichten worden benaderd via de [Kohana::message] methode, die n enkel of een hele groep van berichten teruggeeft.
+
+Bijvoorbeeld, als een gebruiker niet is ingelogd en een pagina dat authenticatie vereist probeert te benaderen, dan moet een error zoals "U moet ingelogd zijn om toegang te hebben tot deze pagina" getoond worden. Dit bericht kan opgeslagen worden in het `auth` bestand met een `must_login` key:
+
+ $message = Kohana::message('auth', 'must_login');
+
+Berichten worden niet vertaald. Om een bericht te vertalen, gebruik dan de [translation function](using.translation):
+
+ $translated = __(Kohana::message('auth', 'must_login'));
+
+[!!] In Kohana v2 werd het berichten-systeem gebruikt voor vertalingen. Echter is het ten zeerste aanbevolen om het nieuwe vertalingssysteem te gebruiken in plaats van berichten, aangezien het leesbare tekst teruggeeft wanneer zelfs geen vertaling beschikbaar is.
+
+## Berichten: de bestanden
+
+Alle berichten bestanden zijn pure PHP files, opgeslaan in de `messages/` folder, die een associatieve array teruggeven:
+
+ 'U moet ingelogd zijn om toegang te hebben tot deze pagina',
+ 'no_access' => 'U heeft geen bevoegdheden om deze pagina te bekijken',
+ );
+
+Berichten bestanden zijn gelijkaardig aan [configuratie bestanden](using.configuration#config-files) omdat ze ook worden samengevoegd. Dit betekent dat alle berichten die opgeslaan zijn in het bestand `auth` zullen worden gecombineerd in n enkele array, het is dus niet noodzakelijk om alle berichten te kopiren wanneer je een nieuw `auth` bestand aanmaakt.
diff --git a/includes/kohana/modules/userguide/guide/nl/using.sessions.md b/includes/kohana/modules/userguide/guide/nl/using.sessions.md
new file mode 100644
index 00000000..cdc4b178
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/using.sessions.md
@@ -0,0 +1,223 @@
+# Gebruik van Sessies en Cookies
+
+Kohana biedt een paar classes die het gemakkelijk maken om te werken met cookies en sessies. Op een hoog niveau, zowel sessies en cookies geven dezelfde functionaliteit. Ze laten de ontwikkelaar toe om tijdelijke of blijvende informatie over een specifieke klant voor later op te slaan.
+
+Cookies moeten worden gebruikt voor de opslag van niet-private gegevens die persistent is voor een lange periode van tijd. Bijvoorbeeld het opslaan van een gebruikers-id of een taalvoorkeur. Gebruik de [Cookie] class voor het verkrijgen en instellen van cookies.
+
+[!!] Kohana gebruikt "ondertekende" cookies. Elke cookie die wordt opgeslagen wordt gecombineerd met een veilige hash om een wijziging van de cookie te voorkomen. Deze hash wordt gegenereerd met behulp van [Cookie:: salt], die de [Cookie::$salt] property gebruikt. Je moet [deze instelling] (using.configuration) veranderen wanneer je applicatie live staat.
+
+Sessies worden gebruikt voor het opslaan van tijdelijke of prive-gegevens. Zeer gevoelige gegevens moeten worden opgeslagen met behulp van de [Session] class met de "database" of "native" adapters. Bij gebruik van de "cookie"-adapter, moet de sessie altijd worden versleuteld.
+
+[!!] Voor meer informatie over de beste manieren van werken met sessie-variabelen, zie [the seven deadly sins of sessions](http://lists.nyphp.org/pipermail/talk/2006-December/020358.html).
+
+# Het opslaan, ophalen en verwijderen van gegevens
+
+[Cookie] en [Session] bieden een zeer gelijkaardige API voor het opslaan van gegevens. Het belangrijkste verschil tussen hen is dat sessies benaderd kunnen worden met behulp van een object, en cookies met behulp van een statische class.
+
+De sessie instantie benaderen wordt gedaan met de [Session::instance] methode:
+
+ // Verkrijg de sessie instantie
+ $session = Session::instance();
+
+Bij het gebruik van sessies, kan je alle huidige sessiegegevens krijgen met behulp van de [Session::as_array] methode:
+
+ // Verkrijg alle sessiegegevens als een array
+ $data = $session->as_array();
+
+Je kan dit ook gebruiken om de `$_SESSION` global te overladen om data te krijgen en in te stellen in verlijkbare manier zoals standaard PHP:
+
+ // Overlaad $_SESSION met sessiegegevens
+ $_SESSION =& $session->as_array();
+
+ // Stel de sessiegegevens in
+ $_SESSION[$key] = $value;
+
+## Gegevens opslaan {#setting}
+
+Het opslaan van sessie- of cookie-gegevens wordt gedaan met behulp van de `set`-methode:
+
+ // Sla sessiegegevens op
+ $session->set($key, $value);
+
+ // Sla cookiegegevens op
+ Cookie::set($key, $value);
+
+ // Sla een gebruikers id op
+ $session->set('user_id', 10);
+ Cookie::set('user_id', 10);
+
+## Verkrijgen van gegevens {#getting}
+
+Verkrijgen van sessie- of cookie-gegevens wordt gedaan met behulp van de `get`-methode:
+
+ // Verkrijg sessiegegevens
+ $data = $session->get($key, $default_value);
+
+ // Verkrijg cookiegegevens
+ $data = Cookie::get($key, $default_value);
+
+ // Verkrijg het gebruikers id
+ $user = $session->get('user_id');
+ $user = Cookie::get('user_id');
+
+## Verwijderen van gegevens {#deleting}
+
+Het verwijderen van sessie- of cookie-gegevens wordt gedaan met behulp van de `delete`-methode:
+
+ // Verwijderen van sessiegegevens
+ $session->delete($key);
+
+ // Verwijderen van cookiegegevens
+ Cookie::delete($key);
+
+ // Verwijder een gebruikers id
+ $session->delete('user_id');
+ Cookie::delete('user_id');
+
+# Configuratie {#configuration}
+
+Zowel cookies als sessies hebben verschillende configuratie-instellingen die van invloed zijn hoe gegevens worden opgeslagen. Controleer altijd deze instellingen voordat u uw applicatie live zet, omdat veel van die instellingen een rechtstreeks effect zal hebben op de veiligheid van uw applicatie.
+
+## Cookie Instellingen {#cookie-settings}
+
+Al de cookie instellingen worden verandert met behulp van statische properties. Je kan deze instellingen veranderen in `bootstrap.php` of door een [class extension](using.autoloading#class-extension) te gebruiken.
+
+De meest belangrijke instelling is [Cookie::$salt], die wordt gebruikt om veilig te ondertekenen. Deze waarde zou moeten gewijzigd en geheim gehouden worden:
+
+ Cookie::$salt = 'Uw geheim is veilig bij mij';
+
+[!!] Door het veranderen van deze waarde zullen alle bestaande cookies niet meer geldig zijn.
+
+Standaard worden cookies bewaard tot het browservenster wordt gesloten. Om een specifieke leeftijd te gebruiken, verander de [Cookie::$expiration] instelling:
+
+ // Stel in dat cookies vervallen na n week
+ Cookie::$expiration = 604800;
+
+ // Alternatief voor het gebruik van getallen, voor meer duidelijkheid
+ Cookie::$expiration = Date::WEEK;
+
+Het path waarvan de cookie kan worden opgevraagd kan worden beperkt met behulp van de [Cookie::$path] instelling.
+
+ // Enkel cookies toelaten wanneer je gaat naar /public/*
+ Cookie::$path = '/public/';
+
+Het domein waarvan de cookie kan worden geopend kan ook worden beperkt, met behulp van de [Cookie::$domain] instelling.
+
+ // Enkel cookies toelaten voor www.example.com
+ Cookie::$domain = 'www.example.com';
+
+Als u de cookie toegankelijk wilt maken op alle subdomeinen, gebruik dan een punt aan het begin van het domein.
+
+ // Cookies toegankelijk maken voor example.com en *.example.com
+ Cookie::$domain = '.example.com';
+
+Als je de cookie alleen wilt kunnen benaderen via een beveiligde (HTTPS) verbinding, gebruik dan de [Cookie::$secure] instelling.
+
+ // Cookies enkel toegangekijk maken via een beveiligde verbinding
+ Cookie::$secure = TRUE;
+
+ // Cookies toegankelijk maken voor elke verbinding
+ Cookie::$secure = FALSE;
+
+Om te voorkomen dat cookies worden geopend met behulp van Javascript, kunt u de [Cookie::$httponly] instelling aanpassen.
+
+ // Maak cookies niet toegankelijk via Javascript
+ Cookie::$httponly = TRUE;
+
+## Sessie Adapters {#adapters}
+
+Bij het maken van of het aanroepen van een instantie van de [Sessie] class kan je kiezen welke sessie adapter je wilt gebruiken. De sessie adapters die beschikbaar zijn voor je:
+
+Native
+: Slaat sessiegegevens op in de standaard locatie voor uw web server. De opslaglocatie is gedefinieerd door [session.save_path](http://php.net/manual/session.configuration.php#ini.session.save-path) in `php.ini` of gedefinieerd door [ini_set](http://php.net/ini_set).
+
+Database
+: Slaat de sessiesgegevens op in een database tabel door gebruik te maken van de [Session_Database] class. De [Database] module is vereist.
+
+Cookie
+: Slaat de sessiegegevens op in een cookie door gebruikt te maken van de [Cookie] class. **Sessies hebben een 4KB limiet wanneer je deze adapter gebruikt.**
+
+De standaard adapter kan ingesteld worden door de waarde aan te passen van [Session::$default]. De standaard adapter is "native".
+
+[!!] Zoals bij cookies bekent een "lifetime" instelling van "0" dat de sessie zal vervallen bij het sluiten van de het browservenster.
+
+### Sessie Adapter Instellingen
+
+Je kan configuratie-instellingen voor elk van de sessie adapters instellen door het creren van een sessie configuratiebestand in `APPPATH/config/session.php`. Het volgende voorbeeld van een configuratie bestand definiert alle instellingen voor elke adapter:
+
+ return array(
+ 'native' => array(
+ 'name' => 'session_name',
+ 'lifetime' => 43200,
+ ),
+ 'cookie' => array(
+ 'name' => 'cookie_name',
+ 'encrypted' => TRUE,
+ 'lifetime' => 43200,
+ ),
+ 'database' => array(
+ 'name' => 'cookie_name',
+ 'encrypted' => TRUE,
+ 'lifetime' => 43200,
+ 'group' => 'default',
+ 'table' => 'table_name',
+ 'columns' => array(
+ 'session_id' => 'session_id',
+ 'last_active' => 'last_active',
+ 'contents' => 'contents'
+ ),
+ 'gc' => 500,
+ ),
+ );
+
+#### Native Adapter {#adapter-native}
+
+Type | Instelling | Omschrijving | Standaard
+----------|------------|-----------------------------------------------------|-----------
+`string` | name | naam van de sessie | `"session"`
+`integer` | lifetime | aantal seconden dat de sessie moet bestaan | `0`
+
+#### Cookie Adapter {#adapter-cookie}
+
+Type | Instelling | Omschrijving | Standaard
+----------|------------|-----------------------------------------------------|-----------
+`string` | name | naam van de cookie om de sessiegegevens op te slaan | `"session"`
+`boolean` | encrypted | de sessiegegevens coderen met [Encrypt]? | `FALSE`
+`integer` | lifetime | aantal seconden dat de sessie moet bestaan | `0`
+
+#### Database Adapter {#adapter-database}
+
+Type | Instelling | Omschrijving | Standaard
+----------|------------|-----------------------------------------------------|-----------
+`string` | group | [Database::instance] groep naam | `"default"`
+`string` | table | de tabelnaam waar de gegevens worden in opgeslagen | `"sessions"`
+`array` | columns | associatieve array met kolom aliassen | `array`
+`integer` | gc | 1:x kans dat de garbage collection uitgevoerd wordt | `500`
+`string` | name | naam van de cookie om de sessiegegevens op te slaan | `"session"`
+`boolean` | encrypted | de sessiegegevens coderen met [Encrypt]? | `FALSE`
+`integer` | lifetime | aantal seconden dat de sessie moet bestaan | `0`
+
+##### Tabel Schema
+
+Je moet de sessie-opslag tabel in de database aanmaken. Dit is het standaard schema:
+
+ CREATE TABLE `sessions` (
+ `session_id` VARCHAR(24) NOT NULL,
+ `last_active` INT UNSIGNED NOT NULL,
+ `contents` TEXT NOT NULL,
+ PRIMARY KEY (`session_id`),
+ INDEX (`last_active`)
+ ) ENGINE = MYISAM;
+
+##### Tabel kolommen
+
+Je kunt de namen van kolommen aanpassen om overeen te komen met een bestaand database-schema. De standaard waarde is hetzelfde als de key waarde.
+
+session_id
+: de naam van de "id" kolom
+
+last_active
+: UNIX timestamp van het laatste tijdstip dat de sessie werd aangepast
+
+contents
+: sessiongegevens opgeslaan in een serialized string, en optioneel gecodeerd
diff --git a/includes/kohana/modules/userguide/guide/nl/using.views.md b/includes/kohana/modules/userguide/guide/nl/using.views.md
new file mode 100644
index 00000000..beef852b
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/nl/using.views.md
@@ -0,0 +1,118 @@
+# Het gebruik van Views
+
+Views zijn bestanden die de visuele informatie bevatten voor je applicatie. Dit is meestal HTML, CSS en Javascript maar kan van alles zijn die je nodig hebt zoals XML of JSON voor AJAX output. Het doel van views is om deze informatie af te scheiden van de applicatie logica zodat je nettere code hebt en deze gemakkelijker kunt hergebruiken.
+
+Hoewel dit waar is, kunnen views zelf ook code bevatten die je gebruikt om gegevens te tonen die je meegestuurd hebt met de view. Bijvoorbeeld, het loopen door een array met producten en voor elk product een nieuwe tabelrij tonen. Views zijn nog altijd PHP bestanden dus kan je erin coderen zoals je normaal zou doen.
+
+# Aanmaken van View bestanden
+
+De View bestanden worden opgeslagen in de `views` folder van het [bestandssysteem](about.filesystem). Je kan ook subfolders aanmaken in de `views` folder om je bestanden meer te organiseren. Alle mogelijkheden uit de volgende voorbeelden zijn goed:
+
+ APPPATH/views/home.php
+ APPPATH/views/pages/about.php
+ APPPATH/views/products/details.php
+ MODPATH/error/views/errors/404.php
+ MODPATH/common/views/template.php
+
+## Inladen van Views
+
+[View] objecten worden gewoonlijk aangemaakt binnenin een [Controller] via de [View::factory] methode. De view wordt dan gewoonlijk aan de [Request::$response] property toegewezen of aan een andere view.
+
+ public function action_about()
+ {
+ $this->request->response = View::factory('pages/about');
+ }
+
+Wanneer een view wordt toegewezen aan de [Request::$response], zoals in bovenstaand voorbeeld, dan zal het automatisch worden gerenderd wanneer noodzakelijk. Om het gerenderde resultaat van een view te verkrijgen kan je de [View::render] methode aanspreken of gewoon laten casten naar een string. Wanneer een view gerenderd is, wordt de view ingeladen en wordt de HTML gegenereerd.
+
+ public function action_index()
+ {
+ $view = View::factory('pages/about');
+
+ // View wordt gerenderd
+ $about_page = $view->render();
+
+ // Of gewoon laten casten naar een string
+ $about_page = (string) $view;
+
+ $this->request->response = $about_page;
+ }
+
+## Variabelen in Views
+
+Eenmaal een view is ingeladen, kunnen variabelen eraan toegewezen worden door de [View::set] en [View::bind] methodes.
+
+ public function action_roadtrip()
+ {
+ $view = View::factory('user/roadtrip')
+ ->set('places', array('Rome', 'Paris', 'London', 'New York', 'Tokyo'));
+ ->bind('user', $this->user);
+
+ // De view zal de variabelen $places en $user hebben
+ $this->request->response = $view;
+ }
+
+[!!] Het enige verschil tussen `set()` en `bind()` is dat `bind()` de variabele toewijst via referentie. Als je een variabele `bind()` vooraleer ze gedefineerd is, zal de variable als `NULL` worden gecreerd.
+
+### Globale Variabelen
+
+Een applicatie kan verschillende views hebben die toegang hebben tot dezelfde variabelen. Bijvoorbeeld, een titel van een pagina wil je zowel tonen in de header van je template als in de body van de pagina inhoud. Je kan variabelen creren dat toegankelijk zijn in elke view dankzij de [View::set_global] en [View::bind_global] methoden.
+
+ // Wijs $page_title toe aan alle views
+ View::bind_global('page_title', $page_title);
+
+Als de applicatie drie views heeft die gerenderd zijn voor de home-pagina: `template`, `template/sidebar` en `pages/home`. Eerst zal je een abstracte controller maken om de template te maken:
+
+ abstract class Controller_Website extends Controller_Template {
+
+ public $page_title;
+
+ public function before()
+ {
+ parent::before();
+
+ // Maak $page_title toegankelijk in alle views
+ View::bind_global('page_title', $this->page_title);
+
+ // Laad $sidebar in de template als een view
+ $this->template->sidebar = View::factory('template/sidebar');
+ }
+
+ }
+
+Dan moet de home controller de `Controller_Website` uitbreiden:
+
+ class Controller_Home extends Controller_Website {
+
+ public function action_index()
+ {
+ $this->page_title = 'Home';
+
+ $this->template->content = View::factory('pages/home');
+ }
+
+ }
+
+## Views in Views
+
+Als je een andere view wilt gebruiken in een view heb je twee keuzes. Door [View::factory] aan te roepen kan je de opgenomen view sandboxen. Dit betekent dat je alle variabelen moet meegeven aan de view door middel van [View::set] of [View::bind]:
+
+ // Enkel de $user variabele zal toegankelijk zijn in "views/user/login.php"
+ bind('user', $user) ?>
+
+De andere optie is om de view rechtstreeks in te voegen, dat maakt alle huidige variabelen beschikbaar in de ingesloten view:
+
+ // Elke variabele gedefinieerd in deze view zal worden ingesloten in "views/message.php"
+
+
+Natuurlijk kan je ook een volledige [Request] inladen in een view:
+
+ execute() ?>
+
+Dit is een voorbeeld van [HMVC](about.mvc), dit maakt het mogelijk om aanroepingen te maken en te lezen via andere URLs binnenin je applicatie.
+
+# Upgraden van v2.x
+
+In tegenstelling tot versie 2.x van Kohana, wordt de view niet ingeladen in de context van de [Controller], dus is het niet mogelijk om `$this` aan te spreken als controller binnenin de view. De controller doorgeven aan de view moet nu expliciet worden gedaan:
+
+ $view->bind('controller', $this);
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/about.conventions.md b/includes/kohana/modules/userguide/guide/ru-ru/about.conventions.md
new file mode 100644
index 00000000..24a6904e
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/about.conventions.md
@@ -0,0 +1,301 @@
+# Соглашения
+
+При работе с фреймворком приветствуется использование Kohana [coding style](http://dev.kohanaphp.com/wiki/kohana2/CodingStyle). В нем используется [BSD/Allman стиль](http://ru.wikipedia.org/wiki/%D0%9E%D1%82%D1%81%D1%82%D1%83%D0%BF_%28%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%29#.D0.A1.D1.82.D0.B8.D0.BB.D1.8C_.D0.9E.D0.BB.D0.BC.D0.B0.D0.BD.D0.B0) расстановки фигурных скобок и прочего форматирования кода.
+
+## Имена классов и расположение файлов {#classes}
+
+Для облегчения работы [автозагрузки](about.autoloading) в Kohana, имена классов строго регламентированы. Имя класса должно начинаться с заглавной буквы, а для разделения слов в имени используется нижнее_подчёркивание. Нижнее подчёркивание отражает расположение файла в файловой структуре проекта.
+
+Данные соглашения предусматривают:
+
+1. Не рекомендуется использовать CamelCase в имени класса, за исключением случаев, когда нежелательно создавать дополнительные поддиректории.
+2. Все файлы классов и директории должны быть в нижнем регистре.
+3. Все классы должны располагаться в директории `classes`, которая может быть на любом уровне [каскадной файловой системы](about.filesystem) фреймворка.
+
+[!!] В отличие от Kohana версии 2.x, в Kohana 3 нет разделения между "типами" классов: будь то "контроллер", "модель", "библиотека" или "хелпер". Все классы располагаются в директории "classes/" вне зависимости от того, являются ли они статическими "хелперами" или объектами "библиотеки". Вы вправе реализовывать любой дизайн класса, который Вам необходим: статический, синглтон, адаптер и т.п.
+
+## Примеры
+
+Помните, нижнее подчёркивание в наименовании класса отвечает за разделение на директории и отражает расположение файла в файловой структуре проекта.
+
+Название класса | Файл
+----------------------|-------------------------------
+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
+
+## Стандарты кодирования {#coding_standards}
+
+Для того, чтобы Ваш код был удобочитаемым и выглядел последовательным, мы просим всех на максимально придерживаться стандартов кодирования.
+
+### Фигурные скобки
+
+Используйте, пожалуйста, [BSD/Allman стиль](http://ru.wikipedia.org/wiki/%D0%9E%D1%82%D1%81%D1%82%D1%83%D0%BF_%28%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%29#.D0.A1.D1.82.D0.B8.D0.BB.D1.8C_.D0.9E.D0.BB.D0.BC.D0.B0.D0.BD.D0.B0) расстановки фигурных скобок.
+
+### Соглашение в именовании классов
+
+В Kohana используется нижнее_подчёркивание в именовании классов. Именование с использованием camelCase не приветствуется.
+
+#### Классы
+
+ // Корневые библиотеки используют суффикс _Core
+ class Beer_Core {
+
+ // При расширение функционала библиотеки суффикс не используется
+ class Beer extends Beer_Core
+
+ // Класс контроллера использует суффикс _Controller
+ class Apple_Controller extends Controller {
+
+ // Класс модели использует суффикс _Model
+ class Cheese_Model extends Model {
+
+ // Класс хелпера
+ class peanut {
+
+Если Вы не передаёте параметры в конструктор при создании класса, не используйте круглые скобки при его объявлении:
+
+ // Правильно:
+ $db = new Database;
+
+ // Не правильно:
+ $db = new Database();
+
+#### Функции и методы
+
+Имена функций должны быть в нижнем регистре и использовать нижнее_подчёркивание для разделения слов:
+
+ function drink_beverage($beverage)
+ {
+
+#### Переменные
+
+Все переменные должны быть в нижнем регистре и использовать нижнее_подчёркивание для разделения слов (camelCase использовать запрещено):
+
+ // Правильно:
+ $foo = 'bar';
+ $long_example = 'uses underscores';
+
+ // Не правильно:
+ $weDontWantThis = 'understood?';
+
+### Отступы
+
+Используйте табуляцию для отступов в Вашем коде. Использование пробелов вместо табуляции строго запрещено.
+
+Вертикальные отступы (при многострочном выравнивании) формируются пробелами. Так как ширина табуляции может отличаться у разных людей, для вертикального выравнивания табуляция является не самым лучшем способом.
+
+ $text = 'это разделённый на несколько строк длинный текст. Зачастую, '
+ . 'при многострочном разделении используют ограничение в 80 знаков '
+ . 'на строку. Для удобочитаемости кода крайне важно применять правильное '
+ . 'вертикальное выравнивание текста. Помните, что любые отступы '
+ . 'должны выполняться в виде табуляции, а вертикальное выравнивание уже '
+ . 'должно завершаться пробелами, которые идут сразу после табов .';
+
+### Конкатенация строк
+
+Не используйте пробелы вокруг оператора конкатенации:
+
+ // Правильно:
+ $str = 'one'.$var.'two';
+
+ // Не правильно:
+ $str = 'one'. $var .'two';
+ $str = 'one' . $var . 'two';
+
+### Однострочные операторы
+
+Однострочные операторы IF могут быть использованы лишь в случае прерывания выполнения кода (например, return или continue):
+
+ // Допустимо:
+ if ($foo == $bar)
+ return $foo;
+
+ if ($foo == $bar)
+ continue;
+
+ if ($foo == $bar)
+ break;
+
+ if ($foo == $bar)
+ throw new Exception('You screwed up!');
+
+ // Не допустимо:
+ if ($baz == $bun)
+ $baz = $bar + 2;
+
+### Операторы сравнения
+
+Используйте, пожалуйста, для сравнения операторы OR и AND:
+
+ // Правильно:
+ if (($foo AND $bar) OR ($b AND $c))
+
+ // Не правильно:
+ if (($foo && $bar) || ($b && $c))
+
+Используйте, пожалуйста, elseif вместо else if:
+
+ // Правильно:
+ elseif ($bar)
+
+ // Не правильно:
+ else if($bar)
+
+### Конструкции switch
+
+Каждый оператор case, break и default должны располагаться на новой строке. Блок внутри case и default должны иметь отступ в 1 таб.
+
+ switch ($var)
+ {
+ case 'bar':
+ case 'foo':
+ echo 'hello';
+ break;
+ case 1:
+ echo 'one';
+ break;
+ default:
+ echo 'bye';
+ break;
+ }
+
+### Круглые скобки
+
+Необходимо отделять имя оператора и следующие за ним скобки пробелом. Оператор ! (оператор отрицания, восклицательный знак) для удобочитаемости должен быть выделен пробелом с обеих сторон.
+
+ // Правильно:
+ if ($foo == $bar)
+ if ( ! $foo)
+
+ // Не правильно:
+ if($foo == $bar)
+ if(!$foo)
+ if ((int) $foo)
+ if ( $foo == $bar )
+ if (! $foo)
+
+### Тернарные операторы
+
+Все тернарные операции должны придерживаться стандартного формата. Используйте круглые скобки только для выделения условий, а не для выделения простых переменных.
+
+ $foo = ($bar == $foo) ? $foo : $bar;
+ $foo = $bar ? $foo : $bar;
+
+Все сравнения и операции должны быть заключены в круглые скобки:
+
+ $foo = ($bar > 5) ? ($bar + $foo) : strlen($bar);
+
+При многострочном разделении тернарного комплекса (при очень длинных условиях, превышающих длину в 80 знаков), для вертикального выравнивания стоит использовать пробелы. Операторы при этом должны располагаться один под другим:
+
+ $foo = ($bar == $foo)
+ ? $foo
+ : $bar;
+
+### Приведение типов
+
+При приведении типов, следует использовать пробелы вокруг оператора приведения:
+
+ // Правильно:
+ $foo = (string) $bar;
+ if ( (string) $bar)
+
+ // Не правильно:
+ $foo = (string)$bar;
+
+По возможности, используйте приведение типов вместо тернарных операторов:
+
+ // Правильно:
+ $foo = (bool) $bar;
+
+ // Не правильно:
+ $foo = ($bar == TRUE) ? TRUE : FALSE;
+
+При приведение к числовому или булеву типам, используйте сокращённый формат:
+
+ // Правильно:
+ $foo = (int) $bar;
+ $foo = (bool) $bar;
+
+ // Не правильно:
+ $foo = (integer) $bar;
+ $foo = (boolean) $bar;
+
+### Константы
+
+Имена констант должны быть строго в верхнем регистре:
+
+ // Правильно:
+ define('MY_CONSTANT', 'my_value');
+ $a = TRUE;
+ $b = NULL;
+
+ // Не правильно:
+ define('MyConstant', 'my_value');
+ $a = True;
+ $b = null;
+
+При сравнении располагайте константы в конце:
+
+ // Правильно:
+ if ($foo !== FALSE)
+
+ // Не правильно:
+ if (FALSE !== $foo)
+
+Такой выбор немного спорный, но можно его объяснить следующим. Если вербализировать предыдущие условия и написать их, то можно их прочитать следующим образом для верного примера:
+
+ если переменная $foo в точности не является FALSE
+
+И для неверного:
+
+ если FALSE в точности не является переменной $foo
+
+Так как наш язык предусматривает чтение слева направо, то и нет причин для того, чтобы ставить константу первой при сравнении.
+
+### Комментарии
+
+#### Однострочные комментарии
+
+Используйте // перед строкой комментария, делайте отступ и начинайте комментарий с заглавной буквы. Никогда не используйте для комментариев #.
+
+ // Правильно
+
+ //Не правильно
+ // не правильно
+ # Не правильно
+
+### Регулярные выражения
+
+При использовании регулярных выражений, используйте синтаксис PCRE вместо POSIX. PCRE признан более мощной и быстрой библиотекой регулярных выражений.
+
+ // Правильно:
+ if (preg_match('/abc/i'), $str)
+
+ // Не правильно:
+ if (eregi('abc', $str))
+
+Используйте одинарные кавычки вокруг регулярного выражения. Одинарные кавычки удобочитаемы ввиду их простоты. Строки, заключённые в одинарные кавычки, в отличие от двойных кавычек, не поддерживают интерполяцию переменных и конструкции с обратным слешем (\n, \t и т.д.).
+
+ // Правильно:
+ preg_match('/abc/', $str);
+
+ // Не правильно:
+ preg_match("/abc/", $str);
+
+При использовании регулярного выражения для поиска и замены, используйте $n нотацию для ссылок. Это более предпочтительно, нежели \\n.
+
+ // Правильно:
+ preg_replace('/(\d+) dollar/', '$1 euro', $str);
+
+ // Не правильно:
+ preg_replace('/(\d+) dollar/', '\\1 euro', $str);
+
+И в заключении, обратите внимание, что символ $ для определения позиции в конце строки не учитывает наличие символа новой строки. Используйте при необходимости модификатор D, чтобы этого избежать. [Подробная информация](http://blog.php-security.org/archives/76-Holes-in-most-preg_match-filters.html).
+
+ $str = "email@example.com\n";
+
+ preg_match('/^.+@.+$/', $str); // TRUE
+ preg_match('/^.+@.+$/D', $str); // FALSE
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/about.filesystem.md b/includes/kohana/modules/userguide/guide/ru-ru/about.filesystem.md
new file mode 100644
index 00000000..03f90b07
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/about.filesystem.md
@@ -0,0 +1,59 @@
+# Каскадная файловая система
+
+Файловая система Kohana представляет собой иерархическая структура каталогов. Когда файл загружается, используя [Kohana::find_file], он ищется в каталогах в следующем порядке:
+
+Application путь
+: Определён как `APPPATH` в `index.php`. Значение по-умолчанию - `application`.
+
+Module путь
+: Определён как ассоциативный массив [Kohana::modules] в `APPPATH/bootstrap.php`. Поиск каждого значение массива будет произведён в том порядке, в каком порядке они определены.
+
+System путь
+: Определён как `SYSPATH` в `index.php`. Значение по-умолчанию - `system`. Все основные или "core" файлы и классы располагаются в этой категории.
+
+Файлы, находящиеся в каталогах выше уровнем, имеют приоритет над файлами, которые располагаются в таких же папках уровнем ниже, что позволяет перегружать любой файл, путём перемещения файла с таким же названием на уровень выше:
+
+
+
+Если у Вас имеется файл представления с названием `wellcome.php`, расположенный в `APPPATH/views` и `SYSPATH/views`, то при поиске файла с этим именем будет возвращен тот, который находится в папке `APPPATH/views` (как находящийся на высшем уровне иерархической системы). Если же удалить файл в каталоге `APPPATH/views`, то при очередном поиске этого файла будет вызван тот, что располагается в каталоге `SYSPATH/views`.
+
+## Типы файлов
+
+Верхний уровень каталогов application, module, и system путей, по-умолчанию имеет следующие директории:
+
+classes/
+: Все классы, которые должны быть [автоматически загружены](using.autoloading) должны располагаться тут. Это касается контроллеров, моделей и всех других классов. Наименования классов должны соответствовать [соглашению по именованию классов](about.conventions#classes).
+
+config/
+: Конфигурационные файлы возвращают ассоциативный массив опций, который может быть загружен с использованием метода [Kohana::config]. Для более подробной информации, обратитесь к разделу [Настройка](using.configuration).
+
+i18n/
+: Файлы перевода возвращают ассоциативный массив строк. Перевод осуществляется с использованием метода `__()`.Для того, чтобы перевести "Hello, world!" на Русский, нужно вызвать `__('Hello, world!')` c [I18n::$lang] определённом как "ru-ru". Для более подробной информации, обратитесь к разделу [Интернационализация](using.translation).
+
+messages/
+: Файлы сообщений возвращают ассоциативный массив строк, который может быть загружен с использованием [Kohana::message]. Разница между сообщениями и файлами интернационализации заключается в том, что сообщения никогда не переводятся, пишутся на языке, используемом по-умолчанию, и вызываются по односложному ключу. Для более подробной информации, обратитесь к разделу [Сообщения (Messages)](using.messages).
+
+views/
+: Файлы представления являются PHP файлами, которые используются для генерации HTML или другого вида вывода информации. Файлы представления загружаются в объект [View] и присваиваются переменным, которые в дальнейшем конвертируются во фрагменты HTML. Множественные файлы представления могут быть вызваны одни в других. Для более подробной информации, обратитесь к разделу [Представление и HTML](usings.views).
+
+## Поиск файлов
+
+Путь к любому файлу в файловой структуре приложения может быть найден путём вызова [Kohana::find_file]:
+
+ // Ищет полный путь до "classes/cookie.php"
+ $path = Kohana::find_file('classes', 'cookie');
+
+ // Ищет полный путь до "views/user/login.php"
+ $path = Kohana::find_file('views', 'user/login');
+
+# Сторонние библиотеки
+
+Мы называем библиотеки, которые не специфичны для Kohana "vendor" библиотеками. Например, если Вы хотите использовать [DOMPDF](http://code.google.com/p/dompdf), то Вам нужно скопировать эту библиотеку в `application/vendor/dompdf` и добавить автозагрузочный класс DOMPDF:
+
+ require Kohana::find_file('vendor', 'dompdf/dompdf/dompdf_config.inc');
+
+После этого, можно использовать DOMPDF без загрузки каких-либо других файлов:
+
+ $pdf = new DOMPDF;
+
+[!!] Если Вы хотите конвертировать представление в PDF с использованием DOMPDF, используйте [PDFView](http://github.com/shadowhand/pdfview) модуль.
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/about.flow.md b/includes/kohana/modules/userguide/guide/ru-ru/about.flow.md
new file mode 100644
index 00000000..28c1186d
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/about.flow.md
@@ -0,0 +1,73 @@
+# Порядок выполнения
+
+Каждое приложение выполняется в следующем порядке:
+
+1. Старт приложения из `index.php`
+2. Установка путей для application, module, и system.
+3. Установка уровня Error reporting.
+4. Загружается файл установки, если таковой имеется.
+5. Загружается класс [Kohana].
+6. Подключение `APPPATH/bootstrap.php`
+7. Вызывается [Kohana::init], который устанавливает обработку ошибок, кэширование и логирование.
+8. Подгружаются [Kohana_Config] и [Kohana_Log].
+9. Вызывается [Kohana::modules] для активации дополнительных модулей.
+ * Информация о пути к модулям добавляется в [каскадную файловую систему](about.filesystem).
+ * Если находится, подгружается файл `init.php` модуля.
+ * Файл `init.php` производит дополнительную настройку окружения, включая добавление маршрутов.
+10. Несколько раз вызывается [Route::set], чтобы определить все [маршруты](using.routing) приложения.
+11. Вызывается [Request::instance], чтобы начать обработку выполнения.
+ 1. Проверяет все маршруты, пока не найдёт совпадения с вызываемым.
+ 2. Создаёт инстанс контроллера и передаёт ему запрос.
+ 3. Вызывает метод [Controller::before].
+ 4. Вызывает действие контроллера, которое генерирует ответа на запрос.
+ 5. Вызывает метод [Controller::after].
+ * Предыдущие 5 шагов могут быть вызваны несколько раз при использовании [HMVC подзапросов](about.mvc).
+12. Отображается результат ответа на запрос ([Request]).
+
+## index.php
+
+Kohana использует в работе паттерн [front controller], что означает, что все запросы направляются на `index.php`. Это позволяет использовать понятный и прозрачный дизайн [файловой системы](about.filesystem). `index.php` содержит лишь основные возможные конфигурационные свойства. Вы лишь можете изменить `$application`, `$modules`, и `$system` пути и выставить уровень оповещения об ошибках.
+
+Переменная `$application` позволяет определить директорию, которая содержит файлы Вашего приложения. По-умолчанию - это `application`. Переменная `$modules`, соответственно, указывает на директорию с файлами модулей, а `$system` - директорию с файлами ядра Kohana.
+
+Вы можете переместить эти директории куда угодно. Например, если Вы имеете такую структуру каталогов:
+
+ www/
+ index.php
+ application/
+ modules/
+ system/
+
+То можете переместить директории на уровень выше корня web папки:
+
+ application/
+ modules/
+ system/
+ www/
+ index.php
+
+После этого нужно привести установки в `index.php` к следующему виду:
+
+ $application = '../application';
+ $modules = '../modules';
+ $system = '../system';
+
+Теперь ни один из каталогов не может быть напрямую доступен для web сервера. Совсем не обязательно производить эти изменения, но, помимо прочего, это может быть полезно при использовании одного набора директорий среди множества приложений.
+
+[!!] В начале всех файлов Kohana присутствует проверка безопасности, дабы предотвратить доступ до файла без использования front контроллера. Однако, более безопасно разместить application, modules, и system директории таким образом, чтобы они были недоступны извне.
+
+### Сообщения об ошибках
+
+По-умолчанию, Kohana отображает все ошибки, включая strict mode предупреждения. Это устанавливается, используя [error_reporting](http://php.net/error_reporting):
+
+ error_reporting(E_ALL | E_STRICT);
+
+Когда Ваше приложение опубликовывается в продуктив, то рекомендуется использовать более консервативные настройки, такие как игнорирование уведомлений:
+
+ error_reporting(E_ALL & ~E_NOTICE);
+
+Если Вы получаете белый экран при ошибке, то это означает, что Ваш сервер имеет отключённое свойство отображение ошибок. Вы можете включить его, добавив эти строчки сразу после вызова `error_reporting`:
+
+ ini_set('display_errors', TRUE);
+
+Ошибки должны **всегда** отображаться, даже в продуктиве, так как это позволяет использовать [исключения и обработку ошибок](debugging.errors), чтобы выдавать симпатичную страничку с ошибкой, нежели дезориентирующий белый экран.
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/about.install.md b/includes/kohana/modules/userguide/guide/ru-ru/about.install.md
new file mode 100644
index 00000000..7380a99a
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/about.install.md
@@ -0,0 +1,89 @@
+# Установка
+
+1. Загрузите последний **стабильный** релиз с [сайта Kohana](http://kohanaframework.org/)
+2. Распакуйте загруженный архив (появится директория `kohana`)
+3. Загрузите содержание архива на Ваш web-сервер
+4. Откройте файл `application/bootstrap.php` и произведите следующие изменения:
+ - Установите [часовой пояс](http://php.net/timezones) (timezone), который будет использоваться по-умолчанию в Вашем приложении
+ - Установите `base_url` в параметрах вызова [Kohana::init], чтобы обозначить расположение фреймворка на Вашем сервере
+6. Убедитесь в том, что папки `application/cache` и `application/logs` доступны для записи (для *nix ОС воспользуйтесь командой `chmod application/{cache,logs} 0777`)
+7. Проверьте правильность установки, открыв URL, указанный Вами ранее как `base_url` в Вашем любимом браузере
+
+[!!] В зависимости от платформы, установленные поддиректории могут потерять значения прав доступа из-за особенностей процесса zip распаковки. Чтобы выправить права доступа, измените права на 755, выполнив в командной строке `find . -type d -exec chmod 0755 {} \;` из корневой директории Kohana.
+
+Вы увидите страницу установки. Если будут отображены какие-либо ошибки, необходимо их устранить перед тем, как продолжать работать.
+
+
+
+После того, как Вы убедитесь, что все сконфигурировано правильно, переименуйте или удалите файл `install.php`. После этого Вы увидите приветственную страницу Kohana:
+
+
+
+## Настройка продуктив-окружения
+
+Имеется несколько вещеё, которые Вы наверняка захотите сделать перед публикацией Вашего приложения.
+
+1. Прочитайте описание процесса [настройки](about.configuration) этой документации. Оно охватывает большинство глобальных настроек, которые требуют изменения при смене окружения. Основное правило для сайтов в продуктиве - это активация кэширования и отключение профилирования (свойства [Kohana::init]). [Кэширование маршрутов](api/Route#cache) так же может быть полезным при наличии большого числа маршрутов.
+2. Обрабатывайте все исключения в `application/bootstrap.php` таким образом, чтобы не было утечки конфиденциальной информации при попытках трассировки запросов. Изучите нижеизложенный пример, который был взят из [исходных кодов сайта wingsc.com](http://github.com/shadowhand/wingsc), написанного Shadowhand'ом.
+3. Включите APC или любой другой вид кэширования кода. Это единственный и самый простой способ увеличения производительности, который можно применить к самому PHP. Чем сложнее и больше Ваше приложение, тем больше выгода от использования кэширования кода.
+
+ /**
+ * Set the environment string by the domain (defaults to Kohana::DEVELOPMENT).
+ */
+ Kohana::$environment = ($_SERVER['SERVER_NAME'] !== 'localhost') ? Kohana::PRODUCTION : Kohana::DEVELOPMENT;
+ /**
+ * Initialise Kohana based on environment
+ */
+ Kohana::init(array(
+ 'base_url' => '/',
+ 'index_file' => FALSE,
+ 'profile' => Kohana::$environment !== Kohana::PRODUCTION,
+ 'caching' => Kohana::$environment === Kohana::PRODUCTION,
+ ));
+
+ /**
+ * Execute the main request using PATH_INFO. If no URI source is specified,
+ * the URI will be automatically detected.
+ */
+ $request = Request::instance($_SERVER['PATH_INFO']);
+
+ try
+ {
+ // Attempt to execute the response
+ $request->execute();
+ }
+ catch (Exception $e)
+ {
+ if (Kohana::$environment === Kohana::DEVELOPMENT)
+ {
+ // Just re-throw the exception
+ throw $e;
+ }
+
+ // Log the error
+ Kohana::$log->add(Kohana::ERROR, Kohana::exception_text($e));
+
+ // Create a 404 response
+ $request->status = 404;
+ $request->response = View::factory('template')
+ ->set('title', '404')
+ ->set('content', View::factory('errors/404'));
+ }
+
+ if ($request->send_headers()->response)
+ {
+ // Get the total memory and execution time
+ $total = array(
+ '{memory_usage}' => number_format((memory_get_peak_usage() - KOHANA_START_MEMORY) / 1024, 2).'KB',
+ '{execution_time}' => number_format(microtime(TRUE) - KOHANA_START_TIME, 5).' seconds');
+
+ // Insert the totals into the response
+ $request->response = str_replace(array_keys($total), $total, $request->response);
+ }
+
+
+ /**
+ * Display the request response.
+ */
+ echo $request->response;
+
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/about.kohana.md b/includes/kohana/modules/userguide/guide/ru-ru/about.kohana.md
new file mode 100644
index 00000000..6367779e
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/about.kohana.md
@@ -0,0 +1,15 @@
+# Что такое Kohana?
+
+Kohana это открытый, [объектно-ориентированный](http://ru.wikipedia.org/wiki/Объектно-ориентированное_программирование) [MVC](http://ru.wikipedia.org/wiki/Model–View–Controller "Model View Controller") web-фреймворк, построенный на [PHP5](http://docs.php.net/manual/ru/intro-whatis.php "PHP Hypertext Preprocessor") на общественных началах, основными целями которого являются скорость, безопасность и небольшой размер.
+
+[!!] Kohana использует лицензию [BSD](http://kohanaphp.com/license), так что Вы можете свободно использовать ее в любых своих открытых, коммерческих или персональных проектах.
+
+## Чем хороша Kohana?
+
+Что угодно может быть расширено с помощью уникальной [файловой системы](about.filesystem), требуется минимум [настроек](about.configuration), [перехват ошибок](debugging.errors) помогает быстро определить источник ошибок, а [отладка](debugging) и [профилирование](debugging.profiling) дают представление о происходящем внутри приложения.
+
+Чтобы обезопасить Ваши приложения, представлены инструменты для [защиты от XSS](security.xss), [проверки введенных данных](security.validation), работы с [cookies](security.cookies), генераторы [форм](security.forms) и [HTML](security.html). Слой [баз данных](security.database) предусматривает защиту от [SQL-инъекций](http://ru.wikipedia.org/wiki/SQL-инъекция). Разумеется, весь официальный код написан с душой и проверен на наличие уязвимостей.
+
+## Эта документация - г*вно!
+
+Мы очень активно работаем над тем, чтобы обеспечить полное документирование. В случае, если Вы не смогли найти ответ на свой вопрос, прочитайте [неофициальную wiki](http://kerkness.ca/wiki/doku.php). Если Вы хотели бы что-то добавить или изменить в Руководстве пользователя, пожалуйста [сделайте свой форк userguide'а](http://github.com/kohana/userguide), внесите свои изменения и пошлите pull request. Если Вы ещё не познакомились с работой с Git, Вы так же можете опубликовать [запрос на изменение](http://dev.kohanaframework.org/projects/kohana3/issues) (требуется регистрация).
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/about.mvc.md b/includes/kohana/modules/userguide/guide/ru-ru/about.mvc.md
new file mode 100644
index 00000000..3d9e0d6c
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/about.mvc.md
@@ -0,0 +1,5 @@
+# Модель-Представление-Контроллер
+
+Модель-Представление-Контроллер (сокращенно MVC) - это популярный паттерн проектирования, используя который источник данных (Модель) отделяется от шаблонов представления (Представление) и логики запроса (Контроллер).
+
+Это делает процесс разработки легче, т.к. облегчается повторное использование кода. Это означает, что писать придется намного меньше кода!
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/about.upgrading.md b/includes/kohana/modules/userguide/guide/ru-ru/about.upgrading.md
new file mode 100644
index 00000000..86623ee8
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/about.upgrading.md
@@ -0,0 +1,292 @@
+# Переход с 2.3.x
+
+Многое в Kohana v3 работает совсем по другому, нежели в Kohana 2.3, вот список самых популярных советов для обновляющихся.
+
+## Правила именования
+
+Ветка 2.x выделяет различные 'типы' классов (т.е. контроллер, модель и т.д.), используя для этого суффиксы. Директории внутри папок model / controller не влияют на имя класса.
+
+В 3.0 от данного подхода отказались в пользу файловых соглашений Zend framework , в котором имя класса является путем к файлу, разделенному знаками подчеркивания вместо слэщей (т.е. `/some/class/file.php` становится `Some_Class_File`)
+
+Смотри [описание соглашений](start.conventions) для получения подробной информации.
+
+## Библиотека Input
+
+Библиотека Input была исключена из 3.0, просто используйте `$_GET` и `$_POST`.
+
+### Защита от XSS
+
+Если Вам нужно обезопасить от XSS введенные пользователем данные, можете использовать [Security::xss_clean] :
+
+ $_POST['description'] = security::xss_clean($_POST['description']);
+
+Также можно вызывать [Security::xss_clean] в качестве фильтра для объекта [Validate]:
+
+ $validation = new Validate($_POST);
+
+ $validate->filter('description', 'Security::xss_clean');
+
+### POST & GET
+
+Одной из важнейших возможностей библиотеки Input было то, что если Вы попытаетесь обратиться к значению одного из суперглобальных массивов и оно не задано, библиотека Input возвратит значение по умолчанию, которое Вы зададите, например:
+
+ $_GET = array();
+
+ // $id установлено в 1
+ $id = Input::instance()->get('id', 1);
+
+ $_GET['id'] = 25;
+
+ // $id установлено в 25
+ $id = Input::instance()->get('id', 1);
+
+В 3.0 Вы можете сделать то же самое с помощью [Arr::get]:
+
+ $_GET = array();
+
+ // $id установлено в 1
+ $id = Arr::get($_GET, 'id', 1);
+
+ $_GET['id'] = 42;
+
+ // $id установлено в 42
+ $id = Arr::get($_GET, 'id', 1);
+
+## Библиотека ORM
+
+Произошло немало серьезных изменений в ORM после версии 2.3, вот список наиболее известных проблем после обновления.
+
+### Свойства
+
+Все свойства модели теперь начинаются со знака подчеркивания (_) и больше не доступны через метод `__get()`. Вместо этого Вы должны вызвать метод с именем свойства, но без подчеркивания.
+
+Например, свойство, бывшее в 2.3 `loaded`, теперь называется `_loaded` и извне класса доступно через `$model->loaded()`.
+
+### Связи
+
+В 2.3, если Вы хотели пройтись по связанным с моделью объектам, надо было использовать:
+
+ foreach($model->{relation_name} as $relation)
+
+Однако в новой системе это не будет работать. В версии 2.3 любой запрос с использованием библиотеки Database, был сгенерирован глобально, т.е. у Вас не получится создать одновременно два запроса. Вот пример:
+
+# TODO: Нужен достойный пример!!!!
+
+Этот запрос приведет к ошибке, т.к. второй запрос будет 'наследовать' условия первого, тем самым вызывая чертовщину.
+В v3.0 это было исправлено выделением каждого запроса в свою собственную 'песочницу', это приводит к тому, что некоторые вещи работают не так, как ожидается. Пример:
+
+ foreach(ORM::factory('user', 3)->where('post_date', '>', time() - (3600 * 24))->posts as $post)
+ {
+ echo $post->title;
+ }
+
+[!!] (Смотри описание нового синтаксиса запросов в [руководстве по Database](tutorials.databases))
+
+В 2.3 данный запрос вернет объект-итератор всех записей пользователя с id=3, где поле `post_date` лежит в диапазоне последних 24 часов. Однако новая версия применит условия where к пользовательской модели и вернет объединенную через join модель `Model_Post`.
+
+Для достижения нужного эффекта необходимо перераспределить порядок вызовов:
+
+ foreach(ORM::factory('user', 3)->posts->where('post_date', '>', time() - (36000 * 24))->find_all() as $post)
+ {
+ echo $post->title;
+ }
+
+Аналогично со связями `has_one`:
+
+ // Неправильно
+ $user = ORM::factory('post', 42)->author;
+ // Правильно
+ $user = ORM::factory('post', 42)->author->find();
+
+### Связи много-ко-многим
+
+В 2.3 Вы можете указать `has_and_belongs_to_many` тип связи. В 3.0 эта функциональность была переработана в связь *сквозное* `has_many`.
+
+В моделях определяется связь `has_many` с другой моделью, но добавляется атрибут `'through' => 'table'` , где `'table'` - имя промежуточной таблицы. Например (в контексте области записи<>категории):
+
+ $_has_many = array
+ (
+ 'categories' => array
+ (
+ 'model' => 'category', // внешняя модель
+ 'through' => 'post_categories' // промежуточная модель
+ ),
+ );
+
+Если Вы настроили kohana с использованием табличных префиксов, то не стоит беспокоиться о явном указании префиксов в модели.
+
+### Внешние ключи
+
+Если Вы хотели переопределить внешний ключ в ORM ветки 2.x, надо было указать в принадлежащей (слабой) модели свойство `$foreign_keys` со значением имени внешнего ключа.
+
+В 3.0 Вы определяется ключ `foreign_key` в самом объявлении связи, вот так:
+
+ Class Model_Post extends ORM
+ {
+ $_belongs_to = array
+ (
+ 'author' => array
+ (
+ 'model' => 'user',
+ 'foreign_key' => 'user_id',
+ ),
+ );
+ }
+
+В данном примере мы настроили поле `user_id` для таблицы записей.
+
+
+
+В связях has_many атрибут `far_key` (дальний ключ) является полем в промежуточной таблице, которое связано с внешней (для текущей) таблицей, а внешний ключ является полем в промежуточной таблице, которое связано с "этой" таблицей.
+
+Следуя вышесказанному, "posts" связаны много-ко-многим с "categories" через `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',
+ ),
+ );
+ }
+
+
+Очевидно, что настройки псевдонимов выглядят несколько неадекватными, но это хороший пример того, как работает схема внешний/дальний ключ.
+
+### ORM Iterator
+
+Стоит также отметить, что от класса `ORM_Iterator` отказались в пользу `Database_Result`.
+
+Если надо получить массив объектов ORM с ключами из первичных ключей, надо вызвать [Database_Result::as_array], вот так:
+
+ $objects = ORM::factory('user')->find_all()->as_array('id');
+
+Здесь `id` является первичным ключом таблицы.
+
+## Библиотека Router
+
+В версии 2 была библиотека Router, которая обрабатывала основной запрос приложения. Она позволяла определить базовые маршруты в файле `config/routes.php` и использовать собственные регулярные выражения для них, однако этого было недостаточно для описания чего-нибудь нестандартного.
+
+## Маршруты
+
+Система маршрутизации (теперь логичнее называть ее системой запросов) в версии 3.0 стала намного более гибкой. Маршруты объявляются в загрузочном файле (`application/bootstrap.php`) и скриптах init.php модулей (`modules/module/init.php`). (Стоит также отметить, что маршруты рассматриваются в том же порядке, в каком были определены).
+
+Вместо объявления массива маршрутов теперь необходимо создавать новый объект [Route] на каждый маршрут. В отличие от ветки 2.x нет необходимости отделять один URI от другого. Вместо этого Вы определяете шаблон с использованием переменных для обозначения секций (таких как контроллер, метод, id).
+
+К примеру, в предыдущей системе данное выражение:
+
+ $config['([a-z]+)/?(\d+)/?([a-z]*)'] = '$1/$3/$1';
+
+подменит URI `controller/id/method` на `controller/method/id`. В 3.0 следует использовать:
+
+ Route::set('reversed','((/(/)))')
+ ->defaults(array('controller' => 'posts', 'action' => 'index'));
+
+[!!] Каждому маршруту назначается уникальное имя (в данном случае он называется `reversed`), причины описаны в [учебнике по URL](tutorials.urls).
+
+Угловые скобки являются признаком динамических секций, которые будут сохранены в виде переменных. Круглые скобки обозначают необязательные участки. Если Вы хотите обрабатывать только адреса, начинающиеся с `admin`, используйте:
+
+ Rouse::set('admin', 'admin(/(/(/)))');
+
+А если надо заставить пользователя указать контроллер:
+
+ Route::set('admin', 'admin/(/(/))');
+
+Также Kohana не устанавливает сама значений по умолчанию. Если Вы хотите, чтобы Kohana установила метод 'index' как дефолтный, необходимо указать это в явном виде! Сделайте это с помощью [Route::defaults]. Когда надо указать регулярное выражение для отдельных сегментов uri, добавьте параметр - массив вида `segment => regex`. Например:
+
+ Route::set('reversed', '((/(/)))', array('id' => '[a-z_]+'))
+ ->defaults(array('controller' => 'posts', 'action' => 'index'))
+
+Значение сегмента `id` теперь должно состоять только из маленьких латинских букв и знака подчеркивания.
+
+### Экшены
+
+Еще один момент, который необходимо отметить - методы контроллера, которые должны быть доступны через url теперь называются "экшены" ("actions"), и начинаются с префикса 'action_'. В вышеуказанном примере, если пользователь введет `admin/posts/1/edit`, то экшен будет называться `edit`, но вызываемый метод контроллера получится `action_edit`. Подробности в [учебнике url](tutorials.urls).
+
+## Сессии
+
+Больше нет методов Session::set_flash(), Session::keep_flash() и Session::expire_flash(), вместо них используйте [Session::get_once].
+
+## Хэлпер URL
+
+Изменилось немногое - `url::redirect()` перемещен в `$this->request->redirect()` (при вызове из контроллера) / `Request::instance()->redirect()`
+
+`url::current` заменен на `$this->request->uri()`
+
+## Valid / Validation
+
+Эти два класса были объединены в единый класс `Validate`.
+
+Немного изменился синтаксис для валидации массивов:
+
+ $validate = new Validate($_POST);
+
+ // Применяем фильтр на все элементы массива
+ $validate->filter(TRUE, 'trim');
+
+ // Для указания специфических правил используйте rule()
+ $validate
+ ->rule('field', 'not_empty')
+ ->rule('field', 'matches', array('another_field'));
+
+ // Устанавливайте множество правил для одного поля через rules(), передавая массив вида rules => params в качестве второго параметра
+ $validate->rules('field', array(
+ 'not_empty' => NULL,
+ 'matches' => array('another_field')
+ ));
+
+Стандартное правило 'required' было переименовано в 'not_empty' для ясности.
+
+## Библиотека View
+
+Сделано несколько незначительных изменений, которые стоит отметить.
+
+В 2.3 представления формировались в зоне видимости контроллера, что позволяло в представлении использовать `$this` как ссылку на контроллер, и в 3.0 теперь по-другому. Представления теперь генерируются в пустом окружении. Если необходимо использовать `$this` в шаблоне, создайте ссылку с помощью [View::bind]: `$view->bind('this', $this)`.
+
+Тем не менее, стоит отметить, что это *очень* плохая привычка, т.к. повышает сцепление представления с контроллером и усложняет повторное использование. Рекомендуется подключать необходимые переменные таким образом:
+
+ $view = View::factory('my/view');
+
+ $view->variable = $this->property;
+
+ // ИЛИ если нравится запись по цепочке
+
+ $view
+ ->set('variable', $this->property)
+ ->set('another_variable', 42);
+
+ // НЕ рекомендуется
+ $view->bind('this', $this);
+
+Так как представление формируется в пустом окружении, `Controller::_kohana_load_view` теперь избыточно. Если надо изменить представление до его генерации (например, добавить меню сайта), используйте [Controller::after].
+
+ template->menu = '...';
+
+ return parent::after();
+ }
+ }
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/debugging.code.md b/includes/kohana/modules/userguide/guide/ru-ru/debugging.code.md
new file mode 100644
index 00000000..b4ba01d2
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/debugging.code.md
@@ -0,0 +1,18 @@
+# Отладка
+
+Kohana включает несколько удобных инструментов для отладки Вашего приложения.
+
+Основной из них - [Kohana::debug]. Этот простой метод показывает любое количество переменных, аналогично [var_export] или [print_r], но с использованием HTML для дополнительного форматирования.
+
+ // Показывает дамп переменных $foo и $bar
+ echo Kohana::debug($foo, $bar);
+
+Kohana также предоставляет метод для отображения исходного кода отдельного файла, используя [Kohana::debug_source].
+
+ // Показывает текущую линию исходного кода
+ echo Kohana::debug_source(__FILE__, __LINE__);
+
+Если вы хотите показать информацию о файлах приложения, не показывая реального пути, вы можете использвать [Kohana::debug_path]:
+
+ // Показывает "APPPATH/cache" вместо реального пути
+ echo Kohana::debug_file(APPPATH.'cache');
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/debugging.errors.md b/includes/kohana/modules/userguide/guide/ru-ru/debugging.errors.md
new file mode 100644
index 00000000..7ec04b04
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/debugging.errors.md
@@ -0,0 +1,22 @@
+# Обработка ошибок/исключений
+
+Kohana предоставляет обработчик как для исключений, так и для ошибок (он превращает ошибку в исключение с помощью стандартного PHP-класса [ErrorException](http://php.net/errorexception)). Обработчик показывает множество подробностей и внутреннее состояние приложения:
+
+1. Класс исключения
+2. Уровень ошибки
+3. Текст ошибки
+4. Исходный код, вызвавший ошибку, соответствующая строка подсвечивается
+5. [Трассировка](http://php.net/debug_backtrace) хода выполнения
+6. Подключенные файлы, загруженные расширения и глобальные переменные
+
+## Пример
+
+Нажмите по любой ссылке для раскрытия блока дополнительной информации:
+
+
{{userguide/examples/error}}
+
+## Отключение обработчика ошибок/исключений
+
+Если Вы не хотите использовать встроенный обработчик ошибок, отключите его с помощью [Kohana::init]:
+
+ Kohana::init(array('errors' => FALSE));
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/debugging.profiling.md b/includes/kohana/modules/userguide/guide/ru-ru/debugging.profiling.md
new file mode 100644
index 00000000..020bac18
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/debugging.profiling.md
@@ -0,0 +1,20 @@
+# Профилирование
+
+Kohana предлагает очень простой способ для отображения статистики Вашего приложения:
+
+1. Системных вызовов [Kohana]
+2. Запросов (Requests)
+3. Запросов к [базам данных](Database)
+4. Среднего времени выполнения Вашего приложения
+
+## Пример
+
+Вы можете отобразить или собрать текущую статистику [профилировщика](profiler) в любое время:
+
+
+
+
+
+## Что получится
+
+{{profiler/stats}}
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/features.md b/includes/kohana/modules/userguide/guide/ru-ru/features.md
new file mode 100644
index 00000000..76f3fa7c
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/features.md
@@ -0,0 +1 @@
+Эта страница перечисляет возможности Kohana v3
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/menu.md b/includes/kohana/modules/userguide/guide/ru-ru/menu.md
new file mode 100644
index 00000000..d8cf8eee
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/menu.md
@@ -0,0 +1,32 @@
+1. **Первые шаги**
+ - [Что такое Kohana?](about.kohana)
+ - [Соглашения и стили](about.conventions)
+ - [Модель-Представление-Контроллер](about.mvc)
+ - [Файловая система](about.filesystem)
+ - [Порядок выполнения](about.flow)
+ - [Установка](about.install)
+ - [Обновление](about.upgrading)
+ - [Обзор API](api)
+2. **Основные операции**
+ - [Настройка](using.configuration)
+ - [Автозагрузка](using.autoloading)
+ - [Представление и HTML](using.views)
+ - [Сессии и Cookies](using.sessions)
+ - [Сообщения (Messages)](using.messages)
+ - [Интернационализация](using.translation)
+3. **Отладка**
+ - [Отладка кода](debugging.code)
+ - [Обработка ошибок](debugging.errors)
+ - [Профилирование](debugging.profiling)
+4. **Безопасность**
+ - [XSS](security.xss)
+ - [Валидация](security.validation)
+ - [Cookies](security.cookies)
+ - [База данных](security.database)
+5. **Обучение**
+ - [Hello, World](tutorials.helloworld)
+ - [Маршруты, URL и ссылки](tutorials.urls)
+ - [Очистка URL, ЧПУ](tutorials.removeindex)
+ - [Базы данных](tutorials.databases)
+ - [ORM](tutorials.orm)
+ - [Работа с Git](tutorials.git)
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/security.cookies.md b/includes/kohana/modules/userguide/guide/ru-ru/security.cookies.md
new file mode 100644
index 00000000..13a8480d
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/security.cookies.md
@@ -0,0 +1,3 @@
+# Безопасность Cookie
+
+[!!] заглушка
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/security.database.md b/includes/kohana/modules/userguide/guide/ru-ru/security.database.md
new file mode 100644
index 00000000..504a2740
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/security.database.md
@@ -0,0 +1,3 @@
+# Безопасность Database
+
+[!!] заглушка
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/security.validation.md b/includes/kohana/modules/userguide/guide/ru-ru/security.validation.md
new file mode 100644
index 00000000..4c0d21bb
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/security.validation.md
@@ -0,0 +1,243 @@
+# Валидация
+
+С использованием [Validate] класса можно произвести валидацию любого массива. По ключу значения массива ("наименование поля" для валидатора), к объекту валидации можно добавить ярлыки, фильтры, правила и функции обратного вызова.
+
+labels (ярлыки)
+: Ярлык - это удобочитаемая интерпретация имени поля.
+
+filters (фильтры)
+: Фильтр видоизменяет значение поля перед применением к нему правил и функций обратного вызова.
+
+rules (правила)
+: Правило - это проверка значения поля, которая возвращает `TRUE` или `FALSE` в результате проверки. Если правило возвращает `FALSE`, то полю будет добавлена информация об ошибке.
+
+callbacks (функция обратного вызова)
+: Функция обратного вызова - это пользовательский метод, который имеет доступ до содержания объекта валидации. Возвращаемое значение функции игнорируется, ввиду этого, функция должна сама добавлять ошибку к полу объекта валидации, используя метод [Validate::error], при возникновении ошибки.
+
+[!!] Заметьте, что функции обратного вызова объекта [Validate] и функции обратного вызова PHP ([PHP callbacks](http://php.net/manual/language.pseudo-types.php#language.types.callback)) - это не одно и то же.
+
+Если вместо имени поля при добавлении фильтра, правила или функции обратного вызова использовать значение `TRUE`, то этот фильтр, правило или функция будет применена ко всем полям объекта Validate.
+
+**В мерах предосторожности, объект [Validate] удалит все поля из массива, которым не присвоен ярлык, фильтр, правило или функция обратного вызова. Это предотвращает доступ до полей, которые не участвуют в валидации.**
+
+Создание объекта валидации производится с использованием метода [Validate::factory]:
+
+ $post = Validate::factory($_POST);
+
+[!!] Далее в данном руководстве будет использован объект `$post`. Как пример, будет рассмотрена валидация регистрации нового пользователя.
+
+### Стандартные правила
+
+Класс валидации содержит следующие правила:
+
+Наименование правила | Действие
+------------------------- |-------------------------------------------------
+[Validate::not_empty] | Значение не должно быть пустым
+[Validate::regex] | Проверяется значение на совпадение с регулярным выражением
+[Validate::min_length] | Минимальная длина значения (минимальное количество знаков)
+[Validate::max_length] | Максимальная длина значения (максимальное количество знаков)
+[Validate::exact_length] | Длина значения должна быть равна указанному числу
+[Validate::email] | Значение должно представлять собой emal адрес
+[Validate::email_domain] | Проверяет существование email домена
+[Validate::url] | Значение должно представлять собой URL
+[Validate::ip] | Значение должно представлять собой IP адрес
+[Validate::phone] | Значение должно представлять собой номер телефона
+[Validate::credit_card] | Значение должно представлять собой номер кредитной карты
+[Validate::date] | Значение должно представлять собой дату (и время)
+[Validate::alpha] | Допустимы только буквенные значения
+[Validate::alpha_dash] | Допустимы значения, состоящие из букв и тире
+[Validate::alpha_numeric] | Допустимы только буквенные и числовые значения
+[Validate::digit] | Значение должно представлять собой целое число
+[Validate::decimal] | Значение должно представлять собой число десятичное число или число с плавающей точкой
+[Validate::numeric] | Допустимы только цифровые символы
+[Validate::range] | Значение должно быть в указанных пределах
+[Validate::color] | Значение должно представлять собой правильное значение цвета в HEX
+[Validate::matches] | Значение должно совпадать со значением другого поля
+
+[!!] Любой метод [Validate] класса может быть использован как правило валидации без определения полного обратного вызова (callback). Например, добавление `'not_empty'` - то же самое, что и `array('Validate', 'not_empty')`.
+
+## Добавление фильтров
+
+Фильтр валидации задаётся как имя поля, метод или функция (используя синтаксис [PHP callback](http://ru2.php.net/callback)) и массив параметров:
+
+ $object->filter($field, $callback, $parameters);
+
+Фильтры изменяют значение поля перед проверкой правилами или функциями обратного вызова.
+
+Для того, чтобы конвертировать значение поля "username" в нижний регистр:
+
+ $post->filter('username', 'strtolower');
+
+Если мы хотим удалить все пробелы до и после значения у *всех* полей:
+
+ $post->filter(TRUE, 'trim');
+
+## Добавление правил
+
+Все правила валидации задаются как имя поля, метод или функция (используя синтаксис [PHP callback](http://ru2.php.net/callback)) и массива параметров:
+
+ $object->rule($field, $callback, $parameters);
+
+Для начала, инициируем валидацию `$_POST` массива, который содержит информацию о регистрации:
+
+ $post = Validate::factory($_POST);
+
+Далее, нам необходимо обработать полученные данные, используя [Validate] класс. Во-первых, добавим несколько правил:
+
+ $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');
+
+В виде правила можно использовать практически все функции PHP. Например, можно проверить, верное ли значение ввёл пользователь на SSL вопрос:
+
+ $post->rule('use_ssl', 'in_array', array(array('yes', 'no')));
+
+Имейте в виду, что все параметры должны быть в виде массива! Если параметры были не в виде массива, то функция `in_array` была бы вызвана как `in_array($value, 'yes', 'no')`, что привело бы к PHP ошибке.
+
+Пользовательские правила могут быть добавлены, используя синтаксис [PHP callback](http://ru2.php.net/callback):
+
+ $post->rule('username', array($model, 'unique_username'));
+
+Метод `$model->unique_username()` определяется следующим образом:
+
+ public function unique_username($username)
+ {
+ // Проверка на то, имеется ли указанное значение username в БД
+ return ! DB::select(array(DB::expr('COUNT(username)'), 'total'))
+ ->from('users')
+ ->where('username', '=', $username)
+ ->execute()
+ ->get('total');
+ }
+
+[!!] Пользовательские правила позволяют производить большое количество дополнительных проверок и могут быть использованы для различных целей множество раз. Зачастую, они создаются как методы модели, однако, если необходимо, могут быть определены в любом классе.
+
+## Добавление функций обратного вызова (callbacks)
+
+Все функции обратного вызова (callback) определяются как имя поля, метод или функция (используя синтаксис [PHP callback](http://ru2.php.net/callback)) и массива параметров:
+
+ $object->callback($field, $callback, $parameters);
+
+[!!] До версии kohana 3.0.7, функции обратного вызова не обрабатывали входных параметров, в отличие от фильтров и правил. Если Вы используете старую версию, эти параметры будут проигнорированы.
+
+Пароль пользователя должен быть захэширован, если он прошёл валидацию, поэтому это можно сделать, используя функцию обратного вызова:
+
+ $post->callback('password', array($model, 'hash_password'));
+
+Это подразумевает, что метод `$model->hash_password()` будет работать следующим образом:
+
+ public function hash_password(Validate $array, $field)
+ {
+ if ($array[$field])
+ {
+ // Хэшировать пароль, если он присутствует
+ $array[$field] = sha1($array[$field]);
+ }
+ }
+
+# Полный пример
+
+Во-первых, нам понадобится представление ([View]), которое содержит HTML форму и которое будет располагаться в `application/views/user/register.php`:
+
+
+
+
В целях безопасности, при проведении платежей всегда используется протокол SSL.
+
+
+
+
+
+[!!] В этом примере повсеместно используется хелпер [Form]. Использование [Form] вместо формирования HTML кода формы вручную, гарантирует корректную обработку входящих данных. Если Вы предпочитаете писать HTML собственноручно, используйте метод [HTML::chars], чтобы обезопасить приложение от введённых пользователем данных.
+
+Во-вторых, нам потребуется контроллер и действие этого контроллера, чтобы произвести регистрацию, которые будут располагаться в файле `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())
+ {
+ // Данные прошли проверку, теперь можно зарегистрировать пользователя
+ $user->register($post);
+
+ // Всегда делайте редирект после успешного POST запроса, чтобы избежать
+ // повторного ввода информации при обновлении страницы
+ $this->request->redirect('user/profile');
+ }
+
+ // Валидация не прошла, собираем ошибки
+ $errors = $post->errors('user');
+
+ // Отображаем форму регистрации
+ $this->request->response = View::factory('user/register')
+ ->bind('post', $post)
+ ->bind('errors', $errors);
+ }
+
+ }
+
+Так же нам потребуется модель пользователя, которую поместим в `application/classes/model/user.php`:
+
+ class Model_User extends Model {
+
+ public function register($array)
+ {
+ // Создание нового пользователя в БД
+ $id = DB::insert(array_keys($array))
+ ->values($array)
+ ->execute();
+
+ // Сохраняем id нового пользователя в cookie
+ cookie::set('user', $id);
+
+ return $id;
+ }
+
+ }
+
+Вот и всё, у нас имеется готовый пример для регистрации пользователя, который правильно проверяет введённые пользователем данные!
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/security.xss.md b/includes/kohana/modules/userguide/guide/ru-ru/security.xss.md
new file mode 100644
index 00000000..428c958c
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/security.xss.md
@@ -0,0 +1,15 @@
+# Защита от межсайтового скриптинга (XSS)
+
+Первый шаг для предотвращения [XSS](http://ru.wikipedia.org/wiki/Xss) атак - необходимо знать, когда и от чего себя защищать. XSS может быть выполнен тогда, когда он отображается внутри HTML-содержимого, иногда после введения данных через форму или выборки из базы данных. Любая глобальная переменная, содержащая информацию от клиента, может быть заражена. Это касается данных из `$_GET`, `$_POST` и `$_COOKIE`.
+
+## Предотвращение
+
+Существует несколько простых правил, защищающих ваше приложение от XSS. Первое - использование метода [Security::xss] для очистки любых входящих данных, полученных из глобальных переменных. Если вы не ожидаете HTML код в переменной, используйте [strip_tags](http://php.net/strip_tags) для удаления нежелательных HTML тэгов.
+
+[!!] Если вы позволяете пользователям передавать HTML в ваше приложение, настоятельно рекомендуется использовать утилиты для очистки HTML кода, такие как [HTML Purifier](http://htmlpurifier.org/) или [HTML Tidy](http://php.net/tidy).
+
+Второй шаг - всегда очищать данные перед их вставкой в HTML код. Класс [HTML] предоставляет возможность создавать наиболее употребляемые тэги, включая ссылки на скрипты и таблицы стилей, гиперссылки, изображения, ссылки на адреса электронной почты (mailto). Все подозрительные данные должны быть экранированы с помощью [HTML::chars].
+
+## Ссылки
+
+* [OWASP XSS Cheat Sheet](http://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet)
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/start.autoloading.md b/includes/kohana/modules/userguide/guide/ru-ru/start.autoloading.md
new file mode 100644
index 00000000..b3ad1dc8
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/start.autoloading.md
@@ -0,0 +1,17 @@
+# Автозагрузка
+
+Kohana использует все преимущества [автозагрузки](http://php.net/manual/language.oop5.autoload.php) в PHP. Это позволяет не использовать функций [include](http://php.net/include) или [require](http://php.net/require) перед использованием класса.
+
+Классы подгружаются с помощью метода [Kohana::auto_load], который использует простое соотношение имени класса с именим файла этого класса:
+
+1. Классы располагаются в категории `classes/` в [файловой системе](start.filesystem) фреймворка
+2. Все нижние подчёркивания в имени класса конвертируются в слеши
+2. Имя файла пишется в нижнем регистре
+
+При вызове ещё не подгружённого класса (например, `Session_Cookie`), Kohana будет искать с помощью [Kohana::find_file] файл `classes/session/cookie.php`.
+
+## Пользовательские автозагрузчики
+
+[!!] Автозагрузчик по-умолчанию активирован в `application/bootstrap.php`.
+
+Дополнительные загрузчики классов могут быть добавлены с использованием [spl_autoload_register](http://php.net/spl_autoload_register).
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/start.configuration.md b/includes/kohana/modules/userguide/guide/ru-ru/start.configuration.md
new file mode 100644
index 00000000..45ccc566
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/start.configuration.md
@@ -0,0 +1,92 @@
+# Общие настройки
+
+[!!] TODO: описание преимуществ статических свойств конфигурации
+
+## Настройки ядра
+
+Первой задачей конфигурирования новой установки Kohana является изменение параметров [Kohana::init] в `application/bootstrap.php`. Вот эти параметры:
+
+`boolean` errors
+: Использование встроенного обработчика ошибок и исключений. (По-умолчанию: `TRUE`) Установите в `FALSE`, чтобы отменить перехват ошибок и исключений фреймворком.
+
+`boolean` profile
+: Использовать возможности встроенного бенчмаркинга. (По-умолчанию: `TRUE`) Установите в `FALSE`, чтобы отменить профилирование. Для увеличения производительности на стадии production данный параметр рекомендуется отключать.
+
+`boolean` caching
+: Кэширование информации о расположении файлов между запросами. (По-умолчанию: `FALSE`) Установите в `TRUE`, чтобы включить кэширование значений абсолютных путей исполняемых файлов. Это значительно ускоряет [Кохана::find_file] и может иногда оказать серьезное влияние на производительность. Используйте на этапе production или для тестирования.
+
+`string` charset
+: Кодировка, используемая во всех операциях ввода-вывода. (По-умолчанию: `"utf-8"`) Должна поддерживаться как [htmlspecialchars](http://php.net/htmlspecialchars), так и [iconv](http://php.net/iconv).
+
+`string` base_url
+: Базовый URL для приложения. (По-умолчанию: `"/"`) Может быть как абсолютным, так и относительным URL. Например, "http://example.com/kohana/" или просто "/kohana/": подходят оба варианта.
+
+`string` index_file
+: Имя PHP файла, который запускает приложение (фронтенд). (По-умолчанию: `"index.php"`) Установите в `FALSE`, если намереваетесь использовать URL rewriting.
+
+`string` cache_dir
+: Директория для хранения файлового кэша. (По-умолчанию: `"application/cache"`) Директория должна быть **доступна для записи**.
+
+## Настройки Cookie
+
+Перед запуском production-версии сайта следует установить значения некоторых статических свойств [Cookie] класса.
+
+`string` salt
+: Уникальная строка salt-значения, которая используется для работы [cookies](security.cookies)
+
+`integer` expiration
+: По-умолчанию: время жизни cookies в секундах
+
+`string` path
+: URL, ограничивающий доступ к cookies
+
+`string` domain
+: Домен, ограничивающий доступ к cookies
+
+`boolean` secure
+: Позволить использовать cookies только по протоколу HTTPS
+
+`boolean` httponly
+: Позволить использовать cookies только по протоколу HTTP (также закрывается доступ через Javascript)
+
+# Конфигурационные файлы
+
+Настройки хранятся в виде PHP файлов примерно такого вида:
+
+~~~
+ 'value',
+ 'options' => array(
+ 'foo' => 'bar',
+ ),
+);
+~~~
+
+Если конфигурационный файл был назван `myconf.php`, то для доступа к нему можно использовать следующий код:
+
+~~~
+$config = Kohana::config('myconf');
+$options = $config['options'];
+~~~
+
+[Kohana::config] позволяет так же использовать "пути с точкой" для доступа к отдельным ключам конфигурационного массива.
+
+Для получения массива "options":
+
+~~~
+$options = Kohana::config('myconf.options');
+~~~
+
+Для получения значения ключа "foo" массива "options":
+
+~~~
+$foo = Kohana::config('myconf.options.foo');
+~~~
+
+Вы можете работать с настроечными массивами как с объектами, если это будет удобнее:
+
+~~~
+$options = Kohana::config('myconf')->options;
+~~~
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/start.controllers.md b/includes/kohana/modules/userguide/guide/ru-ru/start.controllers.md
new file mode 100644
index 00000000..189c7390
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/start.controllers.md
@@ -0,0 +1,104 @@
+# Контроллеры
+
+Контроллеры являются промежуточным звеном между моделью и представлением. Они передают информацию в модель для изменения данных, и запрашивают информацию от модели. Например, операции работы с базой данных: вставка (insert), изменение (update) и удаление (delete) как операции редактирования данных, и выборка (select) для извлечения данных. Информацию, полученную от модели, контроллеры перенаправляют в представления, которые содержат конечный результат, предназначенный для отображения пользователям.
+
+Контроллеры вызываются с помощью URL. За более подробной информацией обратитесь к разделу [URL и ссылки](start.urls).
+
+
+
+## Название контроллера и его содержание
+
+Имя класса контроллера должно соответствовать имени файла.
+
+**Соглашения при использовании контроллеров**
+
+* имя файла должно быть в нижнем регистре, например: `articles.php`
+* файл контроллера должен располагаться в (под-)директории **classes/controller**, например: `classes/controller/articles.php`
+* имя класса контроллера должно соответствовать имени файла, начинаться с заглавной буквы и должно начинаться с префикса **Controller_**, например: `Controller_Articles`
+* класс контроллера должен быть потомком класса Controller.
+* методы контроллера, предназначенные для вызова через URI, должны начинаться с префикса **action_** (например: `action_do_something()` )
+
+
+
+### Пример простейшего контроллера
+
+Создадим простой контроллер, который будет выводить на экран 'Hello World!'.
+
+**application/classes/controller/article.php**
+~~~
+ MODPATH.'database',
+ ...
+ ));
+
+## Настройка {#configuration}
+
+После подключения модуля необходимо создать файл настроек, чтобы модуль знал как соединиться с базой данных. Пример конфигурационного файла можно найти в `modules/database/config/database.php`.
+
+Структура группы настроек базы данных ("instance") выглядит следующим образом:
+
+ 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,
+ ),
+
+[!!] В одном конфигурационном файле можно определить несколько таких групп.
+
+Очень важно понимать каждый параметр конфигурации.
+
+INSTANCE_NAME
+: Соединения могут быть названы как Вы захотите, но одно из них обязательно должно называться "default" (группа по умолчанию).
+
+DATABASE_TYPE
+: Один из установленных драйверов баз данных. Kohana поставляется с драйверами "mysql" и "pdo".
+
+CONNECTION_ARRAY
+: Специфические настройки драйвера для соединения с БД. (Настройки драйвера описаны [ниже](#connection_settings).)
+
+TABLE_PREFIX
+: Префикс, который будет добавлен к названиям таблиц классом [query builder](#query_building).
+
+QUERY_PROFILING
+: Включает [профилирование](debugging.profiling) запросов к БД.
+
+### Пример
+
+Ниже описаны два соединения с MySQL, одно локальное, а другое удаленное.
+
+ 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}
+
+Каждый драйвер БД имеет свои настройки соединения.
+
+#### MySQL
+
+БД MySQL поддерживает следующие параметры массива `connection`:
+
+Тип | Параметр | Описание | Значение по умолчанию
+----------|------------|-----------------------------| -------------------------
+`string` | hostname | Имя сервера или IP-адрес | `localhost`
+`integer` | port | Номер порта | `NULL`
+`string` | socket | сокет UNIX | `NULL`
+`string` | username | Имя пользователя | `NULL`
+`string` | password | Пароль | `NULL`
+`boolean` | persistent | Постоянное соединение | `FALSE`
+`string` | database | Имя базы данных (схемы) | `kohana`
+
+#### PDO
+
+База данных PDO database принимает следующие опции массива `connection`:
+
+Тип | Параметр | Описание | Значение по умолчанию
+----------|------------|-----------------------------| -------------------------
+`string` | dsn | Идентификатор источника PDO | `localhost`
+`string` | username | Имя пользователя | `NULL`
+`string` | password | Пароль | `NULL`
+`boolean` | persistent | Постоянное соединение | `FALSE`
+
+!! Если Вы используете PDO и не уверены, что прописывать в параметре `dsn`, ознакомьтесь с [PDO::__construct](http://php.net/pdo.construct).
+
+## Соединения и сущности {#connections}
+
+Каждая группа настроек связана с экземпляром базы данных ("сущность"). Каждая сущность может быть получена через вызов [Database::instance]:
+
+ $default = Database::instance();
+ $remote = Database::instance('remote');
+
+Чтобы порвать соединение с базой данных, просто уничтожьте объект:
+
+ unset($default, Database::$instances['default']);
+
+Если Вы хотите разорвать соединения со всеми сущностями за раз:
+
+ Database::$instances = array();
+
+## Создаем запросы {#making_queries}
+
+Существует два способа создать запросы. Простейший путь - использование [Database_Query] для создания запросов, через [DB::query]. Эти запросы называются "подготовленные выражения" и позволяют устанавливать параметры, которые автоматически экранируются. Второй путь - построение через специальные методы. Это возможно с помощью объекта [query builder](#query_building).
+
+[!!] Все запросы выполняются методом `execute`, который принимает объект [Database] или имя сущности. Смотри [Database_Query::execute].
+
+### Подготовленные выражения
+
+Подготовленные выражения позволяют писать SQL-запросы вручную, в то же время значения будут автоматически экранированы, чтобы избежать [SQL-инъекций](http://wikipedia.org/wiki/SQL_Injection). Создать запрос просто:
+
+ $query = DB::query(Database::SELECT, 'SELECT * FROM users WHERE username = :user');
+
+Фабричный метод [DB::query] создает новый класс [Database_Query] и возвращает его, поддерживая цепочки вызовов. Запрос содержит параметр `:user`, которому будет назначено значение:
+
+ $query->param(':user', 'john');
+
+[!!] Имя параметра может быть любой строкой, в дальнейшем оно будет заменено функцией [strtr](http://php.net/strtr). Рекомендуется **не** использовать знак доллара в составе имени параметра во избежание путаницы.
+
+Если Вы хотите увидеть SQL, предназначенный для выполнения, просто преобразуйте объект в строку:
+
+ echo Kohana::debug((string) $query);
+ // выведет:
+ // SELECT * FROM users WHERE username = 'john'
+
+Также Вы можете изменить параметр `:user`, снова вызвав [Database_Query::param]:
+
+ $query->param(':user', $_GET['search']);
+
+[!!] Для задания нескольких параметров за раз используйте [Database_Query::parameters].
+
+После установки всех параметров можно выполнить запрос:
+
+ $query->execute();
+
+Также допустимо привязать параметр к переменной, используя [ссылки на переменные]((http://php.net/language.references.whatdo)). Это может стать очень полезным при многократном выполнении схожих запросов:
+
+ $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();
+ }
+
+В данном примере переменные `$username` и `$password` меняются в каждой итерации цикла `foreach`. Когда переменные меняются, также изменяют значение и параметры запроса `:user` и `:pass`. Правильное и уместное использование привязки параметров может сделать код более компактным.
+
+### Конструктор запросов {#query_building}
+
+Динамическое создание запросов с использованием объектов и методов позволяет писать запросы очень быстро и предсказуемо. Построитель запросов также заключает в кавычки идентификаторы (имена таблиц и полей), также как и экранирует значения.
+
+[!!] На данный момент невозможно комбинировать построитель запросов с подготовленными выражениями.
+
+#### Выборка (SELECT)
+
+Каждый тип запросов представлен отдельным классом со своими методами. К примеру, чтобы создать запрос типа SELECT, используем [DB::select]:
+
+ $query = DB::select()->from('users')->where('username', '=', 'john');
+
+По умолчанию, [DB::select] будет запрашивать все поля (`SELECT * ...`), но можно указать, какие столбцы извлекать:
+
+ $query = DB::select('username', 'password')->from('users')->where('username', '=', 'john');
+
+А теперь посмотрим, к чему привела эта цепочка вызовов. Сперва мы создаем новый объект выборки методом [DB::select]. Далее, устанавливаем таблицу(ы) с помощью метода `from`. И напоследок, ищем конкретные записи через метод `where`. Можно посмотреть генерируемый код SQL просто преобразовывая объект к строке:
+
+ echo Kohana::debug((string) $query);
+ // Покажет:
+ // SELECT `username`, `password` FROM `users` WHERE `username` = 'john'
+
+Обратили внимание, что имена полей и таблиц автоматически экранированы, также как и значения? Это одно из ключевых преимуществ использования построителя запросов.
+
+Также допустимо создавать псевдонимы `AS` для выборки:
+
+ $query = DB::select(array('username', 'u'), array('password', 'p'))->from('users');
+
+Сгенерируется следующий SQL-запрос:
+
+ SELECT `username` AS `u`, `password` AS `p` FROM `users`
+
+#### Вставка (INSERT)
+
+Чтобы создать записи в базе данных, используйте [DB::insert], создающий запросы INSERT:
+
+ $query = DB::insert('users', array('username', 'password'))->values(array('fred', 'p@5sW0Rd'));
+
+Запрос сформирует код:
+
+ INSERT INTO `users` (`username`, `password`) VALUES ('fred', 'p@5sW0Rd')
+
+#### Обновление (UPDATE)
+
+Для редактирования существующей записи предназначен метод [DB::update], он возвращает запрос UPDATE:
+
+ $query = DB::update('users')->set(array('username' => 'jane'))->where('username', '=', 'john');
+
+В результате получим запрос:
+
+ UPDATE `users` SET `username` = 'jane' WHERE `username` = 'john'
+
+#### Удаление (DELETE)
+
+Для удаления записи используется [DB::delete], он создает запрос DELETE:
+
+ $query = DB::delete('users')->where('username', 'IN', array('john', 'jane'));
+
+Получим следующий запрос:
+
+ DELETE FROM `users` WHERE `username` IN ('john', 'jane')
+
+#### Функции работы с базами данных {#database_functions}
+
+Иногда Вы можете столкнуться с ситуацией, когда надо вызвать `COUNT` или другую функцию СУБД внутри запроса. Построитель запросов позволяет использовать эти функции двумя способами. Первый - применение кавычек внутри псевдонимов:
+
+ $query = DB::select(array('COUNT("username")', 'total_users'))->from('users');
+
+Это выглядит почти также, как и стандартные псевдонимы, но имя поля обрамлено двойными кавычками. Каждый раз, когда значение в двойных кавычках обнаруживается внутри имени столбца, **только** часть внутри этих кавычек будет экранирована. Сгенерируется код SQL:
+
+ SELECT COUNT(`username`) AS `total_users` FROM `users`
+
+#### Сложные выражения
+
+"Закавыченные" псевдонимы могут решить многие проблемы, но время от времени может понадобиться сложное выражение. В таких случаях надо использовать выражения, создаваемые методом [DB::expr]. Выражение используется для прямого вывода, экранирование не происходит.
+
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/tutorials.git.md b/includes/kohana/modules/userguide/guide/ru-ru/tutorials.git.md
new file mode 100644
index 00000000..4d158f10
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/tutorials.git.md
@@ -0,0 +1,149 @@
+# Работа с Git
+
+Kohana применяет [git](http://git-scm.com/) для контроля версий и [github](http://github.com/kohana) для совместной разработки. Данная статья покажет Вам как использовать git и github для создания простейшего приложения.
+
+## Установка и настройка Git на Вашей машине
+
+### Установка Git
+
+- для OSX: [Git-OSX](http://code.google.com/p/git-osx-installer/)
+- для Windows: [Msygit](http://code.google.com/p/msysgit/)
+- Или загрузите git с [официального сайта](http://git-scm.com/) и установите его самостоятельно (подробности установки смотрите на сайт Git)
+
+### Основные глобальные настройки
+
+ git config --global user.name "Your Name"
+ git config --global user.email "youremail@website.com"
+
+### Дополнительные, но предпочтимые настройки
+
+Для лучшей визуализации команд и репозиторий в командной строке, используйте следующее:
+
+ git config --global color.diff auto
+ git config --global color.status auto
+ git config --global color.branch auto
+
+### Настройка автозавершения
+
+[!!] Следующие строки применимы только для OSX машин
+
+Эти строки сделают всю грязную работу за вас и после этого Вы сможете спокойно работать с git-окружением, используя автозавершение команд:
+
+ 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
+
+### Всегда используйте LF в окончаниях строк
+
+Это соглашение, которое было принято Kohana сообществом. Выставьте эту настройку во имя Вашего Господа, особенно если хотите участвовать в kohana коммьюнити!
+
+ git config --global core.autocrlf input
+ git config --global core.savecrlf true
+
+[!!] Более подробную информацию об окончаниях строк читайте на [GitHub'е](http://help.github.com/dealing-with-lineendings/)
+
+### Информация для размышления
+
+- [Git скринкасты](http://www.gitcasts.com/)
+- [Git справочник](http://gitref.org/)
+- [Pro Git book](http://progit.org/book/)
+
+## Основная структура
+
+[!!] Предполагается, что Ваш web-сервер уже настроен, и Вы будете использовать адрес для нового приложения.
+
+Откройте консоль, перейдите в пустую директорию `gitorial` и выполните команду `git init`. Она создаст заготовку под новый git-репозиторий.
+
+Далее, мы создадим подпроект ([submodule](http://www.kernel.org/pub/software/scm/git/docs/git-submodule.html)) для директории `system`. Откройте и скопируйте значение Clone URL:
+
+
+
+Используйте скопированный URL для создания подпроекта `system`:
+
+ git submodule add git://github.com/kohana/core.git system
+
+[!!] Будет создана связь с текущей разрабатываемой версией следующего стабильного релиза. Эта версия должна практически всегда быть безопасна для использования, иметь тот же API, что в текущем стабильном релизе с исправленными ошибками.
+
+Теперь добавьте остальные необходимые подпроекты. Например, если нужен модуль [Database](http://github.com/kohana/database):
+
+ git submodule add git://github.com/kohana/database.git modules/database
+
+После добавления модули должны быть проиниализированы:
+
+ git submodule init
+
+Теперь мы должны зафиксировать текущее состояние:
+
+ git commit -m 'Added initial submodules'
+
+Следующий шаг - создание структуры папок для приложения. Вот необходимый минимум:
+
+ mkdir -p application/classes/{controller,model}
+ mkdir -p application/{config,views}
+ mkdir -m 0777 -p application/{cache,logs}
+
+Если запустить команду `find application`, Вы должны увидеть такой список:
+
+ application
+ application/cache
+ application/config
+ application/classes
+ application/classes/controller
+ application/classes/model
+ application/logs
+ application/views
+
+Мы не хотим, чтобы git обрабатывал логи или файлы кэша, поэтому добавим файл `.gitignore` в соответствуюшие директории logs и cache. Теперь все нескрытые (non-hidden) файлы будут проигнорированы git'ом:
+
+ echo '[^.]*' > application/{logs,cache}/.gitignore
+
+[!!] Git пропускает пустые папки, так что добавляя файл `.gitignore`, мы дополнительно заставляем git учитывать данную директорию, но не файлы внутри нее.
+
+Теперь загружаем файлы `index.php` и `bootstrap.php`:
+
+ 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
+
+Фиксируем эти изменения:
+
+ git add application
+ git commit -m 'Added initial directory structure'
+
+Это все необходимые изменения. Теперь у Вас имеется приложение, использующее Git для контроля версий.
+
+## Обновление подмодулей
+
+Скорее всего, на определенном этапе Вы захотите обновить свои подпроекты. Чтобы обновить все модули до последних версий `HEAD`, введите:
+
+ git submodule foreach 'git checkout master && git pull origin master'
+
+Для синхронизации подпроекта `system` выполните:
+
+ cd system
+ git checkout master
+ git pull
+ cd ..
+ git add system
+ git commit -m 'Updated system directory'
+
+Обновление отдельного модуля до определенной ревизии:
+
+ cd modules/database
+ git fetch
+ git checkout fbfdea919028b951c23c3d99d2bc1f5bbeda0c0b
+ cd ../..
+ git add database
+ git commit -m 'Updated database module'
+
+Заметьте, что можно так же загрузить коммит по официальной метке релиза. Например:
+
+ git checkout 3.0.7
+
+Для того, чтобы увидеть все метки, просто запустите `git tag` без дополнительных аргументов.
+
+Вот и всё!
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/tutorials.helloworld.md b/includes/kohana/modules/userguide/guide/ru-ru/tutorials.helloworld.md
new file mode 100644
index 00000000..48d98119
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/tutorials.helloworld.md
@@ -0,0 +1,105 @@
+# Hello, World
+
+Каждый фреймворк располагает примером написания приложения hello world, так что не будем нарушать традицию!
+
+Мы начнем с создания простейшего hello world, а затем расширим его согласно принципам MVC.
+
+## Основа
+
+Сперва надо создать контроллер, который Kohana будет использовать для обработки запроса
+
+Создайте файл `application/classes/controller/hello.php` в директории application и вставьте туда такой текст:
+
+ template->message = 'hello, world!';
+ }
+ }
+
+`extends Controller_Template`
+: Теперь мы расширяем шаблонный контроллер (template controller), что делает работу контроллера с представлениями более удобной.
+
+`public $template = 'site';`
+: Шаблонный контроллер должен знать, какое представление использовать. Он автоматически загрузит указанное представление в данную переменную в виде объекта.
+
+`$this->template->message = 'hello, world!';`
+: `$this->template` является ссылкой на наш шаблон. Мы присваиваем переменной "message" значение "hello, world!", и добавляем ее в шаблон template.
+
+А теперь попробуем выполнить наш код...
+
+
{{userguide/examples/hello_world_error}}
+
+По каким-то причинам kohana генерирует ошибку и не хочет показать наше восхитительное сообщение.
+
+Если мы посмотрим на сообщение с ошибкой, то увидим, что библиотека View не смогла найти наш главный шаблон, скорее всего потому что мы его еще не создали - *черт*!
+
+Давайте добавим файл представления `application/views/site.php` для нашего сообщения:
+
+
+
+ We've got a message for you!
+
+
+
+
+
We just wanted to say it! :)
+
+
+
+Если обновить страницу, то мы увидим увидим результаты наших усилий:
+
+
+
+## Этап 3 - Итого!
+
+В данной статье Вы изучили, как создать контроллер и использовать шаблоны для отделения логики от представления.
+
+Очевидно, что это было всего-навсего упрощенное вступление, и оно не отражает даже малой части всех возможностей, доступных при разработке приложений с помощью kohana.
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/tutorials.orm.md b/includes/kohana/modules/userguide/guide/ru-ru/tutorials.orm.md
new file mode 100644
index 00000000..14d2096e
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/tutorials.orm.md
@@ -0,0 +1,312 @@
+# ORM {#top}
+
+Kohana 3.0 включает мощный модуль ORM, который использует паттерн Active Record и автоопределение информации о списке полей БД модели.
+
+Модуль ORM включен в дистрибутив Kohana 3.0, но нуждается в подключении перед его использованием. В файле `application/bootstrap.php` отредактируйте вызов [Kohana::modules] и добавьте модуль ORM:
+
+ Kohana::modules(array(
+ ...
+ 'orm' => MODPATH.'orm',
+ ...
+ ));
+
+## Настройка {#configuration}
+
+ORM требует небольшую настройку перед использованием. Наследуйте Вашу модуль от ORM:
+
+ class Model_User extends ORM
+ {
+ ...
+ }
+
+В примере выше, модель будет искать таблицу `users` в БД по умолчанию.
+
+### Свойства модели, отвечающие за конфигурацию
+
+Следующие свойства используются для настройки каждой модели:
+
+Тип | Название | Описание | Значение по умолчанию
+----------|---------------------|-------------------------------------|---------------------------------
+`string` | _table_name | Используемая таблица БД | `имя модели в единственном числе`
+`string` | _db | Название БД | `default`
+`string` | _primary_key | Поле - первичный ключ | `id`
+`string` | _primary_val | Титульное поле | `name`
+`bool` | _table_names_plural | Имя таблицы во множественном числе | `TRUE`
+`array` | _sorting | Сортировка (столбец => направление) | `primary key => ASC`
+`string` | _foreign_key_suffix | Суффикс внешнего ключа | `_id`
+
+## Использование ORM
+
+### Загрузка записи
+
+Для создания экземпляра модели используйте метод [ORM::factory] или конструктор [ORM::__construct]:
+
+ $user = ORM::factory('user');
+ // или
+ $user = new Model_User();
+
+Конструктор и фабричный метод также поддерживают значение первичного ключа для загрузки конкретной записи:
+
+ // Загружаем пользователя с ID 5
+ $user = ORM::factory('user', 5);
+
+ // Проверяем успешность загрузки объекта пользователя
+ if ($user->loaded()) { ... }
+
+Опционально, Вы можете передать массив с парами ключ => значение для загрузки данных объекта по совпадающим критериям, указанным в массиве:
+
+ // Загрузка пользователя с email joe@example.com
+ $user = ORM::factory('user', array('email' => 'joe@example.com'));
+
+### Поиск записи
+
+ORM поддерживает большинство методов класса [Database] для полноценного поиска данных модели. В свойстве `_db_methods` перечислен полный список поддерживаемых методов. Записи извлекаются после вызовов [ORM::find] или [ORM::find_all].
+
+ // Извлекаем первого активного пользователя по имени Bob
+ $user = ORM::factory('user')
+ ->where('active', '=', TRUE)
+ ->where('name', '=', 'Bob')
+ ->find();
+
+ // Ищем всех активных пользователей по имени Bob
+ $users = ORM::factory('user')
+ ...
+ ->find_all();
+
+Когда Вы запрашиваете список моделей через [ORM::find_all], перебирать его можно аналогично обычным выборкам из БД:
+
+ foreach ($users as $user)
+ {
+ ...
+ }
+
+Мощным инструментом ORM является метод [ORM::as_array], который возвращает полученные записи в виде массива. При использовании совместно с [ORM::find_all], будет возвращён массив всех записей. Хороший пример использование этого метода - когда необходимо передать значения для выпадающего списка:
+
+ // Отображается выпадающий список пользователей
+ // (используется id в качестве значения select option)
+ form::select('user', ORM::factory('user')->find_all()->as_array('id', 'username') ...
+
+### Подсчёт записей
+
+Для получения количества записей для данного запроса, используйте [ORM::count_all].
+
+ // Число активных пользователей
+ $count = ORM::factory('user')->where('active', '=', TRUE)->count_all();
+
+Если требуется подсчитать общее количество пользователей для данного запроса при лимитировании количества возвращаемых записей, используйте метод [ORM::reset] с параметром `FALSE` перед использованием `count_all`:
+
+ $user = ORM::factory('user');
+
+ // Общее число пользователей (reset FALSE предотвращает объект от очистки перез запросом)
+ $count = $user->where('active', '=', TRUE)->reset(FALSE)->count_all();
+
+ // Получаем только первые 10 результатов
+ $users = $user->limit(10)->find_all();
+
+### Доступ к свойствам модели
+
+Все свойства модели доступны через "магические" методы `__get` и `__set`.
+
+ $user = ORM::factory('user', 5);
+
+ // Выводит имя пользователя
+ echo $user->name;
+
+ // Изменяет имя пользователя
+ $user->name = 'Bob';
+
+Для хранения данных/свойств, которые отсутствуют в таблице модели, надо использовать атрибут `_ignored_columns`.
+
+ class Model_User extends ORM
+ {
+ ...
+ protected $_ignored_columns = array('field1', 'field2', ...)
+ ...
+ }
+
+Множественные пары ключ => значение могут быть заданы с использованием метода [ORM::values]:
+
+ $user->values(array('username' => 'Joe', 'password' => 'bob'));
+
+### Создаем и сохраняем записи
+
+Метод [ORM::save] используется как для создания новых записей, так и для обновления существующих.
+
+ // Создаем запись
+ $user = ORM::factory('user');
+ $user->name = 'New user';
+ $user->save();
+
+ // Редактируем запись
+ $user = ORM::factory('user', 5);
+ $user->name = 'User 2';
+ $user->save();
+
+ // Проверяем, сохранена ли запись
+ if ($user->saved()) { ... }
+
+Вы можете обновить множество записей с помощью метода [ORM::save_all]:
+
+ $user = ORM::factory('user');
+ $user->name = 'Bob';
+
+ // Все активные пользователи получат имя 'Bob'
+ $user->where('active', '=', TRUE)->save_all();
+
+#### Использование `Updated` и `Created` для столбцов БД
+
+Свойства `_updated_column` и `_created_column` позволяют производить автоматическое обновление модели при её обновлении и сохранении. Это используется не по-умолчанию. Чтобы использовать эти свойства, следует их указать:
+
+ // date_created является столбцом таблицы, в котором хранится дата создания.
+ // Для сохранения метки времени, используем TRUE
+ protected $_created_column = array('date_created' => TRUE);
+
+ // date_modified является столбцом таблицы, в котором хранится дата изменения.
+ // В этом случае используется строка, определяющая формат для функции date()
+ protected $_updated_column = array('date_modified' => 'm/d/Y');
+
+### Удаление записей
+
+Записи удаляются методами [ORM::delete] и [ORM::delete_all]. Эти методы работают аналогично описанному выше сохранению, за исключением того, что [ORM::delete] принимает необязательный параметр `id` для удаляемой записи.
+
+### Отношения
+
+ORM предоставляет мощную поддержку отношений таблиц. Прочитать про отношения можно в [справочнике по Ruby](http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html)
+
+#### Belongs-To и Has-Many
+
+Допустим, мы работаем со школой, которая имеет много учеников (has many). Каждый студент приписан к одной школе (принадлежит - belongs to). Необходимо определить отношения моделей следующим образом:
+
+ // В модели school
+ protected $_has_many = array('students' => array());
+
+ // В модели student
+ protected $_belongs_to = array('school' => array());
+
+Получаем информацию о школе студента:
+
+ $school = $student->school;
+
+Ищем всех студентов школы:
+
+ // Учтите, что после students следует вызвать метод find_all
+ $students = $school->students->find_all();
+
+ // Чтобы сузить результаты поиска:
+ $students = $school->students->where('active', '=', TRUE)->find_all();
+
+По-умолчанию, ORM будет искать поле `school_id` в таблице модели student. Это можно изменить, используя аттрибут `foreign_key`:
+
+ protected $_belongs_to = array('school' => array('foreign_key' => 'schoolID'));
+
+Внешний ключ будет перегружен как в модели student, так и в school.
+
+#### Has-One
+
+Has-One - это частный случай Has-Many, единственным отличаем которого является то, что в отношении участвует только одна запись. В дополнении к приведённому выше примеру школы, каждая школа будет иметь (has-one) только одного директора, который принадлежит (belongs-to) школе.
+
+ // Inside the school model
+ protected $_has_one = array('principal' => array());
+
+Как и для Belongs-To, Вам не нужно использовать метод `find` для получение объекта, ссылающегося на Has-One объект - это будет сделано автоматически.
+
+#### Has-Many "через" (through)
+
+Отношение Has-Many "through" (так же известное как Has-And-Belongs-To-Many) оспользуется в случае если объект связан с несколькими объектами другого типа и наоборот. Например, студент записан на многие занятия и на занятие ходит много студентов. В этом случаеи используется `промежуточная` таблица. Используем для нашего примера промежуточную таблицу и модель - журнал (`enrollment`).
+
+ // В модели student
+ protected $_has_many = array('classes' => array('through' => 'enrollment'));
+
+ // В модели class
+ protected $_has_many = array('students' => array('through' => 'enrollment'));
+
+Таблица enrollment должна содержать 2 внешних ключа: для занятий `class_id` и для студентов `student_id`. Наименование внешних и дальних ключей (`foreign_key` и `far_key`) могут быть переопределены при определении отношений. Например:
+
+ // В модели student (внешний ключ ссылается на модель student,
+ // в то время, как дальний ключ - на class)
+ protected $_has_many = array(
+ 'classes' => array(
+ 'through' => 'enrollment',
+ 'foreign_key' => 'studentID',
+ 'far_key' => 'classID'
+ ));
+
+ // В модели class
+ protected $_has_many = array(
+ 'students' => array(
+ 'through' => 'enrollment',
+ 'foreign_key' => 'classID',
+ 'far_key' => 'studentID'
+ ));
+
+Определяем в модели enrollment:
+
+ // Журнал принадлежит как студенту, так и занятию
+ protected $_belongs_to = array('student' => array(), 'class' => array());
+
+Для доступа к связанным объектам:
+
+ // Для получение занятий студента
+ $student->classes->find_all();
+
+ // Для получения студентов, записанных на занятие
+ $class->students->find_all();
+
+### Валидация
+
+ORM тесно взимодействует с [Validate] библиотекой, позволяя использовать возможности этого класса в следующих свойствах:
+
+* _rules
+* _callbacks
+* _filters
+* _labels
+
+#### `_rules`
+
+ protected $_rules = array
+ (
+ 'username' => array('not_empty' => array()),
+ 'email' => array('not_empty' => array(), 'email' => array()),
+ );
+
+`username` будет проверяться на то, что значение этого поля не является пустым. `email` поле будет проверено на соответствие значения валидному email адресу. Ввиду возможности передачи дополнительных опций для правил, значения правил задаются как пустые массивы.
+
+#### `_callbacks`
+
+ protected $_callbacks = array
+ (
+ 'username' => array('username_unique'),
+ );
+
+Значение поля `username` будет передано методы `username_unique`. Если метод не существует в текущей модели, то будет вызвана глобальная функция. Вот пример описания этого метода:
+
+ public function username_unique(Validate $data, $field)
+ {
+ // Логика, проверяющая уникальность имени пользователя
+ ...
+ }
+
+#### `_filters`
+
+ protected $_filters = array
+ (
+ TRUE => array('trim' => array()),
+ 'username' => array('stripslashes' => array()),
+ );
+
+`TRUE` Указывает на то, что фильтр `trim` будет применён ко всем полям. Значение поля `username` будет отфильтровано с помощью функции `stripslashes` перед процессом валидации. Ввиду возможности передачи дополнительных опций для фильтров, значения фильтра задаются как пустые массивы.
+
+#### Проверка объекта
+
+Для проверки объекта, используйте [ORM::check]:
+
+ // Задание значений объекта и дальнейшая их валидация
+ if ($user->values($_POST)->check())
+ {
+ $user->save();
+ }
+
+Для доступа к объекту валидации данной модели, можно использовать метод `validate()`:
+
+ // Ручное добавление дополнительного фильтра
+ $user->validate()->filter('username', 'trim');
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/tutorials.removeindex.md b/includes/kohana/modules/userguide/guide/ru-ru/tutorials.removeindex.md
new file mode 100644
index 00000000..96789c90
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/tutorials.removeindex.md
@@ -0,0 +1,88 @@
+# Удаление из URL `index.php`
+
+Для чистоты URL, Вам наверняка захочется иметь доступ до разделов Вашего приложения без `/index.php/` в адресной строке. Для этого необходимо выполнить 2 действия.
+
+1. Откорректировать bootstrap файл
+2. Установить возможности rewriting'а на Вашем веб-сервере
+
+# Конфигурирование Bootstrap
+
+Первое, что следует сделать - это изменить значение `index_file` в [Kohana::init]:
+
+ Kohana::init(array(
+ 'base_url' => '/myapp/',
+ 'index_file' => FALSE,
+ ));
+
+Теперь все ссылки, генерируемые методами [URL::site], [URL::base], и [HTML::anchor] не будут использовать "index.php" при построении URL. Все генерируемые ссылки будут начинаться с `/myapp/` вместо `/myapp/index.php/`.
+
+# URL Rewriting
+
+В зависимости от Вашего сервера, rewriting активируется по разному.
+
+## Apache
+
+Переименуйте `example.htaccess` в `.htaccess` и измените следующую строчку кода:
+
+ RewriteBase /kohana/
+
+RewriteBase должен совпадать со значением, указанным у Вас в `base_url` свойстве [Kohana::init]:
+
+ RewriteBase /myapp/
+
+В большинстве случаев - это всё, что необходимо сделать.
+
+### Ошибка!
+
+Если вдруг Вы стали получать ошибки в виде "Internal Server Error" или "No input file specified", попытайтесь изменить `.htaccess` следующее:
+
+ RewriteRule ^(?:application|modules|system)\b - [F,L]
+
+Вместо параметра `\b` попробуйте использовать слеш:
+
+ RewriteRule ^(application|modules|system)/ - [F,L]
+
+Если это не поможет, попробуйте изменить следующее:
+
+ RewriteRule .* index.php/$0 [PT]
+
+На что-то более простое:
+
+ RewriteRule .* index.php [PT]
+
+### Всё равно ошибка!
+
+Если всё ещё получаете ошибки, убедитесь, что Ваш хостинг предоставляет поддержку Apache `mod_rewrite`. Если у Вас есть доступ до изменения настроек Apache, то добавьте следующие строки в конфигурационный файл (зачастую это `httpd.conf`):
+
+
+ Order allow,deny
+ Allow from all
+ AllowOverride All
+
+
+## NGINX
+
+Тяжело дать пример конфигурации nginx сервера, но можно использовать следующий пример для 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;
+ }
+
+Заметьте, что в данном примере используются [try_files](http://wiki.nginx.org/NginxHttpCoreModule#try_files) и [fastcgi_split_path_info](http://wiki.nginx.org/NginxHttpFcgiModule#fastcgi_split_path_info) свойства.
+
+[!!] Этот пример подразумевает, что Вы запускаете PHP как FastCGI сервер на порту 9000 и используете nginx v0.7.31 и выше.
+
+Если с этой конфигурацией Вы получаете ошибки, установите для nginx уровень логов в debug и проверьте access и error логи на предмет ошибок.
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/tutorials.urls.md b/includes/kohana/modules/userguide/guide/ru-ru/tutorials.urls.md
new file mode 100644
index 00000000..d90e3371
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/tutorials.urls.md
@@ -0,0 +1,160 @@
+# Маршруты, URL, и Ссылки
+
+В данном разделе будет раскрыты фундаментальные основы маршрутизации запросов Kohana, формирования URL и ссылок.
+
+## маршрутизация
+
+Как уже говорилось в секции [порядок выполнения](about.flow), запрос обрабатывается классом [Request], который ищет подходящий маршрут ([Route]) и загружает соответствующий контроллер для выполнения. Таким образом, система обеспечивает большую гибкость, а также интуитивно понятное поведение по умолчанию.
+
+Если Вы заглянете в `APPPATH/bootstrap.php`, то увидите следующий участок кода, который будет выполнен непосредственно до передачи запроса в вызов [Request::instance]:
+
+ Route::set('default', '((/(/)))')
+ ->defaults(array(
+ 'controller' => 'welcome',
+ 'action' => 'index',
+ ));
+
+Так устанавливается машрут по умолчанию (`default`) с адресом по шаблону `((/(/)))`. Символы, заключенные в угловые скобки `<>`, являются *ключами*, а круглыми скобками - *необязательные* части адреса. В данном случае весь адрес является опциональным, так что и пустой адрес будет отработан, а дефолтные контроллер и экшен приведут к тому, что для обработки запроса загрузится класс `Controller_Welcome` и выполнится метод `action_index`.
+
+Заметьте, что в маршрутах Kohana вне `()<>` допустимы любые символы, а `/` не имеет специального значения. В маршруте по умолчанию `/` используется как разделитель, но пока регулярное выражение имеет смысл, нет никаких ограничений на форматирование Ваших маршрутов.
+
+### Директории
+
+Вы можете захотеть разместить некоторые из контроллеров в поддиректориях. Типовое решение для админской части сайта:
+
+ Route::set('admin', 'admin(/(/(/)))')
+ ->defaults(array(
+ 'directory' => 'admin',
+ 'controller' => 'home',
+ 'action' => 'index',
+ ));
+
+Этот маршрут определяет, что адрес должен начинаться с `admin`, в результате значение директории (`admin`) будет взято из дефолтовых значений. Запрос к `admin/users/create` загрузит класс `Controller_Admin_Users` и вызовет метод `action_create`.
+
+### Шаблоны
+
+Система маршрутизации Kohana при поиске совпадений использует perl-совместимые регулярные выражения. По умолчанию ключи (окруженные `<>`) определяются шаблоном `[a-zA-Z0-9_]++`, но Вы можете указать свой шаблон для каждого используемого ключа. Для этого заполняется массив ключей и шаблонов, передающийся как дополнительный аргумент метода [Route::set]. Чтобы усложнить предыдущий пример, давайте представим, что у Вас есть админский раздел и секция подразделений (affiliates). Вы можете разделить их по разным маршрутам, а можете объявить как-то так:
+
+ Route::set('sections', '(/(/(/)))',
+ array(
+ 'directory' => '(admin|affiliate)'
+ ))
+ ->defaults(array(
+ 'controller' => 'home',
+ 'action' => 'index',
+ ));
+
+Таким образом мы выделили два раздела сайта, 'admin' и 'affiliate', которые позволят разместить контроллеры по поддиректориям, в остальных случаях будет работать маршрут по умолчанию.
+
+### Еще примеры маршрутов
+
+Существует бесчисленное количество возможностей маршрутизации. Вот еще некоторые из них:
+
+ /*
+ * Короткие адреса для авторизации
+ */
+ Route::set('auth', '',
+ array(
+ 'action' => '(login|logout)'
+ ))
+ ->defaults(array(
+ 'controller' => 'auth'
+ ));
+
+ /*
+ * Разноформатные ленты новостей
+ * 452346/comments.rss
+ * 5373.json
+ */
+ Route::set('feeds', '(/).',
+ array(
+ 'user_id' => '\d+',
+ 'format' => '(rss|atom|json)',
+ ))
+ ->defaults(array(
+ 'controller' => 'feeds',
+ 'action' => 'status',
+ ));
+
+ /*
+ * Статичные страницы
+ */
+ Route::set('static', '.html',
+ array(
+ 'path' => '[a-zA-Z0-9_/]+',
+ ))
+ ->defaults(array(
+ 'controller' => 'static',
+ 'action' => 'index',
+ ));
+
+ /*
+ * Не нравятся слэши?
+ * EditGallery:bahamas
+ * Watch:wakeboarding
+ */
+ Route::set('gallery', '():',
+ array(
+ 'controller' => '[A-Z][a-z]++',
+ 'action' => '[A-Z][a-z]++',
+ ))
+ ->defaults(array(
+ 'controller' => 'Slideshow',
+ ));
+
+ /*
+ * Быстрый поиск
+ */
+ Route::set('search', ':', array('query' => '.*'))
+ ->defaults(array(
+ 'controller' => 'search',
+ 'action' => 'index',
+ ));
+
+Маршруты анализируются в том же порядке, в котором были добавлены, так что имейте в виду, что если Вы установили маршрут после загрузки модулей, в одном из загруженных модулей может быть определен маршрут, конфликтующий с Вашим. Поэтому дефолтный маршрут устанавливается последним, так что все пользовательские маршруты будут проверены до него.
+
+### Параметры запроса
+
+Директория, контроллер и экшен могут быть доступны через экземпляр класса [Request] одним из двух способов:
+
+ $this->request->action;
+ Request::instance()->action;
+
+Все прочие ключи маршрута могут быть получены из контроллера так:
+
+ $this->request->param('key_name');
+
+Метод [Request::param] принимает дополнительный параметр, определяющий возвращаемое по умолчанию значение, если оно не было передано в адресе. Если метод вызван без параметром, все ключи будут возвращены в ассоциативном массиве.
+
+### Соглашения
+
+Установлено соглашение размещать Ваши маршруты в файле `MODPATH//init.php` модуля, если маршрут относится к данному модулю. Или просто вставить его в файл `APPPATH/bootstrap.php` до дефолтного маршрута, если он относится к приложению в целом. Конечно, они могут быть подгружены из внешнего файла или сгенерированы динамически.
+
+## Адреса URL
+
+Помимо мощных возможностей Kohana, есть методы для генерирования URL для Ваших маршрутов. Вы всегда можете определить адрес как строку с помощью [URL::site] для создания URL, примерно так:
+
+ URL::site('admin/edit/user/'.$user_id);
+
+Однако Kohana дает возможность генерировать URL с учетом определенного маршрута. Это очень полезно, если Ваша маршрутизация может поменяться, поскольку это избавит от необходимости заново редактировать весь код, меняя прописанный там адрес. Вот пример динамической генерации, который использует приведенный ранее маршрут `feeds`:
+
+ Route::get('feeds')->uri(array(
+ 'user_id' => $user_id,
+ 'action' => 'comments',
+ 'format' => 'rss'
+ ));
+
+Предположим, Вы решите позже сделать определение маршрута более говорящим, заменив его на `feeds/(/).`. Если Вы будете писать код с генерацией адресов как в примере выше, то не придется менять ни одной строчки кода! Если в адресе есть заключенный в скобки (опциональный) ключ, для которого не передано значение при генерации, и не приведено значение по умолчанию, то данная часть будет исключена из адреса. Примером будет ключ `(/)` дефолтного маршрута; он не будет присутствовать в сгенерированном URL если id не указан.
+
+Еще Вы можете часто использовать метод [Request::uri], который по сути делает то же самое, что вышеуказанный метод, только он подставляет текущие значения маршрута, директории, контроллера и экшена. Если текщий маршрут дефолтный, и адрес был `users/list`, для формирования адреса вида `users/view/$id` можно сделать так:
+
+ $this->request->uri(array('action' => 'view', 'id' => $user_id));
+
+Из шаблона предпочтительнее вызывать:
+
+ Request::instance()->uri(array('action' => 'view', 'id' => $user_id));
+
+## Ссылки
+
+[!!] Заглушка
+
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/using.autoloading.md b/includes/kohana/modules/userguide/guide/ru-ru/using.autoloading.md
new file mode 100644
index 00000000..778658ef
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/using.autoloading.md
@@ -0,0 +1,96 @@
+# Загрузка классов
+
+Kohana использует все преимущества [автозагрузки](http://php.net/manual/language.oop5.autoload.php) в PHP.
+Это позволяет не использовать вызовы [include](http://php.net/include) или [require](http://php.net/require) перед использованием класса. К примеру, когда Вы хотите использовать метод [Cookie::set], Вы всего лишь вызываете:
+
+ Cookie::set('mycookie', 'any string value');
+
+А для получения объекта [Encrypt] просто используйте [Encrypt::instance]:
+
+ $encrypt = Encrypt::instance();
+
+Классы загружаются с помощью метода [Kohana::auto_load], который осуществляет простое преобразование из имени класса в имя файла:
+
+1. Классы располагаются в директории `classes/` [файловой системы](about.filesystem)
+2. Все знаки подчеркивания заменяются слэшами
+2. Имена файлов должны быть в нижнем регистре
+
+Когда вызывается класс, который еще не был загружен (например `Session_Cookie`), Kohana будет искать файл под именем `classes/session/cookie.php` в файловой системе, с помощью [Kohana::find_file].
+
+## Собственные загрузчики
+
+Системный загрузчик добавляется в файле `application/bootstrap.php` через вызов [spl_autoload_register](http://php.net/spl_autoload_register):
+
+ spl_autoload_register(array('Kohana', 'auto_load'));
+
+Теперь [Kohana::auto_load] будет пытаться загрузить любой несуществующий класс при его первом использовании.
+
+# Прозрачное расширение классов {#class-extension}
+
+[Каскадная файловая система](about.filesystem) поддерживает прозрачное расширение классов. Например, класс [Cookie] определен в `SYSPATH/classes/cookie.php` так:
+
+ class Cookie extends Kohana_Cookie {}
+
+Системные классы Kohana, как и многие модули, используют такое определение, так что практически все классы могут быть расширены. Это делается прозрачно для системы, создайте свой класс в `APPPATH/classes/cookie.php` для добавления собственных методов.
+
+[!!] **Никогда** не изменяйте файлы дистрибутива Kohana. Всегда вносите изменения в классы, используя расширения, так Вы избавитесь от головной боли при обновлении.
+
+К примеру, Вы хотите создать метод, который устанавливает зашифрованную куку с помощью класса [Encrypt]:
+
+ 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
+
+Теперь вызов `Cookie::encrypt('secret', $data)` будет создавать шифрованную куку, которую можно расшифровать так: `$data = Cookie::decrypt('secret')`.
+
+## Многоуровневое расширение {#multiple-extensions}
+
+Если Вы расширяете классы Kohana в модуле, следует поддерживать прозрачное расширение. Вместо того, чтобы наследовать расширение [Cookie] от [Kohana_Cookie], создайте `MODPATH/mymod/encrypted/cookie.php`:
+
+ class Encrypted_Cookie extends Kohana_Cookie {
+
+ // Используйте методы encrypt() and decrypt(), описанные выше
+
+ }
+
+Теперь создайте `MODPATH/mymod/cookie.php`:
+
+ class Cookie extends Encrypted_Cookie {}
+
+Таким образом, пользователи смогут добавлять свои расширения в класс [Cookie], не затрагивая Ваши изменения. Однако, при следующем расширении класса [Cookie] придется наследоваться от `Encrypted_Cookie`, а не от `Kohana_Cookie`.
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/using.configuration.md b/includes/kohana/modules/userguide/guide/ru-ru/using.configuration.md
new file mode 100644
index 00000000..1b8f7d96
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/using.configuration.md
@@ -0,0 +1,58 @@
+# Основы
+
+Kohana использует для конфигурации как статические свойства, так и файлы. Статические свойства обычно применяются для статических классов, таких как [Cookie], [Security], и [Upload]. Файлы используются для объектов типа [Database], [Encrypt], и [Session].
+
+Статические свойства могут быть установлены в `APPPATH/bootstrap.php` или с помощью [расширения классов](using.autoloading#class-extension). Преимущество статических свойств в том, что не требуется загрузки дополнительных файлов. Недостаток в том, что инициируется загрузка класса, когда свойство устанавливается (если Вы не используете для этого расширения). С другой стороны, использование расширений переопределит возможные расширения из других модулей. Так что рекомендуется выносить установку статических свойств в bootstrap.
+
+[!!] При использовании opcode-кэширования, например [APC](http://php.net/apc) или [eAccelerator](http://eaccelerator.net/), время загрузки класса значительно уменьшается. Настойчиво рекомендуем использовать opcode-кэширование на любом работающем сайте, независимо от размера.
+
+## Параметры инициализации
+
+Для каждой новой установки Kohana потребуется изменить настройки [Kohana::init] в `APPPATH/bootstrap.php`. Все опущенные настройки будут использовать дефолтные значения. Настройки могут быть получены и изменены позже, с помощью статических свойств класса [Kohana]. К примеру, для получения текущей кодировки, обратитесь к свойству [Kohana::$charset].
+
+## Настройки безопасности
+
+Существует несколько параметров, которые необходимо поменять, чтобы сделать Kohana более защищенной. Самый важный из них - [Cookie::$salt], который используется для "подписывания" куков, что защищает их от подмены вне Kohana.
+
+Если Вы планируете использовать класс [Encrypt], Вам также понадобится создать файл конфигурации `encrypt` и установить значение `ключа` шифрования. Шифровальный ключ должен включать буквы, цифры и знаки для повышения безопасности.
+
+
+[!!] **Не используйте хэш для ключа шифрования!** Поступая таким образом, Вы облегчаете его взлом.
+
+# Файлы конфигурации {#config-files}
+
+Файлы конфигурации несколько отличаются от прочих файлов [каскадной файловой системы](about.filesystem) тем, что они **сливаются** вместо того, чтобы переопределяться. Это означает, что все конфигурационные файлы с одинаковым путем будут объединены для получения итоговой конфигурации. Как результат, Вы можете перезаписать *отдельные* параметры вместо того, чтобы дублровать все содержимое файла.
+
+Файлы конфигурации являются обычными PHP-файлами, хранимыми в директории `config/`, они должны возвращать ассоциативный массив:
+
+ 'value',
+ 'options' => array(
+ 'foo' => 'bar',
+ ),
+ );
+
+В данном примере файл называется `myconf.php`, и Вы можете получить доступ к нему так:
+
+ $config = Kohana::config('myconf');
+ $options = $config['options'];
+
+[Kohana::config] также предоставляет быстрый доступ к отдельным ключам массива конфигурации, используя "точечные пути".
+
+Получим доступ к массиву "options":
+
+ $options = Kohana::config('myconf.options');
+
+А так извлечем ключ "foo" из массива "options":
+
+ $foo = Kohana::config('myconf.options.foo');
+
+Также массивы могут выступать в качестве объектов, если Вам так больше нравится:
+
+ $options = Kohana::config('myconf')->options;
+
+Пожалуйста, обратите внимание, что Вы можете получить доступ только к корневым ключам через свойства объекта, все дочерние ключи доступны через стандартный синтаксис работы с массивами:
+
+ $foo = Kohana::config('myconf')->options['foo'];
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/using.messages.md b/includes/kohana/modules/userguide/guide/ru-ru/using.messages.md
new file mode 100644
index 00000000..baf08e41
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/using.messages.md
@@ -0,0 +1,26 @@
+# Основы
+
+Сообщения Kohana - это понятные человеку строки, представленные в виде короткого слова или фразы, называемой "ключ". Сообщения извлекают с помощью метода [Kohana::message], который возвращает либо группу сообщений целиком, либо отдельное сообщение.
+
+Например, когда пользователь не вошел в систему и пытается зайти на страницу, требующую аутентифицикации, должна быть отображена ошибка типа "Вы должны залогиниться для доступа к данной странице". Это сообщение может храниться в файле `auth` под ключом `must_login`:
+
+ $message = Kohana::message('auth', 'must_login');
+
+Сообщения не переводятся автоматически. Для осуществления перевода используйте [функции перевода](using.translation):
+
+ $translated = __(Kohana::message('auth', 'must_login'));
+
+[!!] В Kohana v2 система сообщений использовалась для перевода. Однако, рекомендуется использовать новую систему перевода вместо системы сообщений, так как она выведет понятный текст даже если перевод не найден.
+
+## Файлы сообщений
+
+Все файлы сообщений являются PHP-файлами, хранимыми в директории `messages/`, и возвращают ассоциативные массивы:
+
+ 'Вы должны залогиниться для доступа к этой странице',
+ 'no_access' => 'У Вас недостаточно прав для доступа к странице',
+ );
+
+Файлы сообщений похожи на [файлы настроек](using.configuration#config-files) - они тоже собираются вместе. Это означает, что все сообщения, хранимые в файлах с именем `auth`, будут объединены в один массив, так что нет необходимости дублировать все сообщения, если Вы создаете новый файл `auth`.
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/using.sessions.md b/includes/kohana/modules/userguide/guide/ru-ru/using.sessions.md
new file mode 100644
index 00000000..bddfd5fe
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/using.sessions.md
@@ -0,0 +1,223 @@
+# Сессии и Куки
+
+Kohana предоставляет пару классов, которые облегчают работу с куками и сессиями. На верхнем уровне и сессии, и куки обеспечивают одни и те же функции. Они позволяют разработчику хранить временную или постоянную инфомацию о конкретном клиенте для дальнейшего использования.
+
+Куки следует использовать для хранения публичных (некритичных) данных, неизменных в течении длительного времени. Например, хранить идентификатор пользователя или предпочитаемый язык. Используйте класс [Cookie] для установки и получения кук.
+
+[!!] Kohana работает с "подписанными" куками. Каждая хранимая кука содержит хэша для предотвращения подмены значения куки. Этот хэш генерируется методом [Cookie::salt], который учитывает свойство [Cookie::$salt]. Вам следует [изменить настройки](using.configuration), когда будете опубликовывать свое приложение.
+
+Сессии лучше использовать для хранения временных или секретных данных. Крайне критичную информацию стоит хранить в классе [Session] с драйвером "database" или "native". Когда используется драйвер "cookie", сессия должна быть зашифрована.
+
+[!!] Больше информации о работе с переменными сессии Вы можете получить в статье [семь смертных грехов сессий](http://lists.nyphp.org/pipermail/talk/2006-December/020358.html).
+
+# Хранение, извлечение и удаление данных
+
+[Cookie] и [Session] предоставляют очень схожий API для хранения данных. Главное отличие между ними в том, что доступ к сессии осуществляется как к объекту, а к кукам - как статическому классу (хэлпер).
+
+Получить объект сессии можно посредством метода [Session::instance]:
+
+ // Get the session instance
+ $session = Session::instance();
+
+Вы можете также получить все данные сессии с помощью метода [Session::as_array]:
+
+ // Get all of the session data as an array
+ $data = $session->as_array();
+
+Также есть возможность переписать глобальную переменную `$_SESSION`. чтобы работать с сессиями в более привычном, стандартном для PHP стиле:
+
+ // Overload $_SESSION with the session data
+ $_SESSION =& $session->as_array();
+
+ // Set session data
+ $_SESSION[$key] = $value;
+
+## Хранение данных {#setting}
+
+Для сохранения данных сессии или куки применяется метод `set`:
+
+ // Set session data
+ $session->set($key, $value);
+
+ // Set cookie data
+ Cookie::set($key, $value);
+
+ // Store a user id
+ $session->set('user_id', 10);
+ Cookie::set('user_id', 10);
+
+## Получение данных {#getting}
+
+Извлечение данных сессии или кук возможно посредством метода `get`:
+
+ // Get session data
+ $data = $session->get($key, $default_value);
+
+ // Get cookie data
+ $data = Cookie::get($key, $default_value);
+
+ // Get the user id
+ $user = $session->get('user_id');
+ $user = Cookie::get('user_id');
+
+## Удаление данных {#deleting}
+
+Метод `delete` позволяет удалить данные из сессии или кук:
+
+ // Delete session data
+ $session->delete($key);
+
+ // Delete cookie data
+ Cookie::delete($key);
+
+ // Delete the user id
+ $session->delete('user_id');
+ Cookie::delete('user_id');
+
+# Настройка {#configuration}
+
+И куки, и сессии имеют несколько параметров, которые влияют на механизм хранение данных. Всегда проверяйте их перед завершением приложения, так как многие из них будут напрямую влиять на безопасность Вашего приложения.
+
+## Настройка кук
+
+Все настройки изменяются через статические свойства. Вы можете изменить их либо через `bootstrap.php`, либо посредством [расширения классов](using.autoloading#class-extension).
+
+Наиболее важный параметр это [Cookie::$salt], он используется для шифрования подписи. Значение необходимо поменять и держать в тайне:
+
+ Cookie::$salt = 'your secret is safe with me';
+
+[!!] Изменение данного значения сделает недействительными все сохраненные ранее куки.
+
+По умолчанию куки хранятся до закрытия браузера. Чтобы указать свое значение для времени жизни, измените параметр [Cookie::$expiration]:
+
+ // Set cookies to expire after 1 week
+ Cookie::$expiration = 604800;
+
+ // Alternative to using raw integers, for better clarity
+ Cookie::$expiration = Date::WEEK;
+
+Адрес, с которого куки могут быть доступны, может быть ограничен параметром [Cookie::$path].
+
+ // Allow cookies only when going to /public/*
+ Cookie::$path = '/public/';
+
+Домен, на котором куки будут доступны, указан в свойстве [Cookie::$domain].
+
+ // Allow cookies only on the domain www.example.com
+ Cookie::$domain = 'www.example.com';
+
+Если Вы хотите сделать куку доступной для всех поддоменов, поставьте точку перед началом домена
+
+ // Allow cookies to be accessed on example.com and *.example.com
+ Cookie::$domain = '.example.com';
+
+Чтобы разрешить куки только по защищенному (HTTPS) соединению, установите [Cookie::$secure] параметр.
+
+ // Allow cookies to be accessed only on a secure connection
+ Cookie::$secure = TRUE;
+
+ // Allow cookies to be accessed on any connection
+ Cookie::$secure = FALSE;
+
+Защитите куки от доступа через Javascript, изменив параметр [Cookie::$httponly].
+
+ // Make cookies inaccessible to Javascript
+ Cookie::$httponly = TRUE;
+
+## Драйверы сессии {#adapters}
+
+При создании или доступе к объекту класс [Session] Вы можете выбрать, какой драйвер использовать. Доступны следующие драйверы:
+
+Native
+: Хранит данные в стандартном месте на диске web-сервера. Путь указывается в параметре [session.save_path](http://php.net/manual/session.configuration.php#ini.session.save-path) файла `php.ini` или переопределяется методом [ini_set](http://php.net/ini_set).
+
+Database
+: Хранит информацию в базе данных с помощью класса [Session_Database]. Для работы требуется подключенный модуль [Database].
+
+Cookie
+: Хранит данные в куках, с помощью класса [Cookie]. **Для данного драйвера предельный размер сессии будет равен 4Кб **
+
+Драйвер по умолчанию может быть установлен в [Session::$default]. Изначально это драйвер "native".
+
+[!!] Как и с куками, установка параметра "lifetime" в "0" означает, что сессия будет уничтожена после закрытия браузера.
+
+### Настройка драйвера сессии
+
+Вы можете применить настройки для каждого драйвера, создав конфигурационный файл `APPPATH/config/session.php`. Следующий пример настроек определяет конфигурацию для каждого драйвера:
+
+ return array(
+ 'native' => array(
+ 'name' => 'session_name',
+ 'lifetime' => 43200,
+ ),
+ 'cookie' => array(
+ 'name' => 'cookie_name',
+ 'encrypted' => TRUE,
+ 'lifetime' => 43200,
+ ),
+ 'database' => array(
+ 'name' => 'cookie_name',
+ 'encrypted' => TRUE,
+ 'lifetime' => 43200,
+ 'group' => 'default',
+ 'table' => 'table_name',
+ 'columns' => array(
+ 'session_id' => 'session_id',
+ 'last_active' => 'last_active',
+ 'contents' => 'contents'
+ ),
+ 'gc' => 500,
+ ),
+ );
+
+#### Драйвер Native {#adapter-native}
+
+Тип | Параметр | Описание | По умолчанию
+----------|-----------|---------------------------------------------------|-----------
+`string` | name | имя сессии | `"session"`
+`integer` | lifetime | время жизни сессии (в секундах) | `0`
+
+#### Cookie Adapter {#adapter-cookie}
+
+Тип | Параметр | Описание | По умолчанию
+----------|-----------|---------------------------------------------------|-----------
+`string` | name | имя куки, используемой для хранения сессии | `"session"`
+`boolean` | encrypted | шифровать данные с помощью [Encrypt]? | `FALSE`
+`integer` | lifetime | время жизни сессии (в секундах) | `0`
+
+#### Database Adapter {#adapter-database}
+
+Тип | Параметр | Описание | По умолчанию
+----------|-----------|---------------------------------------------------|-----------
+`string` | group | название группы [Database::instance] | `"default"`
+`string` | table | имя таблицы, в которой хранить данные | `"sessions"`
+`array` | columns | ассоциативный массив псевдонимов полей | `array`
+`integer` | gc | дает 1:x шанс, что запустится сборка мусора | `500`
+`string` | name | имя куки, используемой для хранения сессии | `"session"`
+`boolean` | encrypted | шифровать данные с помощью [Encrypt]? | `FALSE`
+`integer` | lifetime | время жизни сессии (в секундах) | `0`
+
+##### Структура таблицы
+
+Вам придется создать таблицу для хранения сессии в базе данных. Вот структура по умолчанию:
+
+ CREATE TABLE `sessions` (
+ `session_id` VARCHAR(24) NOT NULL,
+ `last_active` INT UNSIGNED NOT NULL,
+ `contents` TEXT NOT NULL,
+ PRIMARY KEY (`session_id`),
+ INDEX (`last_active`)
+ ) ENGINE = MYISAM;
+
+##### Поля таблицы
+
+Вы можете изменить имя полей, чтобы использовать существующую таблицу. По умолчанию используется имя ключа.
+
+session_id
+: название поля "id"
+
+last_active
+: метка времени UNIX для последнего времени обновления сессии
+
+contents
+: данные сессии, хранимые в виде сериализованной и (необязательно) зашифрованной строки
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/using.translation.md b/includes/kohana/modules/userguide/guide/ru-ru/using.translation.md
new file mode 100644
index 00000000..a98ecbbf
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/using.translation.md
@@ -0,0 +1 @@
+# Интернационализация, перевод
\ No newline at end of file
diff --git a/includes/kohana/modules/userguide/guide/ru-ru/using.views.md b/includes/kohana/modules/userguide/guide/ru-ru/using.views.md
new file mode 100644
index 00000000..a519bd3d
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/ru-ru/using.views.md
@@ -0,0 +1,118 @@
+# Использование представлений
+
+Представления - файлы, содержащие отображаемую информацию Вашего приложения. Чаще всего это HTML, CSS и Javascript, но может быть чем угодно, например XML или JSON для AJAX-вызовов. Цель представлений - хранить эту информацию отдельно от логики приложения для облегчения повторного использования и более чистого кода.
+
+Несмотря на это, представления сами по себе могут содержать код, используемый для отображения сохраненных в них данных. Например, циклический перебор элементов массива данных о продукте и отображение каждого в отдельном табличном ряду. Представления есть PHP-файлы, так что Вы можете свободно использовать там любой код, как обычно.
+
+# Создание файлов представлений
+
+Файлы представлений располагаются в директории `views` [файловой системы](about.filesystem). Вы также можете создавать поддиректории в ней для упорядочивания файлов. Все приведенные ниже примеры файлов являются допустимыми:
+
+ APPPATH/views/home.php
+ APPPATH/views/pages/about.php
+ APPPATH/views/products/details.php
+ MODPATH/error/views/errors/404.php
+ MODPATH/common/views/template.php
+
+## Загрузка представлений
+
+Объект [View] обычно создается в контроллере ([Controller]) с помощью метода [View::factory]. Чаще всего представление записывается в свойство [Request::$response] или в другое представление.
+
+ public function action_about()
+ {
+ $this->request->response = View::factory('pages/about');
+ }
+
+Когда представление сохранено в [Request::$response], как в примере выше, оно будет автоматически отображено при необходимости. Для получения сгенерированного вывода представления используйте метод [View::render] или просто преобразуйте в строку. Когда представление генерируется, файл представления загружается, и формируется HTML.
+
+ public function action_index()
+ {
+ $view = View::factory('pages/about');
+
+ // Render the view
+ $about_page = $view->render();
+
+ // Or just type cast it to a string
+ $about_page = (string) $view;
+
+ $this->request->response = $about_page;
+ }
+
+## Переменные в представлениях
+
+Как только представление было загружено, к нему могут быть присоединены переменные, методами [View::set] и [View::bind].
+
+ public function action_roadtrip()
+ {
+ $view = View::factory('user/roadtrip')
+ ->set('places', array('Rome', 'Paris', 'London', 'New York', 'Tokyo'));
+ ->bind('user', $this->user);
+
+ // The view will have $places and $user variables
+ $this->request->response = $view;
+ }
+
+[!!] Единственная разница между `set()` и `bind()` в том, что `bind()` присоединяет переменную по ссылке. Если Вы вызываете `bind()` переменной до того, как она определена, переменная будет создана со значением `NULL`.
+
+### Глобальные переменные
+
+Приложение может иметь несколько представлений, которым нужен доступ к одним и тем же переменным. Например, чтобы отобразить заголовок страницы и в шапке представления, и в теле содержимого страницы. Вы можете создать переменные, которые будут доступны в любом представлении, используя [View::set_global] и [View::bind_global].
+
+ // Assign $page_title to all views
+ View::bind_global('page_title', $page_title);
+
+Пусть приложение имеет три представления, которые генерируют главную страницу: `template`, `template/sidebar`, и `pages/home`. Сперва, напишем абстрактный контроллер для создания шаблона:
+
+ abstract class Controller_Website extends Controller_Template {
+
+ public $page_title;
+
+ public function before()
+ {
+ parent::before();
+
+ // Make $page_title available to all views
+ View::bind_global('page_title', $this->page_title);
+
+ // Load $sidebar into the template as a view
+ $this->template->sidebar = View::factory('template/sidebar');
+ }
+
+ }
+
+Далее, главный контроллер будет расширять `Controller_Website`:
+
+ class Controller_Home extends Controller_Website {
+
+ public function action_index()
+ {
+ $this->page_title = 'Home';
+
+ $this->template->content = View::factory('pages/home');
+ }
+
+ }
+
+## Представления внутри представлений
+
+Если Вы хотите вложить одно представление в другое, есть два варианта. Используя [View::factory], Вы можете его заключить в текущем представлении. Это означает, что Вы должны будут передать в него все необходимые переменные методами [View::set] и [View::bind]:
+
+ // Only the $user variable will be available in "views/user/login.php"
+ bind('user', $user) ?>
+
+Другой способ - подключение представлений напрямую, что делает все текущие переменные доступными в подключаемом представлении:
+
+ // Any variable defined in this view will be included in "views/message.php"
+
+
+Естественно, Вы также можете загрузить объект [Request] в представление:
+
+ execute() ?>
+
+Это пример [HMVC](about.mvc), который делает возможным создавать и считывать вызовы других URL Вашего приложения.
+
+# Переход с версии 2.x
+
+В отличие от версии Kohana 2.x, представления не создаются в контексте текущего контроллера, так что Вы не сможете использовать `$this` в качестве контроллера, в который загружено данное представление. Контроллер должен быть передан туда явно:
+
+ $view->bind('controller', $this);
diff --git a/includes/kohana/modules/userguide/guide/security.cookies.md b/includes/kohana/modules/userguide/guide/security.cookies.md
new file mode 100644
index 00000000..920e9c18
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/security.cookies.md
@@ -0,0 +1,3 @@
+# Cookie Security
+
+[!!] stub
diff --git a/includes/kohana/modules/userguide/guide/security.database.md b/includes/kohana/modules/userguide/guide/security.database.md
new file mode 100644
index 00000000..f1d09bb5
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/security.database.md
@@ -0,0 +1,3 @@
+# Database Security
+
+[!!] stub
diff --git a/includes/kohana/modules/userguide/guide/security.validation.md b/includes/kohana/modules/userguide/guide/security.validation.md
new file mode 100644
index 00000000..6a4e55ec
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/security.validation.md
@@ -0,0 +1,245 @@
+# Validation
+
+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:
+
+ $post = Validate::factory($_POST);
+
+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', 'User_Model::unique_username');
+
+[!!] Currently (v3.0.7) it is not possible to use an object for a rule, only static methods and functions.
+
+The method `User_Model::unique_username()` would be defined similar to:
+
+ public static 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);
+
+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`:
+
+
+
+
Some errors were encountered, please check the details you entered.
+
+
+
+
+
+
+
+
+
+
+
+
+
Passwords must be at least 6 characters long.
+
+
+
+
+
'Always', 'no' => 'Only when necessary'), $post['use_ssl']) ?>
+
For security, SSL is always used when making payments.
+
+
+
+
+
+[!!] 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
+ $this->request->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!
diff --git a/includes/kohana/modules/userguide/guide/security.xss.md b/includes/kohana/modules/userguide/guide/security.xss.md
new file mode 100644
index 00000000..71edbfab
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/security.xss.md
@@ -0,0 +1,15 @@
+# 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 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 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).
+
+The second is to always escape data when inserting into HTML. The [HTML] class provides generators for many common tags, including script and stylesheet links, anchors, images, and email (mailto) links. Any untrusted content should be escaped using [HTML::chars].
+
+## References
+
+* [OWASP XSS Cheat Sheet](http://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet)
diff --git a/includes/kohana/modules/userguide/guide/tutorials.databases.md b/includes/kohana/modules/userguide/guide/tutorials.databases.md
new file mode 100644
index 00000000..c611d3b2
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/tutorials.databases.md
@@ -0,0 +1,248 @@
+# Databases {#top}
+
+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 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.
diff --git a/includes/kohana/modules/userguide/guide/tutorials.git.md b/includes/kohana/modules/userguide/guide/tutorials.git.md
new file mode 100644
index 00000000..1b44a072
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/tutorials.git.md
@@ -0,0 +1,149 @@
+# 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 .
+
+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 and copy the "Clone URL":
+
+
+
+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 commit:
+
+ cd modules/database
+ git pull origin master
+ git checkout fbfdea919028b951c23c3d99d2bc1f5bbeda0c0b
+ cd ../..
+ git add database
+ git commit -m 'Updated database module'
+
+Note that you can also check out the commit at a tagged official release point, for example:
+
+ git checkout 3.0.6
+
+Simply run `git tag` without arguments to get a list of all tags.
+
+All done!
diff --git a/includes/kohana/modules/userguide/guide/tutorials.helloworld.md b/includes/kohana/modules/userguide/guide/tutorials.helloworld.md
new file mode 100644
index 00000000..ce0c07e0
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/tutorials.helloworld.md
@@ -0,0 +1,106 @@
+# Hello, World
+
+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:
+
+ 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...
+
+
{{userguide/examples/hello_world_error}}
+
+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:
+
+
+
+ We've got a message for you!
+
+
+
+
+
We just wanted to say it! :)
+
+
+
+If we refresh the page then we can see the fruits of our labour:
+
+
+
+## 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.
diff --git a/includes/kohana/modules/userguide/guide/tutorials.orm.md b/includes/kohana/modules/userguide/guide/tutorials.orm.md
new file mode 100644
index 00000000..3fef93f4
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/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')
+ ->where('name', '=', 'Bob')
+ ->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)
+ echo 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 format => TRUE to store a timestamp.
+ protected $_created_column = array('column' => 'date_created', 'format' => 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('column' => 'date_modified', 'format' => '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](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');
diff --git a/includes/kohana/modules/userguide/guide/tutorials.removeindex.md b/includes/kohana/modules/userguide/guide/tutorials.removeindex.md
new file mode 100644
index 00000000..9c88f0a2
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/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`:
+
+
+ Order allow,deny
+ Allow from all
+ AllowOverride All
+
+
+## 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.
diff --git a/includes/kohana/modules/userguide/guide/tutorials.urls.md b/includes/kohana/modules/userguide/guide/tutorials.urls.md
new file mode 100755
index 00000000..b03befac
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/tutorials.urls.md
@@ -0,0 +1,159 @@
+# Routes, URLs, and Links
+
+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', '((/(/)))')
+ ->defaults(array(
+ 'controller' => 'welcome',
+ 'action' => 'index',
+ ));
+
+This sets the `default` route with a uri in the format of `((/(/)))`. 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(/(/(/)))')
+ ->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', '(/(/(/)))',
+ 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', '',
+ array(
+ 'action' => '(login|logout)'
+ ))
+ ->defaults(array(
+ 'controller' => 'auth'
+ ));
+
+ /*
+ * Multi-format feeds
+ * 452346/comments.rss
+ * 5373.json
+ */
+ Route::set('feeds', '(/).',
+ array(
+ 'user_id' => '\d+',
+ 'format' => '(rss|atom|json)',
+ ))
+ ->defaults(array(
+ 'controller' => 'feeds',
+ 'action' => 'status',
+ ));
+
+ /*
+ * Static pages
+ */
+ Route::set('static', '.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', '():',
+ array(
+ 'controller' => '[A-Z][a-z]++',
+ 'action' => '[A-Z][a-z]++',
+ ))
+ ->defaults(array(
+ 'controller' => 'Slideshow',
+ ));
+
+ /*
+ * Quick search
+ */
+ Route::set('search', ':', 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//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/(/).`. 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 `(/)` 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
diff --git a/includes/kohana/modules/userguide/guide/using.autoloading.md b/includes/kohana/modules/userguide/guide/using.autoloading.md
new file mode 100644
index 00000000..11c9f517
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/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:
+
+ 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`.
diff --git a/includes/kohana/modules/userguide/guide/using.configuration.md b/includes/kohana/modules/userguide/guide/using.configuration.md
new file mode 100644
index 00000000..4c29a650
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/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 slightly different from other files within the [cascading filesystem](about.filesystem) in that they are **merged** rather than overloaded. This means that all configuration files with the same file path are combined to produce the final configuration. The end result is that you can overload *individual* settings rather than duplicating an entire file.
+
+Configuration files are plain PHP files, stored in the `config/` directory, which return an associative array:
+
+ 'value',
+ 'options' => array(
+ 'foo' => 'bar',
+ ),
+ );
+
+If the above configuration file was called `myconf.php`, you could access 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;
+
+Please note that you can only access the top level of keys as object properties, all child keys must be accessed using standard array syntax:
+
+ $foo = Kohana::config('myconf')->options['foo'];
diff --git a/includes/kohana/modules/userguide/guide/using.messages.md b/includes/kohana/modules/userguide/guide/using.messages.md
new file mode 100644
index 00000000..896daede
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/using.messages.md
@@ -0,0 +1,26 @@
+# Message Basics
+
+Kohana messages are human friendly strings represented by a shorter word or phrase, called a "key". Messages are accessed using the [Kohana::message] method, which returns either an entire group of messages, or a single message.
+
+As an example, when a user is not logged in and attempts to access a page that requires authentication, an error such as "You must be logged in to access this page" might be displayed. This message could be stored in the `auth` file with a `must_login` key:
+
+ $message = Kohana::message('auth', 'must_login');
+
+Messages are not translated. To translate a message, use the [translation function](using.translation):
+
+ $translated = __(Kohana::message('auth', 'must_login'));
+
+[!!] In Kohana v2, the message system was used for translation. However, it is highly recommended to use the new translation system instead of messages, as it provides readable text even when a translation is not available.
+
+## Message Files
+
+All message files are plain PHP files, stored in the `messages/` directory, that return an associative array:
+
+ 'You must login to access this page',
+ 'no_access' => 'You do not have privileges to access this page',
+ );
+
+Message files are similar to [config files](using.configuration#config-files) in that they are merged together. This means that all of the messages stored in a file called `auth` will be combined into a single array, so it is not necessary to duplicate all of the messages when you create a new `auth` file.
diff --git a/includes/kohana/modules/userguide/guide/using.sessions.md b/includes/kohana/modules/userguide/guide/using.sessions.md
new file mode 100644
index 00000000..f2cefda2
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/using.sessions.md
@@ -0,0 +1,223 @@
+# Using Sessions and Cookies
+
+Kohana provides a couple of classes that make it easy to work with both cookies and sessions. At a high level both sessions and cookies provide the same function. They allow the developer to store temporary or persistent information about a specific client for later retrieval.
+
+Cookies should be used for storing non-private data that is persistent for a long period of time. For example storing a user id or a language preference. Use the [Cookie] class for getting and setting cookies.
+
+[!!] Kohana uses "signed" cookies. Every cookie that is stored is combined with a secure hash to prevent modification of the cookie. This hash is generated using [Cookie::salt], which uses the [Cookie::$salt] property. You should [change this setting](using.configuration) when your application is live.
+
+Sessions should be used for storing temporary or private data. Very sensitive data should be stored using the [Session] class with the "database" or "native" adapters. When using the "cookie" adapter, the session should always be encrypted.
+
+[!!] For more information on best practices with session variables see [the seven deadly sins of sessions](http://lists.nyphp.org/pipermail/talk/2006-December/020358.html).
+
+# Storing, Retrieving, and Deleting Data
+
+[Cookie] and [Session] provide a very similar API for storing data. The main difference between them is that sessions are accessed using an object, and cookies are accessed using a static class.
+
+Accessing the session instance is done using the [Session::instance] method:
+
+ // Get the session instance
+ $session = Session::instance();
+
+When using sessions, you can also get all of the current session data using the [Session::as_array] method:
+
+ // Get all of the session data as an array
+ $data = $session->as_array();
+
+You can also use this to overload the `$_SESSION` global to get and set data in a way more similar to standard PHP:
+
+ // Overload $_SESSION with the session data
+ $_SESSION =& $session->as_array();
+
+ // Set session data
+ $_SESSION[$key] = $value;
+
+## Storing Data {#setting}
+
+Storing session or cookie data is done using the `set` method:
+
+ // Set session data
+ $session->set($key, $value);
+
+ // Set cookie data
+ Cookie::set($key, $value);
+
+ // Store a user id
+ $session->set('user_id', 10);
+ Cookie::set('user_id', 10);
+
+## Retrieving Data {#getting}
+
+Getting session or cookie data is done using the `get` method:
+
+ // Get session data
+ $data = $session->get($key, $default_value);
+
+ // Get cookie data
+ $data = Cookie::get($key, $default_value);
+
+ // Get the user id
+ $user = $session->get('user_id');
+ $user = Cookie::get('user_id');
+
+## Deleting Data {#deleting}
+
+Deleting session or cookie data is done using the `delete` method:
+
+ // Delete session data
+ $session->delete($key);
+
+ // Delete cookie data
+ Cookie::delete($key);
+
+ // Delete the user id
+ $session->delete('user_id');
+ Cookie::delete('user_id');
+
+# Configuration {#configuration}
+
+Both cookies and sessions have several configuration settings which affect how data is stored. Always check these settings before making your application live, as many of them will have a direct affect on the security of your application.
+
+## Cookie Settings {#cookie-settings}
+
+All of the cookie settings are changed using static properties. You can either change these settings in `bootstrap.php` or by using a [class extension](using.autoloading#class-extension).
+
+The most important setting is [Cookie::$salt], which is used for secure signing. This value should be changed and kept secret:
+
+ Cookie::$salt = 'your secret is safe with me';
+
+[!!] Changing this value will render all cookies that have been set before invalid.
+
+By default, cookies are stored until the browser is closed. To use a specific lifetime, change the [Cookie::$expiration] setting:
+
+ // Set cookies to expire after 1 week
+ Cookie::$expiration = 604800;
+
+ // Alternative to using raw integers, for better clarity
+ Cookie::$expiration = Date::WEEK;
+
+The path that the cookie can be accessed from can be restricted using the [Cookie::$path] setting.
+
+ // Allow cookies only when going to /public/*
+ Cookie::$path = '/public/';
+
+The domain that the cookie can be accessed from can also be restricted, using the [Cookie::$domain] setting.
+
+ // Allow cookies only on the domain www.example.com
+ Cookie::$domain = 'www.example.com';
+
+If you want to make the cookie accessible on all subdomains, use a dot at the beginning of the domain.
+
+ // Allow cookies to be accessed on example.com and *.example.com
+ Cookie::$domain = '.example.com';
+
+To only allow the cookie to be accessed over a secure (HTTPS) connection, use the [Cookie::$secure] setting.
+
+ // Allow cookies to be accessed only on a secure connection
+ Cookie::$secure = TRUE;
+
+ // Allow cookies to be accessed on any connection
+ Cookie::$secure = FALSE;
+
+To prevent cookies from being accessed using Javascript, you can change the [Cookie::$httponly] setting.
+
+ // Make cookies inaccessible to Javascript
+ Cookie::$httponly = TRUE;
+
+## Session Adapters {#adapters}
+
+When creating or accessing an instance of the [Session] class you can decide which session adapter you wish to use. The session adapters that are available to you are:
+
+Native
+: Stores session data in the default location for your web server. The storage location is defined by [session.save_path](http://php.net/manual/session.configuration.php#ini.session.save-path) in `php.ini` or defined by [ini_set](http://php.net/ini_set).
+
+Database
+: Stores session data in a database table using the [Session_Database] class. Requires the [Database] module to be enabled.
+
+Cookie
+: Stores session data in a cookie using the [Cookie] class. **Sessions will have a 4KB limit when using this adapter.**
+
+The default adapter can be set by changing the value of [Session::$default]. The default adapter is "native".
+
+[!!] As with cookies, a "lifetime" setting of "0" means that the session will expire when the browser is closed.
+
+### Session Adapter Settings
+
+You can apply configuration settings to each of the session adapters by creating a session config file at `APPPATH/config/session.php`. The following sample configuration file defines all the settings for each adapter:
+
+ return array(
+ 'native' => array(
+ 'name' => 'session_name',
+ 'lifetime' => 43200,
+ ),
+ 'cookie' => array(
+ 'name' => 'cookie_name',
+ 'encrypted' => TRUE,
+ 'lifetime' => 43200,
+ ),
+ 'database' => array(
+ 'name' => 'cookie_name',
+ 'encrypted' => TRUE,
+ 'lifetime' => 43200,
+ 'group' => 'default',
+ 'table' => 'table_name',
+ 'columns' => array(
+ 'session_id' => 'session_id',
+ 'last_active' => 'last_active',
+ 'contents' => 'contents'
+ ),
+ 'gc' => 500,
+ ),
+ );
+
+#### Native Adapter {#adapter-native}
+
+Type | Setting | Description | Default
+----------|-----------|---------------------------------------------------|-----------
+`string` | name | name of the session | `"session"`
+`integer` | lifetime | number of seconds the session should live for | `0`
+
+#### Cookie Adapter {#adapter-cookie}
+
+Type | Setting | Description | Default
+----------|-----------|---------------------------------------------------|-----------
+`string` | name | name of the cookie used to store the session data | `"session"`
+`boolean` | encrypted | encrypt the session data using [Encrypt]? | `FALSE`
+`integer` | lifetime | number of seconds the session should live for | `0`
+
+#### Database Adapter {#adapter-database}
+
+Type | Setting | Description | Default
+----------|-----------|---------------------------------------------------|-----------
+`string` | group | [Database::instance] group name | `"default"`
+`string` | table | table name to store sessions in | `"sessions"`
+`array` | columns | associative array of column aliases | `array`
+`integer` | gc | 1:x chance that garbage collection will be run | `500`
+`string` | name | name of the cookie used to store the session data | `"session"`
+`boolean` | encrypted | encrypt the session data using [Encrypt]? | `FALSE`
+`integer` | lifetime | number of seconds the session should live for | `0`
+
+##### Table Schema
+
+You will need to create the session storage table in the database. This is the default schema:
+
+ CREATE TABLE `sessions` (
+ `session_id` VARCHAR(24) NOT NULL,
+ `last_active` INT UNSIGNED NOT NULL,
+ `contents` TEXT NOT NULL,
+ PRIMARY KEY (`session_id`),
+ INDEX (`last_active`)
+ ) ENGINE = MYISAM;
+
+##### Table Columns
+
+You can change the column names to match an existing database schema when connecting to a legacy session table. The default value is the same as the key value.
+
+session_id
+: the name of the "id" column
+
+last_active
+: UNIX timestamp of the last time the session was updated
+
+contents
+: session data stored as a serialized string, and optionally encrypted
diff --git a/includes/kohana/modules/userguide/guide/using.views.md b/includes/kohana/modules/userguide/guide/using.views.md
new file mode 100644
index 00000000..e29f9c02
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/using.views.md
@@ -0,0 +1,120 @@
+# Using Views
+
+Views are files that contain the display information for your application. This is most commonly HTML, CSS and Javascript but can be anything you require such as XML or JSON for AJAX output. The purpose of views is to keep this information separate from your application logic for easy reusability and cleaner code.
+
+While this is true, views themselves can contain code used for displaying the data you pass into them. For example, looping through an array of product information and display each one on a new table row. Views are still PHP files so you can use any code you normally would.
+
+# Creating View Files
+
+View files are stored in the `views` directory of the [filesystem](about.filesystem). You can also create sub-directories within the `views` directory to organize your files. All of the following examples are reasonable view files:
+
+ APPPATH/views/home.php
+ APPPATH/views/pages/about.php
+ APPPATH/views/products/details.php
+ MODPATH/error/views/errors/404.php
+ MODPATH/common/views/template.php
+
+## Loading Views
+
+[View] objects will typically be created inside a [Controller] using the [View::factory] method. Typically the view is then assigned as the [Request::$response] property or to another view.
+
+ public function action_about()
+ {
+ $this->request->response = View::factory('pages/about');
+ }
+
+When a view is assigned as the [Request::$response], as in the example above, it will automatically be rendered when necessary. To get the rendered result of a view you can call the [View::render] method or just type cast it to a string. When a view is rendered, the view file is loaded and HTML is generated.
+
+ public function action_index()
+ {
+ $view = View::factory('pages/about');
+
+ // Render the view
+ $about_page = $view->render();
+
+ // Or just type cast it to a string
+ $about_page = (string) $view;
+
+ $this->request->response = $about_page;
+ }
+
+## Variables in Views
+
+Once view has been loaded, variables can be assigned to it using the [View::set] and [View::bind] methods.
+
+ public function action_roadtrip()
+ {
+ $view = View::factory('user/roadtrip')
+ ->set('places', array('Rome', 'Paris', 'London', 'New York', 'Tokyo'));
+ ->bind('user', $this->user);
+
+ // The view will have $places and $user variables
+ $this->request->response = $view;
+ }
+
+[!!] The only difference between `set()` and `bind()` is that `bind()` assigns the variable by reference. If you `bind()` a variable before it has been defined, the variable will be created as `NULL`.
+
+### Global Variables
+
+An application may several view files that need access to the same variables. For example, to display a page title in both the header of your template and in the body of the page content. You can create variables that are accessible in any view using the [View::set_global] and [View::bind_global] methods.
+
+ // Assign $page_title to all views
+ View::bind_global('page_title', $page_title);
+
+If the application has three views that are rendered for the home page: `template`, `template/sidebar`, and `pages/home`. First, an abstract controller to create the template will be created:
+
+ abstract class Controller_Website extends Controller_Template {
+
+ public $page_title;
+
+ public function before()
+ {
+ parent::before();
+
+ // Make $page_title available to all views
+ View::bind_global('page_title', $this->page_title);
+
+ // Load $sidebar into the template as a view
+ $this->template->sidebar = View::factory('template/sidebar');
+ }
+
+ }
+
+Next, the home controller will extend `Controller_Website`:
+
+ class Controller_Home extends Controller_Website {
+
+ public function action_index()
+ {
+ $this->page_title = 'Home';
+
+ $this->template->content = View::factory('pages/home');
+ }
+
+ }
+
+## Views Within Views
+
+If you want to include another view within a view, there are two choices. By calling [View::factory] you can sandbox the included view. This means that you will have to provide all of the variables to the view using [View::set] or [View::bind]:
+
+ // Only the $user variable will be available in "views/user/login.php"
+ bind('user', $user) ?>
+
+The other option is to include the view directly, which makes all of the current variables available to the included view:
+
+ // Any variable defined in this view will be included in "views/message.php"
+
+
+Of course, you can also load an entire [Request] within a view:
+
+ execute() ?>
+
+This is an example of [HMVC](about.mvc), which makes it possible to create and read calls to other URLs within your application.
+
+# Upgrading From v2.x
+
+Unlike version 2.x of Kohana, the view is not loaded within the context of
+the [Controller], so you will not be able to access `$this` as the controller
+that loaded the view. Passing the controller to the view must be done explictly:
+
+ $view->bind('controller', $this);
diff --git a/includes/kohana/modules/userguide/guide/zh-cn/about.conventions.md b/includes/kohana/modules/userguide/guide/zh-cn/about.conventions.md
new file mode 100644
index 00000000..e0d360c1
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/zh-cn/about.conventions.md
@@ -0,0 +1,303 @@
+# 约定
+
+鼓励大家遵循 Kohana 的编码样式,Kohana 基于 [BSD/Allman style](http://en.wikipedia.org/wiki/Indent_style#BSD.2FAllman_style) 的编码样式(这里还有一些更多[关于 Kohana 编码样式的描述](http://dev.kohanaframework.org/wiki/kohana2/CodingStyle))
+
+## 类名和文件位置 {#classes}
+
+在 Kohana 系统中类名严格遵循命名约定才能够[自动加载](using.autoloading)。类名的首字母必须大写,且使用下划线连接单词,千万要注意下划线的重要性,因为它直接关系到文件在文件系统中所存放的位置。
+
+请遵循以下约定:
+
+1. 类名不允许使用骆驼命名法,除非需要创建新一级的目录文件。
+2. 所有的类文件的文件名和目录名都必须是小写。
+3. 所有的类文件都应该存放在 `classes` 目录下面,它可以是在[级联文件系统](about.filesystem)的任何一级。
+
+[!!] 不像 Kohana v2.x,这里不再区分 "controllers","models","libraries" 和 "helpers" 文件夹。所有的类都存放在 "classes/" 目录,既可以是完全静态的 辅助函数("helpers")或对象形式的类库("libraries")。你可以使用任意形式的设计模式的类库:静态,单例,适配器等。
+
+## 实例
+
+请大家记着一点在类文件中,类名到下划线意味着是一个新的目录,参考下面例子:
+
+类名 | 文件路径
+----------------------|-------------------------------
+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
+
+## 编码标准 {#coding_standards}
+
+In order to produce highly consistent source code, we ask that everyone follow the coding standards as closely as possible.
+
+### Brackets
+Please use [BSD/Allman Style](http://en.wikipedia.org/wiki/Indent_style#BSD.2FAllman_style) bracketing.
+
+### 命名约定
+
+Kohana 使用下划线连接命名,而不是驼峰命名。
+
+#### 类
+
+ // 库,使用 _Core 作后缀
+ class Beer_Core {
+
+ // 库的继承不需要使用后缀
+ class Beer extends Beer_Core
+
+ // 控制器类,使用 _Controller 作后缀
+ class Apple_Controller extends Controller {
+
+ // 模型类,使用 _Model 作后缀
+ class Cheese_Model extends Model {
+
+ // 辅助类
+ class peanut {
+
+当你实例化一个不需要附带参数的类时不需要使用圆括号:
+
+ // 正确:
+ $db = new Database;
+
+ // 错误:
+ $db = new Database();
+
+#### 函数和方法
+
+函数尽量全小写,并使用下划线分割单词:
+
+ function drink_beverage($beverage)
+ {
+
+#### 变量
+
+所有变量尽量全小写,并使用下划线分割单词而不是驼峰:
+
+ // 正确:
+ $foo = 'bar';
+ $long_example = 'uses underscores';
+
+ // 错误:
+ $weDontWantThis = 'understood?';
+
+### 缩进
+
+代码在逻辑上缩进使用制表符(TAB)代替空格。
+
+垂直间距(即多行)使用空格。制表符并不适用于垂直间距主要是因为不同的人可能设置类不同的制表符宽度。
+
+ $text = 'this is a long text block that is wrapped. Normally, we aim for '
+ . 'wrapping at 80 chars. Vertical alignment is very important for '
+ . 'code readability. Remember that all indentation is done with tabs,'
+ . 'but vertical alignment should be completed with spaces, after '
+ . 'indenting with tabs.';
+
+### 字符串连接
+
+不要在连接符左右使用空格:
+
+ // 正确:
+ $str = 'one'.$var.'two';
+
+ // 错误:
+ $str = 'one'. $var .'two';
+ $str = 'one' . $var . 'two';
+
+### 单行表达式
+
+单行 IF 表达式仅用于破坏正常执行的情况(比如,return 或 continue):
+
+ // 可接受:
+ if ($foo == $bar)
+ return $foo;
+
+ if ($foo == $bar)
+ continue;
+
+ if ($foo == $bar)
+ break;
+
+ if ($foo == $bar)
+ throw new Exception('You screwed up!');
+
+ // 不可接受:
+ if ($baz == $bun)
+ $baz = $bar + 2;
+
+### 比较操作
+
+使用 OR 和 AND 作为比较符:
+
+ // 正确:
+ if (($foo AND $bar) OR ($b AND $c))
+
+ // 错误:
+ if (($foo && $bar) || ($b && $c))
+
+if/else Blocks
+
+使用 elseif 而不是 else if:
+
+ // 正确:
+ elseif ($bar)
+
+ // 错误:
+ else if($bar)
+
+### Switch 结构
+
+每个 case,break 和 default 都应该是独立的一行。每个 case 或 default 里面必须使用一个制表符(TAB)。
+
+ switch ($var)
+ {
+ case 'bar':
+ case 'foo':
+ echo 'hello';
+ break;
+ case 1:
+ echo 'one';
+ break;
+ default:
+ echo 'bye';
+ break;
+ }
+
+### 括号
+
+There should be one space after statement name, followed by a parenthesis. The ! (bang) character must have a space on either side to ensure maximum readability. Except in the case of a bang or type casting, there should be no whitespace after an opening parenthesis or before a closing parenthesis.
+
+ // 正确:
+ if ($foo == $bar)
+ if ( ! $foo)
+
+ // 错误:
+ if($foo == $bar)
+ if(!$foo)
+ if ((int) $foo)
+ if ( $foo == $bar )
+ if (! $foo)
+
+### 三元操作
+
+所有的三元操作都应该遵循一种标准格式。表达式左右使用括号,而变量则不需要。
+
+ $foo = ($bar == $foo) ? $foo : $bar;
+ $foo = $bar ? $foo : $bar;
+
+所有的比较和操作都必须使用括号括起来作为一个组:
+
+ $foo = ($bar > 5) ? ($bar + $foo) : strlen($bar);
+
+分离复杂的三元操作(三元的第一部分超过了 80 个字符)为多行形式。spaces should be used to line up operators, which should be at the front of the successive lines:
+
+ $foo = ($bar == $foo)
+ ? $foo
+ : $bar;
+
+### 强制类型转换
+
+强制类型转换需要在两边使用空格:
+
+ // 正确:
+ $foo = (string) $bar;
+ if ( (string) $bar)
+
+ // 错误:
+ $foo = (string)$bar;
+
+如果可能,请使用强制类型转换,而不是三元操作:
+
+ // 正确:
+ $foo = (bool) $bar;
+
+ // 错误:
+ $foo = ($bar == TRUE) ? TRUE : FALSE;
+
+如果强制类型转换整形(int)或布尔型(boolean),请使用短格式:
+
+ // 正确:
+ $foo = (int) $bar;
+ $foo = (bool) $bar;
+
+ // 错误:
+ $foo = (integer) $bar;
+ $foo = (boolean) $bar;
+
+### 常量
+
+常量尽量使用全大写:
+
+ // 正确:
+ define('MY_CONSTANT', 'my_value');
+ $a = TRUE;
+ $b = NULL;
+
+ // 错误:
+ define('MyConstant', 'my_value');
+ $a = True;
+ $b = null;
+
+请把常量放在比较符号的末端:
+
+ // 正确:
+ if ($foo !== FALSE)
+
+ // 错误:
+ if (FALSE !== $foo)
+
+这是一个略有争议的选择,所以我会解释其理由。如果我们用简单的英语写前面的例子中,正确的例子如下:
+
+ if variable $foo is not exactly FALSE
+
+但是错误的例子可以理解为:
+
+ if FALSE is not exactly variable $foo
+
+由于我们是从左向右读,因此把常量放在第一位根本没有意义。
+
+### 注解
+
+#### 单行注解
+
+单行注解使用 //,或许你在使用下面几种注解方式。请在注解符后面保留一个空格在添加注解。坚决不能使用 #。
+
+ // 正确
+
+ //错误
+ // 错误
+ # 错误
+
+### 正则表达式
+
+如果编码中使用到正则表达式,请尽量使用 PCRE 风格而不是 POSIX 风格。相比较而言 PCRE 风格更为强大,速度更快。
+
+ // 正确:
+ if (preg_match('/abc/i'), $str)
+
+ // 错误:
+ if (eregi('abc', $str))
+
+正则表达式使用单引号括起来而不是双引号。单引号的字符串简单而且解析起来更快。
+Unlike double-quoted strings they don't support variable interpolation
+nor integrated backslash sequences like \n or \t, etc.
+
+ // 正确:
+ preg_match('/abc/', $str);
+
+ // 错误:
+ preg_match("/abc/", $str);
+
+当需要使用正则搜索活替换时,请使用 $n 符号作反向引用,它的效率优于 \\n。
+
+ // 正确:
+ preg_replace('/(\d+) dollar/', '$1 euro', $str);
+
+ // 错误:
+ preg_replace('/(\d+) dollar/', '\\1 euro', $str);
+
+最后,请注意如果使用 $ 符号匹配字符串末尾是否允许后换行符的话,如果需要可以附加 D 修饰符解决此问题。[更多详情](http://blog.php-security.org/archives/76-Holes-in-most-preg_match-filters.html)。
+
+ $str = "email@example.com\n";
+
+ preg_match('/^.+@.+$/', $str); // TRUE
+ preg_match('/^.+@.+$/D', $str); // FALSE
diff --git a/includes/kohana/modules/userguide/guide/zh-cn/about.filesystem.md b/includes/kohana/modules/userguide/guide/zh-cn/about.filesystem.md
new file mode 100644
index 00000000..72793ecc
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/zh-cn/about.filesystem.md
@@ -0,0 +1,75 @@
+# 级联文件系统
+
+Kohana 文件系统单一的目录结构。
+当使用 [Kohana::find_file] 加载一个文件时,系统会以下顺序搜索:
+
+Application 路径
+: 在 `index.php` 文件中常量被定义为 `APPPATH`,默认值是 `application`。
+
+Module 路径
+: 这是在 `APPPATH/bootstrap.php` 文件中使用 [Kohana::modules] 设置的一组数组。
+ 数组的每个值都会按照顺序搜索并添加进来。
+
+System 路径
+: 在 `index.php` 文件中常量被定义为 `SYSPATH`。默认值是 `system`。
+所有 “core” 核心文件和类文件都在这里定义。
+
+目录中的文件包含了优先顺序建立的从高到低的优先级,这就有可能使得具有"高等级"目录的同名文件的会重载任何可以低于它的文件内容。
+
+
+
+如果在 `APPPATH/views` 目录和 `APPPATH/views` 目录均有一个名为 `welcome.php` 视图文件,
+当 `welcome.php` 被加载的时候由于 application 目录在文件系统的最上面所以只有它会被返回。
+
+## 文件类型
+
+目录的级别从高到低依次是 application,module 和 system 路径,分别都有下面的目录结构:
+
+classes/
+: 所有你想要 [autoload](using.autoloading) 的类库均保存在这里。
+ 本目录包含了控制器,模型和其他类库。所有的库文件都必须遵循[类的命名规则](about.conventions#classes)。
+
+config/
+: 配置文件是使用 [Kohana::config] 返回的数组项。
+ 详情请查阅[配置的用法](using.configuration)。
+
+i18n/
+: 多语言文件返回的包各国语言的字符串数组。多语言是使用 `__()` 方法实现。
+ 如果想把 "Hello, world" 多语言化,只需要调用 `__('Hello, world!')` 并设置
+ [I18n::$lang] 为 "zh-cn"。
+ 详情请查阅[多语言的用法](using.translation)。
+
+messages/
+: 消息文件是使用 [Kohana::message] 返回的字符串数组。消息和 i18n 文件唯一不同的就是无法多语言化,
+ 但是总是携程默认语言并通过单键引用。
+ 详情请查阅[消息的用法](using.messages)。
+
+views/
+: 视图是标准的 PHP 文件被用于生成 HTML。视图文件被加载到 [View] 对象中并得到变量的设置,
+ 最后在转换为 HTML 片段或其他输出。多个视图可以相互引用。
+ 详情请查阅[视图的用法](using.views)。
+
+## 查找文件
+
+使用 [Kohana::find_file] 方法可以找到在文件系统中任意路径下的文件:
+
+ // 查询的路径 "classes/cookie.php"
+ $path = Kohana::find_file('classes', 'cookie');
+
+ // 查询的路径 "views/user/login.php"
+ $path = Kohana::find_file('views', 'user/login');
+
+
+## 第三方扩展
+
+调用扩展并非限定在 Kohana 。
+比如,如果你想使用 [DOMPDF](http://code.google.com/p/dompdf),
+只需把他复制到 `application/vendor/dompdf` 并加载 DOMPDF 的自动加载类:
+
+ require Kohana::find_file('vendor', 'dompdf/dompdf/dompdf_config.inc');
+
+现在无需再加载任何文件就可以使用 DOMPDF:
+
+ $pdf = new DOMPDF;
+
+[!!] 如果你想使用 DOMPDF 转换试图到 PDFs,可以试试 [PDFView](http://github.com/shadowhand/pdfview) 扩展。
diff --git a/includes/kohana/modules/userguide/guide/zh-cn/about.flow.md b/includes/kohana/modules/userguide/guide/zh-cn/about.flow.md
new file mode 100644
index 00000000..2c5ddc69
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/zh-cn/about.flow.md
@@ -0,0 +1,73 @@
+# 请求流程
+
+每个应用都遵循同样的流程:
+
+1. 程序从 `index.php` 文件加载
+2. 设置 application,module 和 system 目录到路径
+3. 设置错误报告的级别
+4. 加载 install.php 文件(如果存在的话)
+5. 加载 [Kohana] 类
+6. 加载 `APPPATH/bootstrap.php` 引导文件
+7. 调用 [Kohana::init] 方法初始化错误句柄,缓存和日志设置
+8. 设置 [Kohana_Config] 读取器和 [Kohana_Log] 记录器
+9. 调用 [Kohana::modules] 方法加载激活的模块
+ * 模块路径附加到[文件级联系统](about.filesystem).
+ * 加载模块的 `init.php` 文件(如果存在的话)
+ * `init.php` 文件可以增强系统环境设置,同时也包括路由
+10. [Route::set] 会被多次调用来定义[程序路由](using.routing)
+11. 调用 [Request::instance] 方法来处理请求
+ 1. 检测每个路由直到发现匹配的
+ 2. 加载控制器实例化并传递请求
+ 3. 调用 [Controller::before] 方法
+ 4. 调用控制器的方法生成请求的响应
+ 5. 调用 [Controller::after] 方法
+ * 当使用 [HMVC sub-requests](about.mvc) 时以上五步会多次循环调用
+12. 显示 [Request] 响应
+
+## index.php
+
+Kohana 遵循[前端控制器]模式,因此所有的请求都要发送到 `index.php` 文件。这样就可以允许保持一个非常整洁的[文件系统](about.filesystem)设计。在 `index.php` 文件中有一些非常重要而又基础的配置变量。你可以改变 `$application`,`$modules` 和 `$system` 的路径以及设置错误报告级别。
+
+`$application` 变量让目录包含着你的程序文件。默认情况下,就是 `application` 目录。`$modules` 变量让目录包含着你的扩展文件。默认情况下。`$system` 变量让目录包含着默认的 Kohana 文件。默认情况下。
+
+你可以移动下面三个目录到任意路径。假如你的目录结构是:
+
+ www/
+ index.php
+ application/
+ modules/
+ system/
+
+你想转移这些目录到 web 目录以外:
+
+ application/
+ modules/
+ system/
+ www/
+ index.php
+
+那么你应该在 `index.php` 文件改变下面变量的配置:
+
+ $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.
+
+### 错误报告
+
+默认情况下,Kohana显示所有错误,包括严格的警告。
+
+ error_reporting(E_ALL | E_STRICT);
+
+对于已经上线并在运行的程序,一个保守的推荐,可以忽略掉提醒:
+
+ error_reporting(E_ALL & ~E_NOTICE);
+
+如果在错误被触发后得到的是一个空白的结果,你的服务器可能关闭了错误提示。你可以在 `error_reporting` 调用前使用下面的代码开启错误提醒:
+
+ ini_set('display_errors', TRUE);
+
+在发送错误提示时,错误应该**实时**显示,甚至是在上线发布之后,因为它允许你使用[异常和错误句柄](debugging.errors) 指引到一个友好的错误页面从而代替空白的页面。
diff --git a/includes/kohana/modules/userguide/guide/zh-cn/about.install.md b/includes/kohana/modules/userguide/guide/zh-cn/about.install.md
new file mode 100644
index 00000000..a7c10b30
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/zh-cn/about.install.md
@@ -0,0 +1,95 @@
+# 系统安装
+
+1. 从 [Kohana 官方网站](http://kohanaframework.org/)下载最新**稳定**版本的框架
+2. 创建一个名为 'kohana' 的目录并解压缩到这个目录
+3. 上传到这个目录的所有文件到你的服务器上
+4. 编辑 `application/bootstrap.php` 文件并按实际情况修改下面配置:
+ - 为你的程序设置默认[时区](http://php.net/timezones)
+ - 在 [Kohana::init] 方法中设置 `base_url` 的值为 kohana 目录的路径(或域名地址)
+6. 确保 `application/cache` 目录和 `application/logs` 目录让服务器可写权限
+7. 在你喜欢的浏览器地址栏中输入 `base_url` 来测试 Kohana 是否安装成功
+
+[!!] 根据系统平台的不同,安装的目录可能会随着解压缩而失去原先的权限属性。如果有错误发生请在 Kohana 根目录设定所有文件属性为 755。命令为:`find . -type d -exec chmod 0755 {} \;`
+
+如果你可以看到安装页面(install.php)则说明已经安装成功(一片绿色),如果它报告有任何的错误(红色显示),你应该在立刻修复。
+
+
+
+一旦安装页面报告你的环境确认无误,并且可以改名或删除在跟目录的 `install.php` 文件,然后你就能看到 Kohana 的欢迎界面:
+
+
+
+
+## 设置产品(Production)环境
+
+在转移到产品环境之前有些事情需要完成:
+
+1. 查看文档的[配置页面](about.configuration)。
+ 它涵盖了大多数的环境全局设置。
+ 一般来讲,在产品环境下需要开启缓存并关闭概况分析(profiling)([Kohana::init] 设置)。
+ 如果设置了很多路由,路由缓存也是很有必要的。
+2. 在 application/bootstrap.php 捕获所有的异常,已保证敏感信息不会被堆栈跟踪泄漏。
+ 下面有一个从 Shadowhand 的 wingsc.com 网站源代码提取出来的样例。
+3. 打开 APC 或某些类型的指令缓存。
+ 这是最简单容易的提升 PHP 自身性能的方法。程序越复杂,使用指令缓存带来越大的利益。
+
+ /**
+ * Set the environment string by the domain (defaults to 'development').
+ */
+ Kohana::$environment = ($_SERVER['SERVER_NAME'] !== 'localhost') ? Kohana::PRODUCTION : Kohana::DEVELOPMENT;
+ /**
+ * Initialise Kohana based on environment
+ */
+ Kohana::init(array(
+ 'base_url' => '/',
+ 'index_file' => FALSE,
+ 'profile' => Kohana::$environment !== Kohana::PRODUCTION,
+ 'caching' => Kohana::$environment === Kohana::PRODUCTION,
+ ));
+
+ /**
+ * Execute the main request using PATH_INFO. If no URI source is specified,
+ * the URI will be automatically detected.
+ */
+ $request = Request::instance($_SERVER['PATH_INFO']);
+
+ try
+ {
+ // Attempt to execute the response
+ $request->execute();
+ }
+ catch (Exception $e)
+ {
+ if ( Kohana::$environment == 'development' )
+ {
+ // Just re-throw the exception
+ throw $e;
+ }
+
+ // Log the error
+ Kohana::$log->add(Kohana::ERROR, Kohana::exception_text($e));
+
+ // Create a 404 response
+ $request->status = 404;
+ $request->response = View::factory('template')
+ ->set('title', '404')
+ ->set('content', View::factory('errors/404'));
+ }
+
+ if ($request->send_headers()->response)
+ {
+ // Get the total memory and execution time
+ $total = array(
+ '{memory_usage}' => number_format((memory_get_peak_usage() - KOHANA_START_MEMORY) / 1024, 2).'KB',
+ '{execution_time}' => number_format(microtime(TRUE) - KOHANA_START_TIME, 5).' seconds');
+
+ // Insert the totals into the response
+ $request->response = str_replace(array_keys($total), $total, $request->response);
+ }
+
+
+ /**
+ * Display the request response.
+ */
+ echo $request->response;
+
diff --git a/includes/kohana/modules/userguide/guide/zh-cn/about.kohana.md b/includes/kohana/modules/userguide/guide/zh-cn/about.kohana.md
new file mode 100644
index 00000000..dc60b6d5
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/zh-cn/about.kohana.md
@@ -0,0 +1,15 @@
+# 什么是 Kohana?
+
+Kohana 是由志愿者团队开发的一个开源形式使用 [PHP5](http://php.net/manual/intro-whatis "PHP Hypertext Preprocessor") 开发的[面对对象](http://wikipedia.org/wiki/Object-Oriented_Programming)模式 [MVC](http://wikipedia.org/wiki/Model–View–Controller "Model View Controller") 架构的 [Web 框架](http://wikipedia.org/wiki/Web_Framework)。它目标旨在快速开发,高安全性,轻量级代码。
+
+[!!] Kohana 是基于 [BSD license](http://kohanaframework.org/license) 发布,所以大家可以用它开发任何的开源形式的,公司形式的或者个人形式的应用。
+
+## Kohana 的特点是什么?
+
+使用独有的 [文件系统](about.filesystem) 设计可以任意继承库类而经可能少的(或不需要)[配置](about.configuration),[错误句柄](debugging.errors)能够在开发过程中迅速定位错误源,并有内置的[调试器](debugging.overview)和[分析器](debugging.profiling)作为辅助开发工具。
+
+并且提供为您的应用程序提供安全系数相关的工具:[XSS removal](security.xss),[input validation](security.validation),[signed cookies](security.cookies),[form](security.forms) 和 [HTML](security.html)。于此同时,[数据库](security.database)层提供对 [SQL 注入](http://wikipedia.org/wiki/SQL_Injection)的保护。这是因为所有的官方代码经过精心编写和安全审查的。
+
+## 这文档太逊了!
+
+我们正在全力提供完整的文档。如果你想从问题中找到答案,请查阅[非官方 Wiki](http://kerkness.ca/wiki/doku.php)。如果你想要添加或修改文档的内容,请 [fork](http://github.com/kohana/userguide) 本文档保存后发送 pull 请求。如果你不善于使用 git,你同样可以提交[feature request](http://dev.kohanaframework.org/projects/kohana3/issues)(需要注册)。
diff --git a/includes/kohana/modules/userguide/guide/zh-cn/about.mvc.md b/includes/kohana/modules/userguide/guide/zh-cn/about.mvc.md
new file mode 100644
index 00000000..035b6b19
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/zh-cn/about.mvc.md
@@ -0,0 +1,7 @@
+# (层次结构)模型 视图 控制器
+
+模型 视图 控制器,英文全称为 Model View Controller(缩写 MVC)是一个流行的设计模式,它从呈现/模板(视图)和请求流程(控制器)中分离了数据源(模型)。
+
+使用此模式设计开发系统级别的应用程序会更加容易和最大限度的重用代码,这就意味着你不必在写那么多不必要的代码了!
+
+[!!] Stub
diff --git a/includes/kohana/modules/userguide/guide/zh-cn/about.upgrading.md b/includes/kohana/modules/userguide/guide/zh-cn/about.upgrading.md
new file mode 100644
index 00000000..30bc5713
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/zh-cn/about.upgrading.md
@@ -0,0 +1,288 @@
+# 从 2.3.x 升级
+
+Kohana v3 大部分功能都不同于 Kohana 2.3 版本,下面列出了一系列的升级建议:
+
+## 命名约定
+
+在 2.x 体系中不同的类的'类型'(比如 controller,model 等)使用后缀来加以区分。文件夹在模型/控制器目录下没有任何类名的关系。
+
+在 3.0 版本中废弃了上面的形式转而使用 Zend framework 的文件体系的约定,也就是类名包含类名和其路径,之间是有下划线分割而不是斜杠符(比如 `/some/class/file.php` 变为了 `Some_Class_File`)
+详情请参见 [约定文档](start.conventions)
+
+## Input 库
+
+Input 库已经从 3.0 版本中移除,请使用 `$_GET` 和 `$_POST` 获取。
+
+### XSS 保护
+
+假如你需要使用 XSS 清除用户输入数据,你可以使用 [Security::xss_clean] 处理输入数据,比如:
+
+ $_POST['description'] = security::xss_clean($_POST['description']);
+
+你也可以把 [Security::xss_clean] 当作 [Validate] 类的过滤器使用:
+
+ $validation = new Validate($_POST);
+
+ $validate->filter('description', 'Security::xss_clean');
+
+### POST & GET
+
+Input 库有一个最大的方便之处在于如果你试图从一个超全域阵列(superglobal arrays)访问它的值,假若其值不存在 Input 库则会返回一个指定的默认值,比如:
+
+ $_GET = array();
+
+ // $id 获得的值是 1
+ $id = Input::instance()->get('id', 1);
+
+ $_GET['id'] = 25;
+
+ // $id 现在获得的值是 25
+ $id = Input::instance()->get('id', 1);
+
+在 3.0 版本你可以使用 [Arr::get] 方法实现同样的效果:
+
+ $_GET = array();
+
+ // $id 获得的值是 1
+ $id = Arr::get($_GET, 'id', 1);
+
+ $_GET['id'] = 42;
+
+ // $id 现在获得的值是 42
+ $id = Arr::get($_GET, 'id', 1);
+
+## ORM 库
+
+自 2.3 版本到现在已经有一些主要的改动,下面是常见的通用升级问题:
+
+### 成员变量
+
+现在所有的成员变量都添加了 下划线(_) 作为前缀而且无法再通过 `__get()` 方法获得访问权利。相反的你可以把属性名并去掉下划线当作函数去调用。
+
+例如,在 2.3 版本中有一个 `loaded` 属性,现在改名为 `_loaded` 并且如果需要在外边类库中访问此属性只需要 `$model->loaded()`。
+
+### 关系
+
+在 2.3 版本中如果你想要迭代一个模型相关对象的话,你需要:
+
+ foreach($model->{relation_name} as $relation)
+
+然而,在新的系统中这已经失效。在 2.3 版本中任何使用 Databate 库生成的查询都是在全局作用域生成,这就意味着你不能同时尝试和构建两个查询语句。这里有个例子:
+
+# TODO: 需要一个具体的实例!!!!
+
+第二此查询则会失效而不能查询,内部查询将 '继承' 作为第一条件,从而造成混乱。在 3.0 版本中此问题得到了有效的解决,创建每条查询都是其自身的作用域之中,尽管如此,这也意味着有些东西没法按实际的预期正常工作。这里有个例子:
+
+ foreach(ORM::factory('user', 3)->where('post_date', '>', time() - (3600 * 24))->posts as $post)
+ {
+ echo $post->title;
+ }
+
+[!!] (相关新的查询语法请查看 [Database 教程](tutorials.databases))
+
+在 2.3 版本中你希望它可以返回用户为 3 且 `post_date` 在最近 24 小时内发布的所有 posts 的迭代器,然而相反的,它将适用 where 语句到 user 模型中并返回带有指定加入语句的 'Model_Post' 对象。
+
+为了达到 2.3 版本的同样效果,你只需要略微修改结构即可:
+
+ foreach(ORM::factory('user', 3)->posts->where('post_date', '>', time() - (36000 * 24))->find_all() as $post)
+ {
+ echo $post->title;
+ }
+
+这同样也应用到 `has_one` 关系中:
+
+ // 错误
+ $user = ORM::factory('post', 42)->author;
+ // 正确
+ $user = ORM::factory('post', 42)->author->find();
+
+### Has and belongs to many relationships
+
+在 2.3 版本中你可以设置 `has_and_belongs_to_many` 关系。但是在 3.0 版本此功能已经融合到了 `has_many` *through*。
+
+在你的模型中定义一个 `has_many` 关系到其他模型中,并且添加一个 `'through' => 'table'` 属性,其中 `'table'` 是连接表的名称。比如(posts<>categories):
+
+ $_has_many = array
+ (
+ 'categories' => array
+ (
+ 'model' => 'category', // 外部模型
+ 'through' => 'post_categories' // 连接表
+ ),
+ );
+
+如果你的数据库配置设置了表前缀,这也不用担心去添加表前缀。
+
+### 外键
+
+如果你想在 2.x 版本的 ORM 中覆写一个外键,你必须指定关系属于谁,并且你的新外键在成员变量 `$foreign_keys` 之中。
+
+在 3.0 版本中你只需要在关系数组中定义一个 `foreign_key` 键即可,比如:
+
+ Class Model_Post extends ORM
+ {
+ $_belongs_to = array
+ (
+ 'author' => array
+ (
+ 'model' => 'user',
+ 'foreign_key' => 'user_id',
+ ),
+ );
+ }
+
+在上面的实例中我们应该在 posts 表中存在一个 `user_id` 字段。
+
+
+
+In has_many relationships the `far_key` is the field in the through table which links it to the foreign table & the foreign key is the field in the through table which links "this" model's table to the through table.
+
+考虑以下设定,"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',
+ ),
+ );
+ }
+
+
+显然,这里的别名设定是有点疯狂,但它是如何让 foreign/far 键很好工作的绝佳范例。
+
+### ORM 迭代器
+
+`ORM_Iterator` 也是值得注意的改动,它已经融合到了 Database_Result 之中。
+
+如果你想要获得带有对象主键的 ORM 对象数组,你只需要调用 [Database_Result::as_array],比如:
+
+ $objects = ORM::factory('user')->find_all()->as_array('id');
+
+其中的 `id` 就是 user 表的主键。
+
+## Router 库
+
+在 2.x 版本中有一个 Router 库用于处理主要的请求工作。它允许你在 `config/routes.php` 配置文件中定义基本的路由,而且它还允许支持自定义的正则表达式路由,尽管如此,如果你想做极端的话它就显得相当呆板。
+
+## 路由
+
+在 3.0 版本中路由系统(现在成为请求系统)有了更多的灵活度。路由现在全部定义在 bootstrap 文件中(`application/bootstrap.php`)以及模块(Module)的 init.php 文件之中(`modules/module/init.php`)。(另外值得一提的是,现在的路由是按照他们定义的顺序评估)
+
+替换定义的路由数组,你现在为每个路由创建一个新的 [Route] 对象。不像在 2.x 体系一样没有必要映射一个 uri 到另一个。相反的你使用标记段(比如,controller,method,id)的变量来指定 uri 模式。
+
+例如,在老系统的正则:
+
+ $config['([a-z]+)/?(\d+)/?([a-z]*)'] = '$1/$3/$1';
+
+需要映射 uri 的 `controller/id/method` 为 `controller/method/id`,在 3.0 版本中这样修改:
+
+ Route::set('reversed','((/(/)))')
+ ->defaults(array('controller' => 'posts', 'action' => 'index'));
+
+[!!] 每个 uri 都必须指定一个独一无二的名称(这里定义的是 `reversed`),其背后的原因是解释在 [URL 教程](tutorials.urls) 之中。
+
+尖括号的内容会当作动态解析部分。圆括号的内容则会当作是可选或不必要的字段。如果你只是想匹配 uris 的开头是 admin,你只需要:
+
+ Rouse::set('admin', 'admin(/(/(/)))');
+
+但,如果你想用户必须指定一个控制器:
+
+ Route::set('admin', 'admin/(/(/))');
+
+同样,Kohana 不使用任何的 '默认的默认项'。如果你想让 Kohana 去设置默认 action 为 'index',你只需要使用 [Route::defaults] 设置即可!如果你需要为 uri 字段自定义正则表达式,你只需要以 `segment => regex` 传递数组,比如:
+
+ Route::set('reversed', '((/(/)))', array('id' => '[a-z_]+'))
+ ->defaults(array('controller' => 'posts', 'action' => 'index'))
+
+这会迫使 id 的值必须全部是小写字母或者是数字,下划线。
+
+### Actions
+
+还有一点我们必须要提到的,如果控制器中的方法可以通过网址访问,现在被称为 "actions",且其前缀为 'action_'。比如,在上面的例中,如果用户访问 `admin/posts/1/edit`,那么 "actions" 就是 'edit' 而且方法在控制器将会是 `action_edit`。详情请参见 [URL 教程](tutorials.urls)
+
+## Sessions
+
+以下方法不再存在:Session::set_flash(),Session::keep_flash() 和 Session::expire_flash() 方法,替代这些废弃方法的函数你可以使用 [Session::get_once]。
+
+## URL 辅助函数
+
+URL 辅助函数仅做了略微的改动 - `url::redirect()` 方法转移到了 `$this->request->redirect()` 之中(包含控制器)/ `Request::instance()->redirect()`
+
+`url::current` 现在替换为了 `$this->request->uri()`
+
+## Valid / Validation
+
+这恋歌类现在已经合并为一个类并命名为 `Validate`.
+
+对于校验数组的语法也有些改动:
+
+ $validate = new Validate($_POST);
+
+ // 应用一个过滤器到所有数组项中
+ $validate->filter(TRUE, 'trim');
+
+ // 定义规则使用 rule() 方法
+ $validate
+ ->rule('field', 'not_empty')
+ ->rule('field', 'matches', array('another_field'));
+
+ // 为单字段设置多个规则也使用 rules() 方法,以 rules => params 的数组方式作为第二参数
+ $validate->rules('field', array(
+ 'not_empty' => NULL,
+ 'matches' => array('another_field')
+ ));
+
+为保证定义明确,其中 'required' 规则现已经改名为 'not_empty'。
+
+## View 库
+
+对于 View 库也有一些值得注意的主要改动。
+
+在 2.3 版本中视图在其处理的控制器中调用呈现,并允许你使用 `$this` 作为视图应用引用到控制器中。这一点在 3.0 版本改变了。视图现在呈现在一个空白的作用域,如果你需要在视图中使用 `$this`,你可以使用 [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. 推荐的方法是像下面这样去传递必备的变量到视图中:
+
+ $view = View::factory('my/view');
+
+ $view->variable = $this->property;
+
+ // 或者如果你想使用连接方式
+
+ $view
+ ->set('variable', $this->property)
+ ->set('another_variable', 42);
+
+ // 不推荐
+ $view->bind('this', $this);
+
+因为视图在一个空的作用域呈现,而 `Controller::_kohana_load_view` 现在是多余的了。如果你想在它呈现之前修改视图(比如,添加一个站点的菜单),你可以使用 [Controller::after]
+
+ Class Controller_Hello extends Controller_Template
+ {
+ function after()
+ {
+ $this->template->menu = '...';
+
+ return parent::after();
+ }
+ }
diff --git a/includes/kohana/modules/userguide/guide/zh-cn/debugging.code.md b/includes/kohana/modules/userguide/guide/zh-cn/debugging.code.md
new file mode 100644
index 00000000..b6105225
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/zh-cn/debugging.code.md
@@ -0,0 +1,18 @@
+# 调试
+
+Kohana 默认加载一些功能强大的功能来辅助调试程序。
+
+最常使用也是最基本的 [Kohana::debug] 方法。这个简单的方法会显示任何的变量数字,类似于 [var_export](http://php.net/var_export) 或 [print_r](http://php.net/print_r),但是使用了 HTML 格式输出。
+
+ // 显示 $foo 和 $bar 变量的相关信息
+ echo Kohana::debug($foo, $bar);
+
+Kohana 也提供一个方法 [Kohana::debug_source] 来显示特定文件的源代码。
+
+ // 显示当前行的源代码
+ echo Kohana::debug_source(__FILE__, __LINE__);
+
+如果你希望显示你应用文件的信息而不泄漏安装路径,你可以使用[Kohana::debug_path]:
+
+ // 显示 "APPPATH/cache" 而不是真实路径
+ echo Kohana::debug_path(APPPATH.'cache');
diff --git a/includes/kohana/modules/userguide/guide/zh-cn/debugging.errors.md b/includes/kohana/modules/userguide/guide/zh-cn/debugging.errors.md
new file mode 100644
index 00000000..0cf6d963
--- /dev/null
+++ b/includes/kohana/modules/userguide/guide/zh-cn/debugging.errors.md
@@ -0,0 +1,22 @@
+# 错误/异常句柄
+
+Kohana 同时提供了异常句柄和错误句柄使用 PHP 的 [ErrorException](http://php.net/errorexception) 类转换错误为异常。关于错误的详情和通过句柄显示应用程序的内部状态:
+
+1. Exception 类
+2. 错误等级
+3. 错误信息
+4. 带有行高亮的错误源
+5. 执行流程的[调试跟踪](http://php.net/debug_backtrace)
+6. 导入(include)文件,加载扩展,全局变量
+
+## 实例
+
+点击任何一个链接可以切换显示额外的信息:
+
+
110 */
+111 public function __construct($file = NULL, array $data = NULL)
+112 {
+113 if ($file !== NULL)
+114 {
+115 $this->set_filename($file);
+
+116 }
+117
+118 if ( $data !== NULL)
+119 {
+120 // Add the values to the current data
+
21 * @param array array of values
+
+22 * @return View
+23 */
+24 public static function factory($file = NULL, array $data = NULL)
+25 {
+26 return new View($file, $data);
+27 }
+
+28
+29 /**
+30 * Captures the output that is generated when a view is included.
+31 * The view data will be extracted to make local variables. This method
+
object Controller_Hello(3){
+ public template => string(4) "site"
+ public auto_render => bool TRUE
+ public request => object Request(9){
+ public route => object Route(4){
+ protected _uri => string(32) "(<controller>(/<action>(/<id>)))"
+ protected _regex => array(0)
+ protected _defaults => array(2)(
+ "controller" => string(7) "welcome"
+ "action" => string(5) "index"
+ )
+
+ protected _route_regex => string(87) "#^(?:(?P<controller>[^/.,;?]++)(?:/(?P<action>[^/.,;?]++)(?:/(?P<id>[^/.,;?]++))?)?)?$#"
+ }
+ public status => integer 500
+ public response => string(0) ""
+ public headers => array(1)(
+ "Content-Type" => string(24) "text/html; charset=utf-8"
+ )
+
+ public directory => string(0) ""
+ public controller => string(5) "hello"
+ public action => string(5) "index"
+ public uri => string(5) "hello"
+ protected _params => array(0)
+ }
+
+}
+
+
+
+
835
+836 // Create a new instance of the controller
+837 $controller = $class->newInstance($this);
+
+838
+839 // Execute the "before action" method
+840 $class->getMethod('before')->invoke($controller);
+841
+842 // Determine the action to use
+843 $action = empty($this->action) ? Route::$default_action : $this->action;
+
+844
+845 // Execute the main action with the parameters
+
71 /**
+72 * Execute the main request. A source of the URI can be passed, eg: $_SERVER['PATH_INFO'].
+73 * If no source is specified, the URI will be automatically detected.
+
+74 */
+75 echo Request::instance()
+76 ->execute()
+77 ->send_headers()
+78 ->response;
+
+