Commits

dchill42 committed b4c59d4 Merge

Sync with Reactor, tweak Driver, and refactor Session with actual CI_Driver classes

  • Participants
  • Parent commits fab10de, f3284f2

Comments (0)

Files changed (26)

 3b6f3beea1262d35735167ee77c1fa8ea8d78d0c v1.7.2
 0000000000000000000000000000000000000000 v1.7.2
 ca87887086193cf375ea30e7d4e5139dbf330783 v2.0.0
+1941a814526f8aa7186112e6b9bfd868c5c4afbf v2.0.1

File application/migrations/001_Create_accounts.php

-<?php defined('BASEPATH') OR exit('No direct script access allowed');
-
-class Migration_Create_accounts extends	CI_Migration {
-	
-	function up() 
-	{	
-		if ( ! $this->db->table_exists('accounts'))
-		{
-			// Setup Keys
-			$this->dbforge->add_key('id', TRUE);
-			
-			$this->dbforge->add_field(array(
-				'id' => array('type' => 'INT', 'constraint' => 5, 'unsigned' => TRUE, 'auto_increment' => TRUE),
-				'company_name' => array('type' => 'VARCHAR', 'constraint' => '200', 'null' => FALSE),
-				'first_name' => array('type' => 'VARCHAR', 'constraint' => '200', 'null' => FALSE),
-				'last_name' => array('type' => 'VARCHAR', 'constraint' => '200', 'null' => FALSE),
-				'phone' => array('type' => 'TEXT', 'null' => FALSE),
-				'email' => array('type' => 'TEXT', 'null' => FALSE),
-				'address' => array('type' => 'TEXT', 'null' => FALSE),
-				'Last_Update' => array('type' => 'DATETIME', 'null' => FALSE)
-			));
-			
-			$this->dbforge->add_field("Created_At TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP");
-			$this->dbforge->create_table('accounts', TRUE);
-		}
-	}
-
-	function down() 
-	{
-		$this->dbforge->drop_table('accounts');
-	}
-}

File system/core/CodeIgniter.php

  *  Load the framework constants
  * ------------------------------------------------------
  */
-	require(APPPATH.'config/constants'.EXT);
+	if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/constants'.EXT))
+	{
+		require(APPPATH.'config/'.ENVIRONMENT.'/constants'.EXT);
+	}
+	else
+	{
+		require(APPPATH.'config/constants'.EXT);
+	}
 
 /*
  * ------------------------------------------------------
 
 
 /* End of file CodeIgniter.php */
-/* Location: ./system/core/CodeIgniter.php */
+/* Location: ./system/core/CodeIgniter.php */

File system/core/Common.php

 			@unlink($file);
 			return TRUE;
 		}
-		elseif (($fp = @fopen($file, FOPEN_WRITE_CREATE)) === FALSE)
+		elseif ( ! is_file($file) OR ($fp = @fopen($file, FOPEN_WRITE_CREATE)) === FALSE)
 		{
 			return FALSE;
 		}

File system/core/Hooks.php

 		// Grab the "hooks" definition file.
 		// If there are no hooks, we're done.
 
-		@include(APPPATH.'config/hooks'.EXT);
+		if (is_file(APPPATH.'config/'.ENVIRONMENT.'/hooks'.EXT))
+		{
+		    include(APPPATH.'config/'.ENVIRONMENT.'/hooks'.EXT);
+		}
+		elseif (is_file(APPPATH.'config/hooks'.EXT))
+		{
+			include(APPPATH.'config/hooks'.EXT);
+		}
+
 
 		if ( ! isset($hook) OR ! is_array($hook))
 		{

File system/core/Input.php

 		{
 			if (strpos($str, "\r") !== FALSE)
 			{
-				$str = str_replace(array("\r\n", "\r"), PHP_EOL, $str);
+				$str = str_replace(array("\r\n", "\r", "\r\n\n"), PHP_EOL, $str);
 			}
 		}
 

File system/core/Lang.php

 	function line($line = '')
 	{
 		$line = ($line == '' OR ! isset($this->language[$line])) ? FALSE : $this->language[$line];
+
+		// Because killer robots like unicorns!
+		if ($line === FALSE)
+		{
+			log_message('error', 'Could not find the language line "'.$line.'"');
+		}
+
 		return $line;
 	}
 

File system/core/Loader.php

 	 */
 	function _ci_autoloader()
 	{
-		include_once(APPPATH.'config/autoload'.EXT);
+		if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/autoload'.EXT))
+		{
+			include_once(APPPATH.'config/'.ENVIRONMENT.'/autoload'.EXT);
+		}
+		else
+		{
+			include_once(APPPATH.'config/autoload'.EXT);
+		}
+		
 
 		if ( ! isset($autoload))
 		{

File system/core/Output.php

  */
 class CI_Output {
 
+	public $parse_exec_vars	= TRUE;	// whether or not to parse variables like {elapsed_time} and {memory_usage}
 	protected $final_output;
 	protected $cache_expiration	= 0;
 	protected $headers			= array();
 	protected $mime_types			= array();
 	protected $enable_profiler	= FALSE;
-	protected $parse_exec_vars	= TRUE;	// whether or not to parse variables like {elapsed_time} and {memory_usage}
-
 	protected $_zlib_oc			= FALSE;
 	protected $_profiler_sections = array();
 
 		$this->_zlib_oc = @ini_get('zlib.output_compression');
 
 		// Get mime types for later
-		include APPPATH.'config/mimes'.EXT;
+		if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/mimes'.EXT))
+		{
+		    include APPPATH.'config/'.ENVIRONMENT.'/mimes'.EXT;
+		}
+		else
+		{
+			include APPPATH.'config/mimes'.EXT;
+		}
+
+
 		$this->mime_types = $mimes;
-		
+
 		log_message('debug', "Output Class Initialized");
 	}
 
 	function set_output($output)
 	{
 		$this->final_output = $output;
-		
+
 		return $this;
 	}
 
 		$header = 'Content-Type: '.$mime_type;
 
 		$this->headers[] = array($header, TRUE);
-		
+
 		return $this;
 	}
 

File system/core/Router.php

 		}
 
 		// Load the routes.php file.
-		@include(APPPATH.'config/routes'.EXT);
+		if (is_file(APPPATH.'config/'.ENVIRONMENT.'/routes'.EXT))
+		{
+			include(APPPATH.'config/'.ENVIRONMENT.'/routes'.EXT);
+		}
+		elseif (is_file(APPPATH.'config/routes'.EXT))
+		{
+			include(APPPATH.'config/routes'.EXT);
+		}
+		
 		$this->routes = ( ! isset($route) OR ! is_array($route)) ? array() : $route;
 		unset($route);
 

File system/database/DB_active_rec.php

 
 		if ($query->num_rows() == 0)
 		{
-			return '0';
+			return 0;
 		}
 
 		$row = $query->row();
-		return $row->numrows;
+		return (int) $row->numrows;
 	}
 
 	// --------------------------------------------------------------------

File system/helpers/download_helper.php

 		$extension = end($x);
 
 		// Load the mime types
-		@include(APPPATH.'config/mimes'.EXT);
+		if (is_file(APPPATH.'config/'.ENVIRONMENT.'/mimes'.EXT))
+		{
+			include(APPPATH.'config/'.ENVIRONMENT.'/mimes'.EXT);
+		}
+		elseif (is_file(APPPATH.'config/mimes'.EXT))
+		{
+			include(APPPATH.'config/mimes'.EXT);
+		}
 
 		// Set a default mime if we can't find it
 		if ( ! isset($mimes[$extension]))

File system/helpers/file_helper.php

 
 		if ( ! is_array($mimes))
 		{
-			if ( ! require_once(APPPATH.'config/mimes.php'))
+			if (is_file(APPPATH.'config/'.ENVIRONMENT.'/mimes'.EXT))
+			{
+				include(APPPATH.'config/'.ENVIRONMENT.'/mimes'.EXT);
+			}
+			elseif (is_file(APPPATH.'config/mimes'.EXT))
+			{
+				include(APPPATH.'config/mimes'.EXT);
+			}
+
+			if ( ! is_array($mimes))
 			{
 				return FALSE;
 			}

File system/helpers/html_helper.php

 
 		if ( ! is_array($_doctypes))
 		{
-			if ( ! require_once(APPPATH.'config/doctypes.php'))
+			if (is_file(APPPATH.'config/'.ENVIRONMENT.'/doctypes'.EXT))
+			{
+				include(APPPATH.'config/'.ENVIRONMENT.'/doctypes'.EXT);
+			}
+			elseif (is_file(APPPATH.'config/doctypes'.EXT))
+			{
+				include(APPPATH.'config/doctypes'.EXT);
+			}
+
+			if ( ! is_array($_doctypes))
 			{
 				return FALSE;
 			}

File system/helpers/smiley_helper.php

 {
 	function _get_smiley_array()
 	{
-		if ( ! file_exists(APPPATH.'config/smileys'.EXT))
+		if ( ! file_exists(APPPATH.'config/smileys'.EXT) AND ! file_exists(APPPATH.'config/'.ENVIRONMENT.'/smileys'.EXT))
 		{
 			return FALSE;
 		}
 
-		include(APPPATH.'config/smileys'.EXT);
-
+		if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/smileys'.EXT))
+		{
+		    include(APPPATH.'config/'.ENVIRONMENT.'/smileys'.EXT);
+		}
+		else
+		{
+			include(APPPATH.'config/smileys'.EXT);
+		}
+		
 		if ( ! isset($smileys) OR ! is_array($smileys))
 		{
 			return FALSE;

File system/helpers/text_helper.php

 {
 	function convert_accented_characters($str)
 	{
-		if ( ! file_exists(APPPATH.'config/foreign_chars'.EXT))
+		if (is_file(APPPATH.'config/'.ENVIRONMENT.'/foreign_chars'.EXT))
 		{
-			return $str;
+			include(APPPATH.'config/'.ENVIRONMENT.'/foreign_chars'.EXT);
 		}
-
-		include APPPATH.'config/foreign_chars'.EXT;
+		elseif (is_file(APPPATH.'config/foreign_chars'.EXT))
+		{
+			include(APPPATH.'config/foreign_chars'.EXT);
+		}
 
 		if ( ! isset($foreign_characters))
 		{

File system/language/english/form_validation_lang.php

 $lang['matches']			= "The %s field does not match the %s field.";
 $lang['is_natural']			= "The %s field must contain only positive numbers.";
 $lang['is_natural_no_zero']	= "The %s field must contain a number greater than zero.";
+$lang['decimal']			= "The %s field must contain a decimal number.";
+$lang['less_than']			= "The %s field must contain a number less than %s.";
+$lang['greater_than']		= "The %s field must contain a number greater than %s.";
 
 
 /* End of file form_validation_lang.php */

File system/libraries/Driver.php

-<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php  if (!defined('BASEPATH')) exit('No direct script access allowed');
 /**
  * CodeIgniter
  *
  *
  * @package		CodeIgniter
  * @author		EllisLab Dev Team
- * @copyright	Copyright (c) 2006 - 2011, EllisLab, Inc.
+ * @copyright	Copyright (c) 2006 - 2010, EllisLab, Inc.
  * @license		http://codeigniter.com/user_guide/license.html
  * @link		http://codeigniter.com
  * @since		Version 1.0
 	// subsequents calls will go straight to the proper child.
 	function __get($child)
 	{
-		if ( ! isset($this->lib_name))
+        return load_driver($child);
+    }
+
+    // Separate load_driver call to support explicit driver load by library or user
+    function load_driver($child)
+    {
+		if (! isset($this->lib_name))
 		{
 			$this->lib_name = get_class($this);
 		}
 
 		if (in_array(strtolower($child_class), array_map('strtolower', $this->valid_drivers)))
 		{
+            $lower = FALSE;
+
 			// check and see if the driver is in a separate file
-			if ( ! class_exists($child_class))
+            if (class_exists($child_class))
+            {
+                // identify non-lowercase name
+                $lower = FALSE;
+            }
+            else if (class_exists(strtolower($child_class)))
+            {
+                // identify lowercase name
+                $lower = TRUE;
+            }
+            else
 			{
-				// check application path first
-				foreach (array(APPPATH, BASEPATH) as $path)
+				// check loader library paths
+                $CI =& get_instance();
+				foreach ($CI->load->_ci_library_paths as $path)
 				{
 					// and check for case sensitivity of both the parent and child libs
 					foreach (array(ucfirst($this->lib_name), strtolower($this->lib_name)) as $lib)
 						// loves me some nesting!
 						foreach (array(ucfirst($child_class), strtolower($child_class)) as $class)
 						{
-							$filepath = $path.'libraries/'.$this->lib_name.'/drivers/'.$child_class.EXT;
+							$filepath = $path.'libraries/'.$lib.'/drivers/'.$class.EXT;
 
 							if (file_exists($filepath))
 							{
 					}
 				}
 
-				// it's a valid driver, but the file simply can't be found
-				if ( ! class_exists($child_class))
-				{
+                // see if class exists now
+                if (class_exists($child_class))
+                {
+                    // identify non-lowercase name
+                    $lower = FALSE;
+                }
+                else if (class_exists(strtolower($child_class)))
+                {
+                    // identify lowercase name
+                    $lower = TRUE;
+                }
+                else
+                {
+                    // it's a valid driver, but the file simply can't be found
 					log_message('error', "Unable to load the requested driver: ".$child_class);
 					show_error("Unable to load the requested driver: ".$child_class);
 				}
 			}
 
+            // lowercase name as necessary
+            if ($lower) {
+                $child_class = strtolower($child_class);
+            }
+
 			$obj = new $child_class;
 			$obj->decorate($this);
 			$this->$child = $obj;
 				}
 			}
 
-			foreach ($r->getProperties() as $prop)
+			foreach($r->getProperties() as $prop)
 			{
 				if ($prop->isPublic())
 				{
 // END CI_Driver CLASS
 
 /* End of file Driver.php */
-/* Location: ./system/libraries/Driver.php */
+/* Location: ./system/libraries/Driver.php */

File system/libraries/Session/Session.php

 
 
 /**
- * SessionDriver Interface
- *
- * Implement this interface to make a new Session driver.
- * A Session driver basically manages an array of name/value pairs with some sort of storage mechanism.
- * To make a new driver, implement a constructor where session data is read or created, a save handler to write
- * changed data to storage, a destroy handler to remove deleted data, and an access handler to expose the data.
- * Put your driver in the libraries/Session/drivers folder anywhere in the loader paths. This includes the application
- * directory, the system directory, or any path you add with $CI->load->add_package_path().
- * Your driver must be named Session_<name>, where <name> is capitalized, and your filename must be Session_<name>.EXT,
- * preferably also capitalized. (e.g.: Session_Foo in libraries/Session/drivers/Session_Foo.php)
- * Then specify the driver by setting 'sess_driver' in your config file or as a parameter when loading the Session
- * object. (e.g.: $config['sess_driver'] = 'foo'; OR $CI->load->driver('session', array('sess_driver' => 'foo')); )
- * Included in this file are the Native driver, which manages the native PHP $_SESSION array, and
- * the Cookie driver, which manages the data in a browser cookie, with optional extra storage in a database table.
- *
- * @package     CodeIgniter
- * @subpackage  Libraries
- * @category    Sessions
- * @author      Darren Hill (DChill)
- */
-interface SessionDriver {
-    /**
-     * SessionDriver constructor
-     *
-     * Initialize session array, loading data from storage or creating a new instance
-     *
-     * @access  public
-     * @param   $params array - Array of parameters
-     */
-    public function __construct(array $params = array());
-
-    /**
-     * Save the session data
-     *
-     * Data in the array has changed - perform any storage synchronization necessary
-     *
-     * @access  public
-     * @return  void
-     */
-    public function sess_save();
-
-    /**
-     * Destroy the current session
-     *
-     * Clean up storage for this session - it has been terminated
-     *
-     * @access  public
-     * @return  void
-     */
-    public function sess_destroy();
-
-    /**
-     * Get a reference to user data array
-     *
-     * Give array access to the main Session object
-     *
-     * @access  public
-     * @return  array - Reference to userdata
-     */
-    public function &get_userdata();
-}
-// END SessionDriver Interface
-
-
-/**
  * Session Class
  *
  * The user interface defined by EllisLabs, now with puggable drivers to manage different storage mechanisms.
- * Although these classes are not derived from the CI_Driver and CI_Driver_Library classes, they do follow the
- * driver loading conventions. Instead of loading the CI_Session library, use this object by loading the Session driver.
- * (e.g.: $CI->load->driver('session'); )
  * By default, the Native PHP session driver will load, but the 'sess_driver' config/param item (see above) can be
  * used to specify the 'Cookie' driver, or any other you might create.
  * Once loaded, this driver setup is a drop-in replacement for the former CI_Session library, taking its place as the
  * 'session' member of the global controller framework (e.g.: $CI->session or $this->session).
+ * In keeping with the CI_Driver methodology, multiple drivers may be loaded, although this might be a bit confusing.
+ * The Session library class keeps track of the most recently loaded driver as "current" to call for driver methods.
+ * Ideally, one driver is loaded and all calls go directly through the main library interface. However, any methods
+ * called through the specific driver will switch the "current" driver to itself before invoking the library method
+ * (which will then call back into the driver for low-level operations). So, alternation between two drivers can be
+ * achieved by specifying which driver to use for each call (e.g.: $this->session->native->set_userdata('foo', 'bar');
+ * $this->session->cookie->userdata('foo'); $this->session->native->unset_userdata('foo');). Notice in the previous
+ * example that the _native_ userdata value 'foo' would be set to 'bar', which would NOT be returned by the call for
+ * the _cookie_ userdata 'foo', nor would the _cookie_ value be unset by the call to unset the _native_ 'foo' value.
  *
  * @package     CodeIgniter
  * @subpackage  Libraries
  * @author      Darren Hill (DChill)
  * @link        http://codeigniter.com/user_guide/libraries/sessions.html
  */
-final class Session {
-    private $driver = null;
+final class Session extends CI_Driver_Library {
+    public $params = array();
+    private $current = null;
     private $userdata = array();
 
     const FLASHDATA_KEY = 'flash';
     public function __construct(array $params = array()) {
         log_message('debug', 'Session Class Initialized');
 
-        // Get the super object and driver name
+        // Get valid drivers list
         $CI =& get_instance();
+        $this->valid_drivers = array('Session_Native', 'Session_Cookie');
+        $key = 'sess_valid_drivers';
+        $drivers = (isset($params[$key])) ? $params[$key] : $CI->config->item($key);
+        if ($drivers) {
+            if (!is_array($drivers)) $drivers = array($drivers);
+
+            // Add driver names to valid list
+            foreach ($drivers as $driver) {
+                if (!in_array(strtolower($driver), array_map('strtolower', $this->valid_drivers))) {
+                    $this->valid_drivers[] = $driver;
+                }
+            }
+        }
+
+        // Get driver to load
         $key = 'sess_driver';
         $driver = (isset($params[$key])) ? $params[$key] : $CI->config->item($key);
         if (!$driver) $driver = 'Native';
+        if (!in_array('session_'.strtolower($driver), array_map('strtolower', $this->valid_drivers))) {
+            $this->valid_drivers[] = 'Session_'.$driver;
+        }
+
+        // Save a copy of parameters in case drivers need access
+        $this->params = $params;
 
         // Load driver and get array reference
-        $this->_load_driver($driver, $CI->load->_ci_library_paths, $params);
-        $this->userdata =& $this->driver->get_userdata();
+        $this->load_driver($driver);
+        $this->userdata =& $this->current->get_userdata();
 
         // Delete 'old' flashdata (from last request)
         $this->_flashdata_sweep();
     }
 
     /**
+     * Loads session storage driver
+     *
+     * @param   $driver string - Driver classname
+     * @return  object - Loaded driver object
+     */
+	public function load_driver($driver) {
+        // Save reference to most recently loaded driver as library default
+        $this->current = parent::load_driver($driver);
+        return $this->current;
+	}
+
+    /**
+     * Select default session storage driver
+     *
+     * @param   $driver string - Driver classname
+     * @return  void
+     */
+    public function select_driver($driver) {
+        // Validate driver name
+        $lowername = strtolower($driver);
+        if (in_array($lowername, array_map('strtolower', $this->valid_drivers))) {
+            // See if regular or lowercase variant is loaded
+            if (class_exists($driver)) {
+                $this->current = $this->$driver;
+            }
+            else if (class_exists($lowername)) {
+                $this->current = $this->$lowername;
+            }
+            else {
+                $this->load_driver($driver);
+            }
+        }
+    }
+
+    /**
      * Destroy the current session
      *
      * @access  public
      */
     public function sess_destroy() {
         // Just call destroy on driver
-        $this->driver->sess_destroy();
+        $this->current->sess_destroy();
     }
 
     /**
         }
 
         // Tell driver data changed
-        $this->driver->sess_save();
+        $this->current->sess_save();
     }
 
     /**
         }
 
         // Tell driver data changed
-        $this->driver->sess_save();
+        $this->current->sess_save();
     }
 
     /**
         // Update expiration list
         $this->set_userdata(self::EXPIRATION_KEY, $expirations);
     }
-
-    /**
-     * Loads session storage driver
-     *
-     * @access  private
-     * @param   $child  string - Driver classname
-     * @param   $paths  array - Array of loader paths
-     * @param   $params array - Configuration parameters
-     * @return  void
-     */
-	private function _load_driver($child, array $paths, array $params) {
-		// The class will be prefixed with the parent lib
-        $lib_name = get_class($this);
-		$child_class = $lib_name.'_'.ucfirst($child);
-        $lower = FALSE;
-
-        // Determine if driver is already loaded
-        if (class_exists($child_class)) {
-            // Identify non-lowercase name
-            $lower = FALSE;
-        }
-        else if (class_exists(strtolower($child_class))) {
-            // Identify lowercase name
-            $lower = TRUE;
-        }
-        else {
-            // Iterate loader library paths
-            foreach ($paths as $path) {
-                // Try case variations for parent lib
-                foreach (array(ucfirst($lib_name), strtolower($lib_name)) as $lib) {
-                    // ...and child class
-                    foreach (array(ucfirst($child_class), strtolower($child_class)) as $file) {
-                        $filepath = $path.'libraries/'.$lib.'/drivers/'.$file.EXT;
-
-                        // Check for file
-                        if (file_exists($filepath)) {
-                            include_once $filepath;
-                            break;
-                        }
-                    }
-                }
-            }
-
-            // See if class exists now
-            if (class_exists($child_class)) {
-                // Identify non-lowercase name
-                $lower = FALSE;
-            }
-            else if (class_exists(strtolower($child_class))) {
-                // Identify lowercase name
-                $lower = TRUE;
-            }
-            else {
-                // Report driver file not found
-                log_message('error', 'Unable to load the requested driver: '.$child_class);
-                show_error('Unable to load the requested driver: '.$child_class);
-            }
-        }
-
-        // See if class is a valid driver
-        if ($lower) {
-            // Lowercase name to check
-            $child_class = strtolower($child_class);
-        }
-        if (!in_array('SessionDriver', class_implements($child_class))) {
-            // Report invalid driver class
-            log_message('error', 'The requested driver is not a Session Driver: '.$child_class);
-            show_error('The requested driver is not a Session Driver: '.$child_class);
-        }
-
-        // Instantiate driver object
-        $this->driver = new $child_class($params);
-	}
 }
 // END Session Class
 
 
 /**
- * Native PHP session management driver
+ * SessionDriver Class
  *
- * This is the driver that uses the native PHP $_SESSION array through the Session driver library above.
+ * Extend this class to make a new Session driver.
+ * A Session driver basically manages an array of name/value pairs with some sort of storage mechanism.
+ * To make a new driver, derive from (extend) SessionDriver. Overload the initialize method and read or create
+ * session data. Then implement a save handler to write changed data to storage (sess_save), a destroy handler
+ * to remove deleted data (sess_destroy), and an access handler to expose the data (get_userdata).
+ * Put your driver in the libraries/Session/drivers folder anywhere in the loader paths. This includes the application
+ * directory, the system directory, or any path you add with $CI->load->add_package_path().
+ * Your driver must be named Session_<name>, where <name> is capitalized, and your filename must be Session_<name>.EXT,
+ * preferably also capitalized. (e.g.: Session_Foo in libraries/Session/drivers/Session_Foo.php)
+ * Then specify the driver by setting 'sess_driver' in your config file or as a parameter when loading the Session
+ * object. (e.g.: $config['sess_driver'] = 'foo'; OR $CI->load->driver('session', array('sess_driver' => 'foo')); )
+ * Already provided are the Native driver, which manages the native PHP $_SESSION array, and
+ * the Cookie driver, which manages the data in a browser cookie, with optional extra storage in a database table.
  *
  * @package     CodeIgniter
  * @subpackage  Libraries
  * @category    Sessions
  * @author      Darren Hill (DChill)
  */
-class Session_Native implements SessionDriver {
+abstract class SessionDriver extends CI_Driver {
+	/**
+	 * Decorate
+	 *
+	 * Decorates the child with the parent driver lib's methods and properties
+	 *
+	 * @param	$parent     object - Parent library object
+	 * @return	void
+	 */
+    public function decorate($parent) {
+        // Call base class decorate first
+        parent::decorate($parent);
+
+        // Call initialize method now that driver has access to $this->parent
+        $this->initialize();
+    }
+
     /**
-     * Native Session Driver constructor
+	 * __call magic method
+	 *
+	 * Handles access to the parent driver library's methods
+	 *
+     * @access  public
+     * @param   $method string - Library method name
+     * @param   $args   array - Method arguments (default: none)
+	 * @return	mixed
+     */
+	public function __call($method, $args = array()) {
+        // Make sure the parent library uses this driver
+        $this->parent->select_driver(get_class($this));
+        return parent::__call($method, $args);
+    }
+
+    /**
+     * Initialize driver
      *
-     * Initializes session driver object
-     *
-     * @access  public
-     * @param   $params array - Array of parameters
+     * @return  void
      */
-    public function __construct(array $params = array()) {
-        // Get config parameters
-        $CI =& get_instance();
-        foreach (array('sess_cookie_name', 'sess_expire_on_close', 'sess_expiration', 'sess_match_ip',
-        'sess_match_useragent', 'cookie_prefix', 'cookie_path', 'cookie_domain') as $key) {
-            $config[$key] = isset($params[$key]) ? $params[$key] : $CI->config->item($key);
-        }
-
-        // Set session name, if specified
-        if ($config['sess_cookie_name']) {
-            $name = $config['sess_cookie_name'];
-            if ($config['cookie_prefix']) {
-                // Prepend cookie prefix
-                $name = $config['cookie_prefix'].$name;
-            }
-            session_name($name);
-        }
-
-        // Set expiration, path, and domain
-        $expire = 7200;
-        $path = '/';
-        $domain = '';
-        if ($config['sess_expiration'] !== FALSE) {
-            // Default to 2 years if expiration is "0"
-            $expire = ($config['sess_expiration'] == 0) ? (60*60*24*365*2) : $config['sess_expiration'];
-        }
-        if ($config['cookie_path']) {
-            // Use specified path
-            $path = $config['cookie_path'];
-        }
-        if ($config['cookie_domain']) {
-            // Use specified domain
-            $domain = $config['cookie_domain'];
-        }
-        session_set_cookie_params($config['sess_expire_on_close'] ? 0 : $expire, $path, $domain);
-
-        // Start session
-        session_start();
-
-        // Check session expiration, ip, and agent
-        $now = time();
-        $destroy = FALSE;
-        if (isset($_SESSION['last_activity']) && ($_SESSION['last_activity'] + $expire) < $now) {
-            // Expired - destroy
-            $destroy = TRUE;
-        }
-        else if ($config['sess_match_ip'] == TRUE && isset($_SESSION['ip_address']) &&
-        $_SESSION['ip_address'] != $CI->input->ip_address()) {
-            // IP doesn't match - destroy
-            $destroy = TRUE;
-        }
-        else if ($config['sess_match_useragent'] == TRUE && isset($_SESSION['user_agent']) &&
-        $_SESSION['user_agent'] != trim(substr($CI->input->user_agent(), 0, 50))) {
-            // Agent doesn't match - destroy
-            $destroy = TRUE;
-        }
-
-        // Destroy expired or invalid session
-        if ($destroy) {
-            // Clear old session and start new
-            $this->sess_destroy();
-            session_start();
-        }
-
-        // Set activity time
-        $_SESSION['last_activity'] = $now;
-
-        // Set matching values as required
-        if ($config['sess_match_ip'] == TRUE && !isset($_SESSION['ip_address'])) {
-            // Store user IP address
-            $_SESSION['ip_address'] = $CI->input->ip_address();
-        }
-        if ($config['sess_match_useragent'] == TRUE && !isset($_SESSION['user_agent'])) {
-            // Store user agent string
-            $_SESSION['user_agent'] = trim(substr($CI->input->user_agent(), 0, 50));
-        }
+    protected function initialize() {
+        // Overload this method to implement initialization
     }
 
     /**
      * Save the session data
      *
+     * Data in the array has changed - perform any storage synchronization necessary
+     * The child class MUST implement this abstract method!
+     *
      * @access  public
      * @return  void
      */
-    public function sess_save() {
-        // Nothing to do - changes to $_SESSION are automatically saved
-    }
+    abstract public function sess_save();
 
     /**
      * Destroy the current session
      *
+     * Clean up storage for this session - it has been terminated
+     * The child class MUST implement this abstract method!
+     *
      * @access  public
      * @return  void
      */
-    public function sess_destroy() {
-        // Cleanup session
-        $_SESSION = array();
-        $name = session_name();
-        if (isset($_COOKIE[$name])) {
-            // Clear session cookie
-            $params = session_get_cookie_params();
-            setcookie($name, '', time() - 42000, $params['path'], $params['domain']);
-            unset($_COOKIE[$name]);
-        }
-        session_destroy();
-    }
+    abstract public function sess_destroy();
 
     /**
      * Get a reference to user data array
      *
-     * @access  public
-     * @return  array - Reference to userdata
-     */
-    public function &get_userdata() {
-        // Just return reference to $_SESSION
-        return $_SESSION;
-    }
-}
-// END Session_Native Class
-
-
-/**
- * Cookie-based session management driver
- *
- * This is the CI_Session functionality, as written by EllisLab, abstracted out to a driver.
- * I have done a little updating for PHP5, and made minor changes to extract this functionality from
- * the public interface (now in Session, above), but effectively this code is unchanged.
- *
- * @package     CodeIgniter
- * @subpackage  Libraries
- * @category    Sessions
- * @author      ExpressionEngine Dev Team and Darren Hill (DChill)
- */
-class Session_Cookie implements SessionDriver {
-    private $sess_encrypt_cookie    = FALSE;
-    private $sess_use_database      = FALSE;
-    private $sess_table_name        = '';
-	private $sess_expiration        = 7200;
-    private $sess_expire_on_close   = FALSE;
-    private $sess_match_ip          = FALSE;
-    private $sess_match_useragent   = TRUE;
-	private $sess_cookie_name       = 'ci_session';
-	private $cookie_prefix          = '';
-    private $cookie_path            = '';
-    private $cookie_domain          = '';
-    private $sess_time_to_update    = 300;
-    private $encryption_key         = '';
-	private $time_reference         = 'time';
-    private $userdata               = array();
-    private $CI                     = null;
-    private $now                    = 0;
-
-    const gc_probability            = 5;
-
-    /**
-     * Cookie Session Driver Constructor
-     *
-     * @access  public
-     * @param   $params array - Array of parameters
-     */
-    public function __construct(array $params = array()) {
-        // Set the super object to a local variable for use throughout the class
-        $this->CI =& get_instance();
-
-        // Set all the session preferences, which can either be set
-        // manually via the $params array above or via the config file
-        foreach (array('sess_encrypt_cookie', 'sess_use_database', 'sess_table_name', 'sess_expiration',
-        'sess_expire_on_close', 'sess_match_ip', 'sess_match_useragent', 'sess_cookie_name', 'cookie_path',
-        'cookie_domain', 'sess_time_to_update', 'time_reference', 'cookie_prefix', 'encryption_key') as $key) {
-            $this->$key = (isset($params[$key])) ? $params[$key] : $this->CI->config->item($key);
-        }
-
-        if ($this->encryption_key == '') {
-            show_error('In order to use the Cookie Session driver you are required to set an encryption key '.
-                'in your config file.');
-        }
-
-        // Load the string helper so we can use the strip_slashes() function
-        $this->CI->load->helper('string');
-
-        // Do we need encryption? If so, load the encryption class
-        if ($this->sess_encrypt_cookie == TRUE) {
-            $this->CI->load->library('encrypt');
-        }
-
-        // Are we using a database?  If so, load it
-        if ($this->sess_use_database === TRUE && $this->sess_table_name != '') {
-            $this->CI->load->database();
-        }
-
-		// Set the "now" time.  Can either be GMT or server time, based on the config prefs.
-        // We use this to set the "last activity" time
-		$this->now = $this->_get_time();
-
-		// Set the session length. If the session expiration is
-		// set to zero we'll set the expiration two years from now.
-		if ($this->sess_expiration == 0) {
-			$this->sess_expiration = (60*60*24*365*2);
-		}
-		
-		// Set the cookie name
-		$this->sess_cookie_name = $this->cookie_prefix.$this->sess_cookie_name;
-
-        // Run the Session routine. If a session doesn't exist we'll
-        // create a new one.  If it does, we'll update it.
-        if ( ! $this->_sess_read()) {
-            $this->_sess_create();
-        }
-        else {
-            $this->_sess_update();
-        }
-
-        // Delete expired sessions if necessary
-        $this->_sess_gc();
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Write the session data
-     *
-     * @access  public
-     * @return  void
-     */
-    public function sess_save() {
-        // Are we saving custom data to the DB?  If not, all we do is update the cookie
-        if ($this->sess_use_database === FALSE) {
-            $this->_set_cookie();
-            return;
-        }
-
-        // set the custom userdata, the session data we will set in a second
-        $custom_userdata = $this->all_userdata();
-        $cookie_userdata = array();
-
-        // Before continuing, we need to determine if there is any custom data to deal with.
-        // Let's determine this by removing the default indexes to see if there's anything left in the array
-        // and set the session data while we're at it
-        foreach (array('session_id','ip_address','user_agent','last_activity') as $val) {
-            unset($custom_userdata[$val]);
-            $cookie_userdata[$val] = $this->userdata($val);
-        }
-
-        // Did we find any custom data?  If not, we turn the empty array into a string
-        // since there's no reason to serialize and store an empty array in the DB
-        if (count($custom_userdata) === 0) {
-            $custom_userdata = '';
-        }
-        else {
-            // Serialize the custom data array so we can store it
-            $custom_userdata = $this->_serialize($custom_userdata);
-        }
-
-        // Run the update query
-        $this->CI->db->where('session_id', $this->userdata('session_id'));
-        $this->CI->db->update($this->sess_table_name,
-            array('last_activity' => $this->userdata('last_activity'), 'user_data' => $custom_userdata));
-
-        // Write the cookie.  Notice that we manually pass the cookie data array to the
-        // _set_cookie() function. Normally that function will store $this->userdata, but
-        // in this case that array contains custom data, which we do not want in the cookie.
-        $this->_set_cookie($cookie_userdata);
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Destroy the current session
-     *
-     * @access  public
-     * @return  void
-     */
-    public function sess_destroy() {
-        // Kill the session DB row
-        if ($this->sess_use_database === TRUE && $this->has_userdata('session_id')) {
-            $this->CI->db->where('session_id', $this->userdata['session_id']);
-            $this->CI->db->delete($this->sess_table_name);
-        }
-
-        // Kill the cookie
-        setcookie($this->sess_cookie_name, addslashes(serialize(array())), ($this->now - 31500000),
-            $this->cookie_path, $this->cookie_domain, 0);
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Get a reference to user data array
+     * Give array access to the main Session object
+     * The child class MUST implement this abstract method!
      *
      * @access  public
      * @return  array - Reference to userdata
      */
-    public function &get_userdata() {
-        // Return reference to array
-        return $this->userdata;
-    }
+    abstract public function &get_userdata();
+}
+// END SessionDriver Class
 
-    // --------------------------------------------------------------------
-
-    /**
-     * Fetch the current session data if it exists
-     *
-     * @access  private
-     * @return  bool
-     */
-    private function _sess_read() {
-        // Fetch the cookie
-        $session = $this->CI->input->cookie($this->sess_cookie_name);
-
-        // No cookie?  Goodbye cruel world!...
-        if ($session === FALSE) {
-            log_message('debug', 'A session cookie was not found.');
-            return FALSE;
-        }
-
-        // Decrypt the cookie data
-        if ($this->sess_encrypt_cookie == TRUE) {
-            $session = $this->CI->encrypt->decode($session);
-        }
-        else {
-            // encryption was not used, so we need to check the md5 hash
-            $hash    = substr($session, strlen($session)-32); // get last 32 chars
-            $session = substr($session, 0, strlen($session)-32);
-
-            // Does the md5 hash match?  This is to prevent manipulation of session data in userspace
-            if ($hash !==  md5($session.$this->encryption_key)) {
-                log_message('error', 'The session cookie data did not match what was expected. '.
-                    'This could be a possible hacking attempt.');
-                $this->sess_destroy();
-                return FALSE;
-            }
-        }
-
-        // Unserialize the session array
-        $session = $this->_unserialize($session);
-
-        // Is the session data we unserialized an array with the correct format?
-        if ( ! is_array($session) || ! isset($session['session_id']) || ! isset($session['ip_address']) ||
-        ! isset($session['user_agent']) || ! isset($session['last_activity'])) {
-            $this->sess_destroy();
-            return FALSE;
-        }
-
-        // Is the session current?
-        if (($session['last_activity'] + $this->sess_expiration) < $this->now()) {
-            $this->sess_destroy();
-            return FALSE;
-        }
-
-        // Does the IP Match?
-        if ($this->sess_match_ip == TRUE && $session['ip_address'] != $this->CI->input->ip_address()) {
-            $this->sess_destroy();
-            return FALSE;
-        }
-
-        // Does the User Agent Match?
-        if ($this->sess_match_useragent == TRUE &&
-        trim($session['user_agent']) != trim(substr($this->CI->input->user_agent(), 0, 50))) {
-            $this->sess_destroy();
-            return FALSE;
-        }
-
-        // Is there a corresponding session in the DB?
-        if ($this->sess_use_database === TRUE) {
-            $this->CI->db->where('session_id', $session['session_id']);
-
-            if ($this->sess_match_ip == TRUE)
-            {
-                $this->CI->db->where('ip_address', $session['ip_address']);
-            }
-
-            if ($this->sess_match_useragent == TRUE)
-            {
-                $this->CI->db->where('user_agent', $session['user_agent']);
-            }
-
-            $query = $this->CI->db->get($this->sess_table_name);
-
-            // No result?  Kill it!
-            if ($query->num_rows() == 0)
-            {
-                $this->sess_destroy();
-                return FALSE;
-            }
-
-            // Is there custom data?  If so, add it to the main session array
-            $row = $query->row();
-            if (isset($row->user_data) && $row->user_data != '')
-            {
-                $custom_data = $this->_unserialize($row->user_data);
-
-                if (is_array($custom_data))
-                {
-                    foreach ($custom_data as $key => $val)
-                    {
-                        $session[$key] = $val;
-                    }
-                }
-            }
-        }
-
-        // Session is valid!
-        $this->userdata = $session;
-        unset($session);
-
-        return TRUE;
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Create a new session
-     *
-     * @access  private
-     * @return  void
-     */
-    private function _sess_create() {
-        $sessid = '';
-        while (strlen($sessid) < 32) {
-            $sessid .= mt_rand(0, mt_getrandmax());
-        }
-
-        // To make the session ID even more secure we'll combine it with the user's IP
-        $sessid .= $this->CI->input->ip_address();
-
-        $this->set_userdata('session_id', md5(uniqid($sessid, TRUE)));
-        $this->set_userdata('ip_address', $this->CI->input->ip_address());
-        $this->set_userdata('user_agent', substr($this->CI->input->user_agent(), 0, 50));
-        $this->set_userdata('last_activity',$this->now());
-
-
-        // Save the data to the DB if needed
-        if ($this->sess_use_database === TRUE) {
-            $this->CI->db->query($this->CI->db->insert_string($this->sess_table_name, $this->all_userdata()));
-        }
-
-        // Write the cookie
-        $this->_set_cookie();
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Update an existing session
-     *
-     * @access  private
-     * @return  void
-     */
-    private function _sess_update() {
-        // We only update the session every five minutes by default
-        if (($this->userdata['last_activity'] + $this->sess_time_to_update) >= $this->now()) {
-            return;
-        }
-
-        // Save the old session id so we know which record to
-        // update in the database if we need it
-        $old_sessid = $this->userdata['session_id'];
-        $new_sessid = '';
-        while (strlen($new_sessid) < 32) {
-            $new_sessid .= mt_rand(0, mt_getrandmax());
-        }
-
-        // To make the session ID even more secure we'll combine it with the user's IP
-        $new_sessid .= $this->CI->input->ip_address();
-
-        // Turn it into a hash
-        $new_sessid = md5(uniqid($new_sessid, TRUE));
-
-        // Update the session data in the session data array
-        $this->set_userdata('session_id', $new_sessid);
-        $this->set_userdata('last_activity', $this->now());
-
-        // _set_cookie() will handle this for us if we aren't using database sessions
-        // by pushing all userdata to the cookie.
-        $cookie_data = NULL;
-
-        // Update the session ID and last_activity field in the DB if needed
-        if ($this->sess_use_database === TRUE) {
-            // set cookie explicitly to only have our session data
-            $cookie_data = array();
-            foreach (array('session_id','ip_address','user_agent','last_activity') as $val) {
-                $cookie_data[$val] = $this->userdata[$val];
-            }
-
-            $this->CI->db->query($this->CI->db->update_string($this->sess_table_name,
-                array('last_activity' => $this->now(), 'session_id' => $new_sessid),
-                array('session_id' => $old_sessid)));
-        }
-
-        // Write the cookie
-        $this->_set_cookie($cookie_data);
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Get the "now" time
-     *
-     * @access  private
-     * @return  int
-     */
-    private function _get_time() {
-        if (strtolower($this->time_reference) == 'gmt') {
-            $now = time();
-            $time = mktime(gmdate('H', $now), gmdate('i', $now), gmdate('s', $now), gmdate('m', $now),
-                gmdate('d', $now), gmdate('Y', $now));
-        }
-        else {
-            $time = time();
-        }
-
-        return $time;
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Write the session cookie
-     *
-     * @access  private
-     * @param   $cookie_data    array - Cookie name/value pairs
-     * @return  void
-     */
-    private function _set_cookie(array $cookie_data = NULL) {
-        if (is_null($cookie_data)) {
-            $cookie_data = $this->all_userdata();
-        }
-
-        // Serialize the userdata for the cookie
-        $cookie_data = $this->_serialize($cookie_data);
-
-        if ($this->sess_encrypt_cookie == TRUE) {
-            $cookie_data = $this->CI->encrypt->encode($cookie_data);
-        }
-        else {
-            // if encryption is not used, we provide an md5 hash to prevent userside tampering
-            $cookie_data = $cookie_data.md5($cookie_data.$this->encryption_key);
-        }
-
-        $expire = ($this->sess_expire_on_close === TRUE) ? 0 : $this->sess_expiration + time();
-
-        // Set the cookie
-        setcookie($this->sess_cookie_name, $cookie_data, $expire, $this->cookie_path, $this->cookie_domain, 0);
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Serialize an array
-     *
-     * This function first converts any slashes found in the array to a temporary
-     * marker, so when it gets unserialized the slashes will be preserved
-     *
-     * @access  private
-     * @param   $data   mixed - Data to serialize
-     * @return  string
-     */
-    private function _serialize($data) {
-        if (is_array($data)) {
-            foreach ($data as $key => $val) {
-                if (is_string($val)) {
-                    $data[$key] = str_replace('\\', '{{slash}}', $val);
-                }
-            }
-        }
-        else {
-            if (is_string($data)) {
-                $data = str_replace('\\', '{{slash}}', $data);
-            }
-        }
-
-        return serialize($data);
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Unserialize
-     *
-     * This function unserializes a data string, then converts any
-     * temporary slash markers back to actual slashes
-     *
-     * @access  private
-     * @param   $data   string - Data to unserialize
-     * @return  mixed
-     */
-    private function _unserialize($data) {
-        $data = @unserialize(strip_slashes($data));
-
-        if (is_array($data)) {
-            foreach ($data as $key => $val) {
-                if (is_string($val)) {
-                    $data[$key] = str_replace('{{slash}}', '\\', $val);
-                }
-            }
-
-            return $data;
-        }
-
-        return (is_string($data)) ? str_replace('{{slash}}', '\\', $data) : $data;
-    }
-
-    // --------------------------------------------------------------------
-
-    /**
-     * Garbage collection
-     *
-     * This deletes expired session rows from database
-     * if the probability percentage is met
-     *
-     * @access  private
-     * @return  void
-     */
-    private function _sess_gc() {
-        if ($this->sess_use_database != TRUE) {
-            return;
-        }
-
-        srand(time());
-        if ((rand() % 100) < self::gc_probability) {
-            $expire = $this->now() - $this->sess_expiration;
-
-            $this->CI->db->where('last_activity < '.$expire);
-            $this->CI->db->delete($this->sess_table_name);
-
-            log_message('debug', 'Session garbage collection performed.');
-        }
-    }
-}
-// END Session_Cookie Class
 
 /* End of file Session.php */
 /* Location: ./system/libraries/Session/Session.php */

File system/libraries/Upload.php

 	 */
 	public function do_upload($field = 'userfile')
 	{
-		
+
 	// Is $_FILES[$field] set? If not, no reason to continue.
 		if ( ! isset($_FILES[$field]))
 		{
 
 		if (count($this->mimes) == 0)
 		{
-			if (@require_once(APPPATH.'config/mimes'.EXT))
+			if (is_file(APPPATH.'config/'.ENVIRONMENT.'/mimes'.EXT))
 			{
-				$this->mimes = $mimes;
-				unset($mimes);
+				include(APPPATH.'config/'.ENVIRONMENT.'/mimes'.EXT);
 			}
+			elseif (is_file(APPPATH.'config/mimes'.EXT))
+			{
+				include(APPPATH.'config//mimes'.EXT);
+			}
+			else
+			{
+				return FALSE;
+			}
+
+			$this->mimes = $mimes;
+			unset($mimes);
 		}
 
 		return ( ! isset($this->mimes[$mime])) ? FALSE : $this->mimes[$mime];

File system/libraries/User_agent.php

 	 */
 	private function _load_agent_file()
 	{
-		if ( ! @include(APPPATH.'config/user_agents'.EXT))
+		if (is_file(APPPATH.'config/'.ENVIRONMENT.'/user_agents'.EXT))
+		{
+			include(APPPATH.'config/'.ENVIRONMENT.'/user_agents'.EXT);
+		}
+		elseif (is_file(APPPATH.'config/user_agents'.EXT))
+		{
+			include(APPPATH.'config/user_agents'.EXT);
+		}
+		else
 		{
 			return FALSE;
 		}

File user_guide/changelog.html

 
 <p>The <img src="images/reactor-bullet.png" width="16" height="16" alt="Reactor Marker" /> indicates items that were contributed to CodeIgniter via CodeIgniter Reactor.</p>
 
-<h2>Version 2.0.1</h2>
+<h2>Version 2.0.2</h2>
 <p>Release Date: n/a<br />
 Hg Tag: n/a</p>
 
 <ul>
 	<li>General changes
 		<ul>
+			<li class="reactor"><kbd>constants.php</kbd> will now be loaded from the environment folder if available.</li>
+			<li class="reactor">Added language key error logging</li>
+			<li class="reactor">Added Environment Support for Hooks.</li>
+		</ul>
+	</li>
+	<li>Database
+		<ul>
+			<li class="reactor"><kbd>$this->db->count_all_results()</kbd> will now return an integer instead of a string.</li>
+		</ul>
+	</li>
+</ul>
+
+<h3>Bug fixes for 2.0.2</h3>
+<ul>
+	<li class="reactor">Fixed a bug (Reactor #145) where the Output Library had parse_exec_vars set to protected.</li>
+	<li class="reactor">Fixed a bug (Reactor #80) where is_really_writable would create an empty file when on Windows or with safe_mode enabled.</li>
+	<li class="reactor">Fixed various bugs with User Guide.</li>
+	<li class="reactor">Added form_validation_lang entries for <kbd>decimal</kbd>, <kbd>less_than</kbd> and <kbd>greater_than</kbd>.</li>
+</ul>
+
+<h2>Version 2.0.1</h2>
+<p>Release Date: March, 15, 2011<br />
+Hg Tag: v2.0.1</p>
+
+<ul>
+	<li>General changes
+		<ul>
 			<li>Added <kbd>$config['cookie_secure']</kbd> to the config file to allow requiring a secure (HTTPS) in order to set cookies.</li>
 			<li class="reactor">Added the constant <kbd>CI_CORE</kbd> to help differentiate between Core: TRUE and Reactor: FALSE.</li>
-			<li class="reactor">Added an <kbd>ENVIRONMENT</kbd> constant in index.php, which affects PHP error reporting settings, and optionally, 
+			<li class="reactor">Added an <kbd>ENVIRONMENT</kbd> constant in index.php, which affects PHP error reporting settings, and optionally,
                 which configuration files are loaded (see below). Read more on the <a href="general/environments.html">Handling Environments</a> page.</li>
 			<li class="reactor">Added support for <a href="libraries/config.html#environments">environment-specific</a> configuration files.</li>
 		</ul>

File user_guide/database/active_record.html

 <br /><br />
 // Produces: INSERT INTO mytable (title, content, date) VALUES ('My Title', 'My Content', 'My Date')</code>
 
-<p>The first parameter will contain the table name, the second is an associative array of values.</p>
+<p>The first parameter will contain the table name, the second is an object.</p>
 
 <p class="important"><strong>Note:</strong> All values are escaped automatically producing safer queries.</p>
 

File user_guide/general/creating_libraries.html

 <br /><br />
 class Someclass {<br />
 <br />
-&nbsp;&nbsp;&nbsp;&nbsp;function some_function()<br />
+&nbsp;&nbsp;&nbsp;&nbsp;public function some_function()<br />
 &nbsp;&nbsp;&nbsp;&nbsp;{<br />
 &nbsp;&nbsp;&nbsp;&nbsp;}<br />
 }<br /><br />
-?&gt;</code>
+/* End of file Someclass.php */</code>
 
 
 <h2>Using Your Class</h2>
 <br />
 class Someclass {<br />
 <br />
-&nbsp;&nbsp;&nbsp;&nbsp;function __construct($params)<br />
+&nbsp;&nbsp;&nbsp;&nbsp;public function __construct($params)<br />
 &nbsp;&nbsp;&nbsp;&nbsp;{<br />
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Do something with $params<br />
 &nbsp;&nbsp;&nbsp;&nbsp;}<br />
 <code>
 class MY_Email extends CI_Email {<br />
 <br />
-&nbsp;&nbsp;&nbsp;&nbsp;function __construct()<br />
+&nbsp;&nbsp;&nbsp;&nbsp;public function __construct()<br />
 &nbsp;&nbsp;&nbsp;&nbsp;{<br />
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;parent::__construct();<br />
 &nbsp;&nbsp;&nbsp;&nbsp;}<br />

File user_guide/general/environments.html

 <head>
 
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-<title>Creating Libraries : CodeIgniter User Guide</title>
+<title>Handling Multiple Environments : CodeIgniter User Guide</title>
 
 <style type='text/css' media='all'>@import url('../userguide.css');</style>
 <link rel='stylesheet' type='text/css' media='all' href='../userguide.css' />
 <div id="masthead">
 <table cellpadding="0" cellspacing="0" border="0" style="width:100%">
 <tr>
-<td><h1>CodeIgniter User Guide Version 2.0.0</h1></td>
+<td><h1>CodeIgniter User Guide Version 2.0.1</h1></td>
 <td id="breadcrumb_right"><a href="../toc.html">Table of Contents Page</a></td>
 </tr>
 </table>
 <td id="breadcrumb">
 <a href="http://codeigniter.com/">CodeIgniter Home</a> &nbsp;&#8250;&nbsp;
 <a href="../index.html">User Guide Home</a> &nbsp;&#8250;&nbsp;
-Creating Libraries
+Handling Multiple Environments
 </td>
 <td id="searchbox"><form method="get" action="http://www.google.com/search"><input type="hidden" name="as_sitesearch" id="as_sitesearch" value="codeigniter.com/user_guide/" />Search User Guide&nbsp; <input type="text" class="input" style="width:200px;" name="q" id="q" size="31" maxlength="255" value="" />&nbsp;<input type="submit" class="submit" name="sa" value="Go" /></form></td>
 </tr>
 
 <div id="footer">
 <p>
-Previous Topic:&nbsp;&nbsp;<a href="libraries.html">Using CodeIgniter Libraries</a>
+Previous Topic:&nbsp;&nbsp;<a href="managing_apps.html">Managing Applications</a>
 &nbsp;&nbsp;&nbsp;&middot;&nbsp;&nbsp;
 <a href="#top">Top of Page</a>&nbsp;&nbsp;&nbsp;&middot;&nbsp;&nbsp;
 <a href="../index.html">User Guide Home</a>&nbsp;&nbsp;&nbsp;&middot;&nbsp;&nbsp;
-Next Topic:&nbsp;&nbsp;<a href="drivers.html">Using CodeIgniter Drivers</a>
+Next Topic:&nbsp;&nbsp;<a href="alternative_php.html">Alternative PHP Syntax</a>
 </p>
 <p><a href="http://codeigniter.com">CodeIgniter</a> &nbsp;&middot;&nbsp; Copyright &#169; 2006 - 2011 &nbsp;&middot;&nbsp; <a href="http://ellislab.com/">EllisLab, Inc.</a></p>
 </div>

File user_guide/installation/upgrade_201.html

 
 <p>This config file has been updated to contain more mime types, please copy it to <kbd>application/config/mimes.php</kbd>.</p>
 
+<h2>Step 3: Check for forms posting to default controller</h2>
+
+<p>
+	The default behavior for <kbd>form_open()</kbd> when called with no parameters used to be to post to the default controller, but it will now just leave an empty action="" meaning the form will submit to the current URL.
+	If submitting to the default controller was the expected behavior it will need to be changed from:
+</p>
+
+<code>echo form_open(); //&lt;form action="" method="post" accept-charset="utf-8"></code>
+
+<p>to use either a / or <kbd>base_url()</kbd>:</p>
+
+<code>echo form_open('/'); //&lt;form action="http://example.com/index.php/" method="post" accept-charset="utf-8"><br/>
+echo form_open(base_url()); //&lt;form action="http://example.com/" method="post" accept-charset="utf-8"></code>
+
 </div>
 <!-- END CONTENT -->