Merge branch 'develop' of github.com:philsturgeon/codeigniter-reactor into develop
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d740c49
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+.DS_Store
+
+application/cache/*
+!application/cache/index.html
+!application/cache/.htaccess
+
+application/logs/*
+!application/logs/index.html
+!application/logs/.htaccess
\ No newline at end of file
diff --git a/.hgflow b/.hgflow
deleted file mode 100644
index ad6e56e..0000000
--- a/.hgflow
+++ /dev/null
@@ -1,8 +0,0 @@
-[Basic]
-develop = develop
-feature = feature/
-version_tag =
-publish = default
-release = release/
-hotfix = hotfix/
-
diff --git a/.hgignore b/.hgignore
deleted file mode 100644
index 5ee4d82..0000000
--- a/.hgignore
+++ /dev/null
@@ -1,7 +0,0 @@
-syntax: glob
-
-.DS_Store
-
-syntax: regexp
-application/cache/(?!index\.html|\.htaccess)
-application/logs/(?!index\.html|\.htaccess)
diff --git a/application/config/migration.php b/application/config/migration.php
index 509fd90..dba8700 100644
--- a/application/config/migration.php
+++ b/application/config/migration.php
@@ -9,7 +9,7 @@
| and disable it back when you're done.
|
*/
-$config['migration_enabled'] = TRUE;
+$config['migration_enabled'] = FALSE;
/*
diff --git a/application/config/mimes.php b/application/config/mimes.php
index 8065794..82767d7 100644
--- a/application/config/mimes.php
+++ b/application/config/mimes.php
@@ -10,7 +10,7 @@
$mimes = array( 'hqx' => 'application/mac-binhex40',
'cpt' => 'application/mac-compactpro',
- 'csv' => array('text/x-comma-separated-values', 'text/comma-separated-values', 'application/octet-stream', 'application/vnd.ms-excel', 'text/x-csv', 'text/csv', 'application/csv', 'application/excel', 'application/vnd.msexcel'),
+ 'csv' => array('text/x-comma-separated-values', 'text/comma-separated-values', 'application/octet-stream', 'application/vnd.ms-excel', 'application/x-csv', 'text/x-csv', 'text/csv', 'application/csv', 'application/excel', 'application/vnd.msexcel'),
'bin' => 'application/macbinary',
'dms' => 'application/octet-stream',
'lha' => 'application/octet-stream',
diff --git a/system/core/CodeIgniter.php b/system/core/CodeIgniter.php
index 7b92ddf..0a1391d 100755
--- a/system/core/CodeIgniter.php
+++ b/system/core/CodeIgniter.php
@@ -27,10 +27,11 @@
* @link http://codeigniter.com/user_guide/
*/
-/*
- * ------------------------------------------------------
- * Define the CodeIgniter Version
- * ------------------------------------------------------
+/**
+ * CodeIgniter Version
+ *
+ * @var string
+ *
*/
/**
* CodeIgniter Version
@@ -40,10 +41,11 @@
*/
define('CI_VERSION', '2.0.2');
-/*
- * ------------------------------------------------------
- * Define the CodeIgniter Branch (Core = TRUE, Reactor = FALSE)
- * ------------------------------------------------------
+/**
+ * CodeIgniter Branch (Core = TRUE, Reactor = FALSE)
+ *
+ * @var boolean
+ *
*/
/**
* CodeIgniter Branch (Core = TRUE, Reactor = FALSE)
diff --git a/system/core/Config.php b/system/core/Config.php
index 5cacf86..714c466 100755
--- a/system/core/Config.php
+++ b/system/core/Config.php
@@ -218,10 +218,7 @@
// --------------------------------------------------------------------
/**
- * Fetch a config file item - adds slash after item
- *
- * The second parameter allows a slash to be added to the end of
- * the item, in the case of a path.
+ * Fetch a config file item - adds slash after item (if item is not empty)
*
* @access public
* @param string the config item name
@@ -234,6 +231,10 @@
{
return FALSE;
}
+ if( trim($this->config[$item]) == '')
+ {
+ return '';
+ }
return rtrim($this->config[$item], '/').'/';
}
@@ -242,6 +243,7 @@
/**
* Site URL
+ * Returns base_url . index_page [. uri_string]
*
* @access public
* @param string the URI string
@@ -256,14 +258,48 @@
if ($this->item('enable_query_strings') == FALSE)
{
+ $suffix = ($this->item('url_suffix') == FALSE) ? '' : $this->item('url_suffix');
+ return $this->slash_item('base_url').$this->slash_item('index_page').$this->_uri_string($uri).$suffix;
+ }
+ else
+ {
+ return $this->slash_item('base_url').$this->item('index_page').'?'.$this->_uri_string($uri);
+ }
+ }
+
+ // -------------------------------------------------------------
+
+ /**
+ * Base URL
+ * Returns base_url [. uri_string]
+ *
+ * @access public
+ * @param string $uri
+ * @return string
+ */
+ function base_url($uri = '')
+ {
+ return $this->slash_item('base_url').ltrim($this->_uri_string($uri),'/');
+ }
+
+ // -------------------------------------------------------------
+
+ /**
+ * Build URI string for use in Config::site_url() and Config::base_url()
+ *
+ * @access protected
+ * @param $uri
+ * @return string
+ */
+ protected function _uri_string($uri)
+ {
+ if ($this->item('enable_query_strings') == FALSE)
+ {
if (is_array($uri))
{
$uri = implode('/', $uri);
}
-
- $index = $this->item('index_page') == '' ? '' : $this->slash_item('index_page');
- $suffix = ($this->item('url_suffix') == FALSE) ? '' : $this->item('url_suffix');
- return $this->slash_item('base_url').$index.trim($uri, '/').$suffix;
+ $uri = trim($uri, '/');
}
else
{
@@ -277,12 +313,10 @@
$str .= $prefix.$key.'='.$val;
$i++;
}
-
$uri = $str;
}
-
- return $this->slash_item('base_url').$this->item('index_page').'?'.$uri;
}
+ return $uri;
}
// --------------------------------------------------------------------
diff --git a/system/core/Input.php b/system/core/Input.php
index bc202b3..5a033e7 100755
--- a/system/core/Input.php
+++ b/system/core/Input.php
@@ -710,7 +710,7 @@
*/
public function is_cli_request()
{
- return (bool) defined('STDIN');
+ return (php_sapi_name() == 'cli') or defined('STDIN');
}
}
diff --git a/system/core/Loader.php b/system/core/Loader.php
index 019de82..e7fa3d3 100755
--- a/system/core/Loader.php
+++ b/system/core/Loader.php
@@ -1018,22 +1018,22 @@
// first, global next
if (defined('ENVIRONMENT') AND file_exists($path .'config/'.ENVIRONMENT.'/'.strtolower($class).'.php'))
{
- include_once($path .'config/'.ENVIRONMENT.'/'.strtolower($class).'.php');
+ include($path .'config/'.ENVIRONMENT.'/'.strtolower($class).'.php');
break;
}
elseif (defined('ENVIRONMENT') AND file_exists($path .'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php'))
{
- include_once($path .'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php');
+ include($path .'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php');
break;
}
elseif (file_exists($path .'config/'.strtolower($class).'.php'))
{
- include_once($path .'config/'.strtolower($class).'.php');
+ include($path .'config/'.strtolower($class).'.php');
break;
}
elseif (file_exists($path .'config/'.ucfirst(strtolower($class)).'.php'))
{
- include_once($path .'config/'.ucfirst(strtolower($class)).'.php');
+ include($path .'config/'.ucfirst(strtolower($class)).'.php');
break;
}
}
diff --git a/system/core/URI.php b/system/core/URI.php
index 10b0b7f..a3ae20c 100755
--- a/system/core/URI.php
+++ b/system/core/URI.php
@@ -87,7 +87,7 @@
if (strtoupper($this->config->item('uri_protocol')) == 'AUTO')
{
// Is the request coming from the command line?
- if (defined('STDIN'))
+ if (php_sapi_name() == 'cli' or defined('STDIN'))
{
$this->_set_uri_string($this->_parse_cli_args());
return;
diff --git a/system/database/DB_active_rec.php b/system/database/DB_active_rec.php
index 0a25b3c..7bab729 100644
--- a/system/database/DB_active_rec.php
+++ b/system/database/DB_active_rec.php
@@ -870,11 +870,11 @@
*/
public function limit($value, $offset = '')
{
- $this->ar_limit = $value;
+ $this->ar_limit = (int) $value;
if ($offset != '')
{
- $this->ar_offset = $offset;
+ $this->ar_offset = (int) $offset;
}
return $this;
@@ -1322,7 +1322,7 @@
{
if ($this->db_debug)
{
- return $this->display_error('db_myst_use_index');
+ return $this->display_error('db_must_use_index');
}
return FALSE;
@@ -1694,7 +1694,8 @@
// is because until the user calls the from() function we don't know if there are aliases
foreach ($this->ar_select as $key => $val)
{
- $this->ar_select[$key] = $this->_protect_identifiers($val, FALSE, $this->ar_no_escape[$key]);
+ $no_escape = isset($this->ar_no_escape[$key]) ? $this->ar_no_escape[$key] : NULL;
+ $this->ar_select[$key] = $this->_protect_identifiers($val, FALSE, $no_escape);
}
$sql .= implode(', ', $this->ar_select);
diff --git a/system/database/DB_forge.php b/system/database/DB_forge.php
index a71fca7..0dd29c2 100644
--- a/system/database/DB_forge.php
+++ b/system/database/DB_forge.php
@@ -234,7 +234,7 @@
show_error('A table name is required for that operation.');
}
- $sql = $this->_rename_table($table_name, $new_table_name);
+ $sql = $this->_rename_table($this->db->dbprefix.$table_name, $this->db->dbprefix.$new_table_name);
return $this->db->query($sql);
}
diff --git a/system/database/drivers/mysql/mysql_utility.php b/system/database/drivers/mysql/mysql_utility.php
index e9747c5..48c4d63 100644
--- a/system/database/drivers/mysql/mysql_utility.php
+++ b/system/database/drivers/mysql/mysql_utility.php
@@ -96,7 +96,7 @@
}
// Get the table schema
- $query = $this->db->query("SHOW CREATE TABLE `".$this->db->database.'`.'.$table);
+ $query = $this->db->query("SHOW CREATE TABLE `".$this->db->database.'`.`'.$table.'`');
// No result means the table name was invalid
if ($query === FALSE)
diff --git a/system/database/drivers/sqlsrv/index.html b/system/database/drivers/sqlsrv/index.html
new file mode 100644
index 0000000..c942a79
--- /dev/null
+++ b/system/database/drivers/sqlsrv/index.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+ <title>403 Forbidden</title>
+</head>
+<body>
+
+<p>Directory access is forbidden.</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/system/database/drivers/sqlsrv/sqlsrv_driver.php b/system/database/drivers/sqlsrv/sqlsrv_driver.php
new file mode 100644
index 0000000..1d32792
--- /dev/null
+++ b/system/database/drivers/sqlsrv/sqlsrv_driver.php
@@ -0,0 +1,598 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.1.6 or newer
+ *
+ * @package CodeIgniter
+ * @author ExpressionEngine Dev Team
+ * @copyright Copyright (c) 2008 - 2011, EllisLab, Inc.
+ * @license http://codeigniter.com/user_guide/license.html
+ * @link http://codeigniter.com
+ * @since Version 1.0
+ * @filesource
+ */
+
+// ------------------------------------------------------------------------
+
+/**
+ * SQLSRV Database Adapter Class
+ *
+ * Note: _DB is an extender class that the app controller
+ * creates dynamically based on whether the active record
+ * class is being used or not.
+ *
+ * @package CodeIgniter
+ * @subpackage Drivers
+ * @category Database
+ * @author ExpressionEngine Dev Team
+ * @link http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_sqlsrv_driver extends CI_DB {
+
+ var $dbdriver = 'sqlsrv';
+
+ // The character used for escaping
+ var $_escape_char = '';
+
+ // clause and character used for LIKE escape sequences
+ var $_like_escape_str = " ESCAPE '%s' ";
+ var $_like_escape_chr = '!';
+
+ /**
+ * The syntax to count rows is slightly different across different
+ * database engines, so this string appears in each driver and is
+ * used for the count_all() and count_all_results() functions.
+ */
+ var $_count_string = "SELECT COUNT(*) AS ";
+ var $_random_keyword = ' ASC'; // not currently supported
+
+ /**
+ * Non-persistent database connection
+ *
+ * @access private called by the base class
+ * @return resource
+ */
+ function db_connect($pooling = false)
+ {
+ // Check for a UTF-8 charset being passed as CI's default 'utf8'.
+ $character_set = (0 === strcasecmp('utf8', $this->char_set)) ? 'UTF-8' : $this->char_set;
+
+ $connection = array(
+ 'UID' => empty($this->username) ? '' : $this->username,
+ 'PWD' => empty($this->password) ? '' : $this->password,
+ 'Database' => $this->database,
+ 'ConnectionPooling' => $pooling ? 1 : 0,
+ 'CharacterSet' => $character_set,
+ 'ReturnDatesAsStrings' => 1
+ );
+
+ // If the username and password are both empty, assume this is a
+ // 'Windows Authentication Mode' connection.
+ if(empty($connection['UID']) && empty($connection['PWD'])) {
+ unset($connection['UID'], $connection['PWD']);
+ }
+
+ return sqlsrv_connect($this->hostname, $connection);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Persistent database connection
+ *
+ * @access private called by the base class
+ * @return resource
+ */
+ function db_pconnect()
+ {
+ $this->db_connect(TRUE);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Reconnect
+ *
+ * Keep / reestablish the db connection if no queries have been
+ * sent for a length of time exceeding the server's idle timeout
+ *
+ * @access public
+ * @return void
+ */
+ function reconnect()
+ {
+ // not implemented in MSSQL
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Select the database
+ *
+ * @access private called by the base class
+ * @return resource
+ */
+ function db_select()
+ {
+ return $this->_execute('USE ' . $this->database);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Set client character set
+ *
+ * @access public
+ * @param string
+ * @param string
+ * @return resource
+ */
+ function db_set_charset($charset, $collation)
+ {
+ // @todo - add support if needed
+ return TRUE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Execute the query
+ *
+ * @access private called by the base class
+ * @param string an SQL query
+ * @return resource
+ */
+ function _execute($sql)
+ {
+ $sql = $this->_prep_query($sql);
+ return sqlsrv_query($this->conn_id, $sql, null, array(
+ 'Scrollable' => SQLSRV_CURSOR_STATIC,
+ 'SendStreamParamsAtExec' => true
+ ));
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Prep the query
+ *
+ * If needed, each database adapter can prep the query string
+ *
+ * @access private called by execute()
+ * @param string an SQL query
+ * @return string
+ */
+ function _prep_query($sql)
+ {
+ return $sql;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Begin Transaction
+ *
+ * @access public
+ * @return bool
+ */
+ function trans_begin($test_mode = FALSE)
+ {
+ if ( ! $this->trans_enabled)
+ {
+ return TRUE;
+ }
+
+ // When transactions are nested we only begin/commit/rollback the outermost ones
+ if ($this->_trans_depth > 0)
+ {
+ return TRUE;
+ }
+
+ // Reset the transaction failure flag.
+ // If the $test_mode flag is set to TRUE transactions will be rolled back
+ // even if the queries produce a successful result.
+ $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
+
+ return sqlsrv_begin_transaction($this->conn_id);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Commit Transaction
+ *
+ * @access public
+ * @return bool
+ */
+ function trans_commit()
+ {
+ if ( ! $this->trans_enabled)
+ {
+ return TRUE;
+ }
+
+ // When transactions are nested we only begin/commit/rollback the outermost ones
+ if ($this->_trans_depth > 0)
+ {
+ return TRUE;
+ }
+
+ return sqlsrv_commit($this->conn_id);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Rollback Transaction
+ *
+ * @access public
+ * @return bool
+ */
+ function trans_rollback()
+ {
+ if ( ! $this->trans_enabled)
+ {
+ return TRUE;
+ }
+
+ // When transactions are nested we only begin/commit/rollback the outermost ones
+ if ($this->_trans_depth > 0)
+ {
+ return TRUE;
+ }
+
+ return sqlsrv_rollback($this->conn_id);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Escape String
+ *
+ * @access public
+ * @param string
+ * @param bool whether or not the string will be used in a LIKE condition
+ * @return string
+ */
+ function escape_str($str, $like = FALSE)
+ {
+ // Escape single quotes
+ return str_replace("'", "''", $str);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Affected Rows
+ *
+ * @access public
+ * @return integer
+ */
+ function affected_rows()
+ {
+ return @sqlrv_rows_affected($this->conn_id);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Insert ID
+ *
+ * Returns the last id created in the Identity column.
+ *
+ * @access public
+ * @return integer
+ */
+ function insert_id()
+ {
+ return $this->query('select @@IDENTITY as insert_id')->row('insert_id');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Parse major version
+ *
+ * Grabs the major version number from the
+ * database server version string passed in.
+ *
+ * @access private
+ * @param string $version
+ * @return int16 major version number
+ */
+ function _parse_major_version($version)
+ {
+ preg_match('/([0-9]+)\.([0-9]+)\.([0-9]+)/', $version, $ver_info);
+ return $ver_info[1]; // return the major version b/c that's all we're interested in.
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Version number query string
+ *
+ * @access public
+ * @return string
+ */
+ function _version()
+ {
+ $info = sqlsrv_server_info($this->conn_id);
+ return sprintf("select '%s' as ver", $info['SQLServerVersion']);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * "Count All" query
+ *
+ * Generates a platform-specific query string that counts all records in
+ * the specified database
+ *
+ * @access public
+ * @param string
+ * @return string
+ */
+ function count_all($table = '')
+ {
+ if ($table == '')
+ return '0';
+
+ $query = $this->query("SELECT COUNT(*) AS numrows FROM " . $this->dbprefix . $table);
+
+ if ($query->num_rows() == 0)
+ return '0';
+
+ $row = $query->row();
+ return $row->numrows;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * List table query
+ *
+ * Generates a platform-specific query string so that the table names can be fetched
+ *
+ * @access private
+ * @param boolean
+ * @return string
+ */
+ function _list_tables($prefix_limit = FALSE)
+ {
+ return "SELECT name FROM sysobjects WHERE type = 'U' ORDER BY name";
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * List column query
+ *
+ * Generates a platform-specific query string so that the column names can be fetched
+ *
+ * @access private
+ * @param string the table name
+ * @return string
+ */
+ function _list_columns($table = '')
+ {
+ return "SELECT * FROM INFORMATION_SCHEMA.Columns WHERE TABLE_NAME = '".$this->_escape_table($table)."'";
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field data query
+ *
+ * Generates a platform-specific query so that the column data can be retrieved
+ *
+ * @access public
+ * @param string the table name
+ * @return object
+ */
+ function _field_data($table)
+ {
+ return "SELECT TOP 1 * FROM " . $this->_escape_table($table);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * The error message string
+ *
+ * @access private
+ * @return string
+ */
+ function _error_message()
+ {
+ $error = array_shift(sqlsrv_errors());
+ return !empty($error['message']) ? $error['message'] : null;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * The error message number
+ *
+ * @access private
+ * @return integer
+ */
+ function _error_number()
+ {
+ $error = array_shift(sqlsrv_errors());
+ return isset($error['SQLSTATE']) ? $error['SQLSTATE'] : null;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Escape Table Name
+ *
+ * This function adds backticks if the table name has a period
+ * in it. Some DBs will get cranky unless periods are escaped
+ *
+ * @access private
+ * @param string the table name
+ * @return string
+ */
+ function _escape_table($table)
+ {
+ return $table;
+ }
+
+
+ /**
+ * Escape the SQL Identifiers
+ *
+ * This function escapes column and table names
+ *
+ * @access private
+ * @param string
+ * @return string
+ */
+ function _escape_identifiers($item)
+ {
+ return $item;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * From Tables
+ *
+ * This function implicitly groups FROM tables so there is no confusion
+ * about operator precedence in harmony with SQL standards
+ *
+ * @access public
+ * @param type
+ * @return type
+ */
+ function _from_tables($tables)
+ {
+ if ( ! is_array($tables))
+ {
+ $tables = array($tables);
+ }
+
+ return implode(', ', $tables);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Insert statement
+ *
+ * Generates a platform-specific insert string from the supplied data
+ *
+ * @access public
+ * @param string the table name
+ * @param array the insert keys
+ * @param array the insert values
+ * @return string
+ */
+ function _insert($table, $keys, $values)
+ {
+ return "INSERT INTO ".$this->_escape_table($table)." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Update statement
+ *
+ * Generates a platform-specific update string from the supplied data
+ *
+ * @access public
+ * @param string the table name
+ * @param array the update data
+ * @param array the where clause
+ * @param array the orderby clause
+ * @param array the limit clause
+ * @return string
+ */
+ function _update($table, $values, $where)
+ {
+ foreach($values as $key => $val)
+ {
+ $valstr[] = $key." = ".$val;
+ }
+
+ return "UPDATE ".$this->_escape_table($table)." SET ".implode(', ', $valstr)." WHERE ".implode(" ", $where);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Truncate statement
+ *
+ * Generates a platform-specific truncate string from the supplied data
+ * If the database does not support the truncate() command
+ * This function maps to "DELETE FROM table"
+ *
+ * @access public
+ * @param string the table name
+ * @return string
+ */
+ function _truncate($table)
+ {
+ return "TRUNCATE ".$table;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Delete statement
+ *
+ * Generates a platform-specific delete string from the supplied data
+ *
+ * @access public
+ * @param string the table name
+ * @param array the where clause
+ * @param string the limit clause
+ * @return string
+ */
+ function _delete($table, $where)
+ {
+ return "DELETE FROM ".$this->_escape_table($table)." WHERE ".implode(" ", $where);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Limit string
+ *
+ * Generates a platform-specific LIMIT clause
+ *
+ * @access public
+ * @param string the sql query string
+ * @param integer the number of rows to limit the query to
+ * @param integer the offset value
+ * @return string
+ */
+ function _limit($sql, $limit, $offset)
+ {
+ $i = $limit + $offset;
+
+ return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$i.' ', $sql);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Close DB Connection
+ *
+ * @access public
+ * @param resource
+ * @return void
+ */
+ function _close($conn_id)
+ {
+ @sqlsrv_close($conn_id);
+ }
+
+}
+
+
+
+/* End of file mssql_driver.php */
+/* Location: ./system/database/drivers/mssql/mssql_driver.php */
\ No newline at end of file
diff --git a/system/database/drivers/sqlsrv/sqlsrv_forge.php b/system/database/drivers/sqlsrv/sqlsrv_forge.php
new file mode 100644
index 0000000..cc88ec5
--- /dev/null
+++ b/system/database/drivers/sqlsrv/sqlsrv_forge.php
@@ -0,0 +1,248 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.1.6 or newer
+ *
+ * @package CodeIgniter
+ * @author ExpressionEngine Dev Team
+ * @copyright Copyright (c) 2008 - 2011, EllisLab, Inc.
+ * @license http://codeigniter.com/user_guide/license.html
+ * @link http://codeigniter.com
+ * @since Version 1.0
+ * @filesource
+ */
+
+// ------------------------------------------------------------------------
+
+/**
+ * SQLSRV Forge Class
+ *
+ * @category Database
+ * @author ExpressionEngine Dev Team
+ * @link http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_sqlsrv_forge extends CI_DB_forge {
+
+ /**
+ * Create database
+ *
+ * @access private
+ * @param string the database name
+ * @return bool
+ */
+ function _create_database($name)
+ {
+ return "CREATE DATABASE ".$name;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Drop database
+ *
+ * @access private
+ * @param string the database name
+ * @return bool
+ */
+ function _drop_database($name)
+ {
+ return "DROP DATABASE ".$name;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Drop Table
+ *
+ * @access private
+ * @return bool
+ */
+ function _drop_table($table)
+ {
+ return "DROP TABLE ".$this->db->_escape_identifiers($table);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Create Table
+ *
+ * @access private
+ * @param string the table name
+ * @param array the fields
+ * @param mixed primary key(s)
+ * @param mixed key(s)
+ * @param boolean should 'IF NOT EXISTS' be added to the SQL
+ * @return bool
+ */
+ function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists)
+ {
+ $sql = 'CREATE TABLE ';
+
+ if ($if_not_exists === TRUE)
+ {
+ $sql .= 'IF NOT EXISTS ';
+ }
+
+ $sql .= $this->db->_escape_identifiers($table)." (";
+ $current_field_count = 0;
+
+ foreach ($fields as $field=>$attributes)
+ {
+ // Numeric field names aren't allowed in databases, so if the key is
+ // numeric, we know it was assigned by PHP and the developer manually
+ // entered the field information, so we'll simply add it to the list
+ if (is_numeric($field))
+ {
+ $sql .= "\n\t$attributes";
+ }
+ else
+ {
+ $attributes = array_change_key_case($attributes, CASE_UPPER);
+
+ $sql .= "\n\t".$this->db->_protect_identifiers($field);
+
+ $sql .= ' '.$attributes['TYPE'];
+
+ if (array_key_exists('CONSTRAINT', $attributes))
+ {
+ $sql .= '('.$attributes['CONSTRAINT'].')';
+ }
+
+ if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE)
+ {
+ $sql .= ' UNSIGNED';
+ }
+
+ if (array_key_exists('DEFAULT', $attributes))
+ {
+ $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\'';
+ }
+
+ if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE)
+ {
+ $sql .= ' NULL';
+ }
+ else
+ {
+ $sql .= ' NOT NULL';
+ }
+
+ if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE)
+ {
+ $sql .= ' AUTO_INCREMENT';
+ }
+ }
+
+ // don't add a comma on the end of the last field
+ if (++$current_field_count < count($fields))
+ {
+ $sql .= ',';
+ }
+ }
+
+ if (count($primary_keys) > 0)
+ {
+ $primary_keys = $this->db->_protect_identifiers($primary_keys);
+ $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")";
+ }
+
+ if (is_array($keys) && count($keys) > 0)
+ {
+ foreach ($keys as $key)
+ {
+ if (is_array($key))
+ {
+ $key = $this->db->_protect_identifiers($key);
+ }
+ else
+ {
+ $key = array($this->db->_protect_identifiers($key));
+ }
+
+ $sql .= ",\n\tFOREIGN KEY (" . implode(', ', $key) . ")";
+ }
+ }
+
+ $sql .= "\n)";
+
+ return $sql;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Alter table query
+ *
+ * Generates a platform-specific query so that a table can be altered
+ * Called by add_column(), drop_column(), and column_alter(),
+ *
+ * @access private
+ * @param string the ALTER type (ADD, DROP, CHANGE)
+ * @param string the column name
+ * @param string the table name
+ * @param string the column definition
+ * @param string the default value
+ * @param boolean should 'NOT NULL' be added
+ * @param string the field after which we should add the new field
+ * @return object
+ */
+ function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '')
+ {
+ $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ".$this->db->_protect_identifiers($column_name);
+
+ // DROP has everything it needs now.
+ if ($alter_type == 'DROP')
+ {
+ return $sql;
+ }
+
+ $sql .= " $column_definition";
+
+ if ($default_value != '')
+ {
+ $sql .= " DEFAULT \"$default_value\"";
+ }
+
+ if ($null === NULL)
+ {
+ $sql .= ' NULL';
+ }
+ else
+ {
+ $sql .= ' NOT NULL';
+ }
+
+ if ($after_field != '')
+ {
+ $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field);
+ }
+
+ return $sql;
+
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Rename a table
+ *
+ * Generates a platform-specific query so that a table can be renamed
+ *
+ * @access private
+ * @param string the old table name
+ * @param string the new table name
+ * @return string
+ */
+ function _rename_table($table_name, $new_table_name)
+ {
+ // I think this syntax will work, but can find little documentation on renaming tables in MSSQL
+ $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name);
+ return $sql;
+ }
+
+}
+
+/* End of file mssql_forge.php */
+/* Location: ./system/database/drivers/mssql/mssql_forge.php */
\ No newline at end of file
diff --git a/system/database/drivers/sqlsrv/sqlsrv_result.php b/system/database/drivers/sqlsrv/sqlsrv_result.php
new file mode 100644
index 0000000..bf0abd1
--- /dev/null
+++ b/system/database/drivers/sqlsrv/sqlsrv_result.php
@@ -0,0 +1,169 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.1.6 or newer
+ *
+ * @package CodeIgniter
+ * @author ExpressionEngine Dev Team
+ * @copyright Copyright (c) 2008 - 2011, EllisLab, Inc.
+ * @license http://codeigniter.com/user_guide/license.html
+ * @link http://codeigniter.com
+ * @since Version 1.0
+ * @filesource
+ */
+
+// ------------------------------------------------------------------------
+
+/**
+ * SQLSRV Result Class
+ *
+ * This class extends the parent result class: CI_DB_result
+ *
+ * @category Database
+ * @author ExpressionEngine Dev Team
+ * @link http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_sqlsrv_result extends CI_DB_result {
+
+ /**
+ * Number of rows in the result set
+ *
+ * @access public
+ * @return integer
+ */
+ function num_rows()
+ {
+ return @sqlsrv_num_rows($this->result_id);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Number of fields in the result set
+ *
+ * @access public
+ * @return integer
+ */
+ function num_fields()
+ {
+ return @sqlsrv_num_fields($this->result_id);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Fetch Field Names
+ *
+ * Generates an array of column names
+ *
+ * @access public
+ * @return array
+ */
+ function list_fields()
+ {
+ $field_names = array();
+ foreach(sqlsrv_field_metadata($this->result_id) as $offset => $field)
+ {
+ $field_names[] = $field['Name'];
+ }
+
+ return $field_names;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Field data
+ *
+ * Generates an array of objects containing field meta-data
+ *
+ * @access public
+ * @return array
+ */
+ function field_data()
+ {
+ $retval = array();
+ foreach(sqlsrv_field_metadata($this->result_id) as $offset => $field)
+ {
+ $F = new stdClass();
+ $F->name = $field['Name'];
+ $F->type = $field['Type'];
+ $F->max_length = $field['Size'];
+ $F->primary_key = 0;
+ $F->default = '';
+
+ $retval[] = $F;
+ }
+
+ return $retval;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Free the result
+ *
+ * @return null
+ */
+ function free_result()
+ {
+ if (is_resource($this->result_id))
+ {
+ sqlsrv_free_stmt($this->result_id);
+ $this->result_id = FALSE;
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Data Seek
+ *
+ * Moves the internal pointer to the desired offset. We call
+ * this internally before fetching results to make sure the
+ * result set starts at zero
+ *
+ * @access private
+ * @return array
+ */
+ function _data_seek($n = 0)
+ {
+ // Not implemented
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Result - associative array
+ *
+ * Returns the result set as an array
+ *
+ * @access private
+ * @return array
+ */
+ function _fetch_assoc()
+ {
+ return sqlsrv_fetch_array($this->result_id, SQLSRV_FETCH_ASSOC);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Result - object
+ *
+ * Returns the result set as an object
+ *
+ * @access private
+ * @return object
+ */
+ function _fetch_object()
+ {
+ return sqlsrv_fetch_object($this->result_id);
+ }
+
+}
+
+
+/* End of file mssql_result.php */
+/* Location: ./system/database/drivers/mssql/mssql_result.php */
\ No newline at end of file
diff --git a/system/database/drivers/sqlsrv/sqlsrv_utility.php b/system/database/drivers/sqlsrv/sqlsrv_utility.php
new file mode 100644
index 0000000..13a1850
--- /dev/null
+++ b/system/database/drivers/sqlsrv/sqlsrv_utility.php
@@ -0,0 +1,88 @@
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+/**
+ * CodeIgniter
+ *
+ * An open source application development framework for PHP 5.1.6 or newer
+ *
+ * @package CodeIgniter
+ * @author ExpressionEngine Dev Team
+ * @copyright Copyright (c) 2008 - 2011, EllisLab, Inc.
+ * @license http://codeigniter.com/user_guide/license.html
+ * @link http://codeigniter.com
+ * @since Version 1.0
+ * @filesource
+ */
+
+// ------------------------------------------------------------------------
+
+/**
+ * SQLSRV Utility Class
+ *
+ * @category Database
+ * @author ExpressionEngine Dev Team
+ * @link http://codeigniter.com/user_guide/database/
+ */
+class CI_DB_sqlsrv_utility extends CI_DB_utility {
+
+ /**
+ * List databases
+ *
+ * @access private
+ * @return bool
+ */
+ function _list_databases()
+ {
+ return "EXEC sp_helpdb"; // Can also be: EXEC sp_databases
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Optimize table query
+ *
+ * Generates a platform-specific query so that a table can be optimized
+ *
+ * @access private
+ * @param string the table name
+ * @return object
+ */
+ function _optimize_table($table)
+ {
+ return FALSE; // Is this supported in MS SQL?
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Repair table query
+ *
+ * Generates a platform-specific query so that a table can be repaired
+ *
+ * @access private
+ * @param string the table name
+ * @return object
+ */
+ function _repair_table($table)
+ {
+ return FALSE; // Is this supported in MS SQL?
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * MSSQL Export
+ *
+ * @access private
+ * @param array Preferences
+ * @return mixed
+ */
+ function _backup($params = array())
+ {
+ // Currently unsupported
+ return $this->db->display_error('db_unsuported_feature');
+ }
+
+}
+
+/* End of file mssql_utility.php */
+/* Location: ./system/database/drivers/mssql/mssql_utility.php */
\ No newline at end of file
diff --git a/system/helpers/form_helper.php b/system/helpers/form_helper.php
index 5720a06..47f93e7 100644
--- a/system/helpers/form_helper.php
+++ b/system/helpers/form_helper.php
@@ -249,7 +249,7 @@
{
function form_textarea($data = '', $value = '', $extra = '')
{
- $defaults = array('name' => (( ! is_array($data)) ? $data : ''), 'cols' => '90', 'rows' => '12');
+ $defaults = array('name' => (( ! is_array($data)) ? $data : ''), 'cols' => '40', 'rows' => '10');
if ( ! is_array($data) OR ! isset($data['value']))
{
diff --git a/system/helpers/html_helper.php b/system/helpers/html_helper.php
index 080f622..b64b606 100644
--- a/system/helpers/html_helper.php
+++ b/system/helpers/html_helper.php
@@ -124,6 +124,10 @@
}
$attributes = $atts;
}
+ elseif (is_string($attributes) AND strlen($attributes) > 0)
+ {
+ $attributes = ' '. $attributes;
+ }
// Write the opening list tag
$out .= "<".$type.$attributes.">\n";
diff --git a/system/helpers/inflector_helper.php b/system/helpers/inflector_helper.php
index c7c113b..7b99bc5 100644
--- a/system/helpers/inflector_helper.php
+++ b/system/helpers/inflector_helper.php
@@ -41,30 +41,48 @@
{
function singular($str)
{
- $str = trim($str);
- $end = substr($str, -3);
-
- $str = preg_replace('/(.*)?([s|c]h)es/i','$1$2',$str);
-
- if (strtolower($end) == 'ies')
- {
- $str = substr($str, 0, strlen($str)-3).(preg_match('/[a-z]/',$end) ? 'y' : 'Y');
- }
- elseif (strtolower($end) == 'ses')
- {
- $str = substr($str, 0, strlen($str)-2);
- }
- else
- {
- $end = strtolower(substr($str, -1));
+ $result = strval($str);
- if ($end == 's')
+ $singular_rules = array(
+ '/(matr)ices$/' => '\1ix',
+ '/(vert|ind)ices$/' => '\1ex',
+ '/^(ox)en/' => '\1',
+ '/(alias)es$/' => '\1',
+ '/([octop|vir])i$/' => '\1us',
+ '/(cris|ax|test)es$/' => '\1is',
+ '/(shoe)s$/' => '\1',
+ '/(o)es$/' => '\1',
+ '/(bus|campus)es$/' => '\1',
+ '/([m|l])ice$/' => '\1ouse',
+ '/(x|ch|ss|sh)es$/' => '\1',
+ '/(m)ovies$/' => '\1\2ovie',
+ '/(s)eries$/' => '\1\2eries',
+ '/([^aeiouy]|qu)ies$/' => '\1y',
+ '/([lr])ves$/' => '\1f',
+ '/(tive)s$/' => '\1',
+ '/(hive)s$/' => '\1',
+ '/([^f])ves$/' => '\1fe',
+ '/(^analy)ses$/' => '\1sis',
+ '/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/' => '\1\2sis',
+ '/([ti])a$/' => '\1um',
+ '/(p)eople$/' => '\1\2erson',
+ '/(m)en$/' => '\1an',
+ '/(s)tatuses$/' => '\1\2tatus',
+ '/(c)hildren$/' => '\1\2hild',
+ '/(n)ews$/' => '\1\2ews',
+ '/([^u])s$/' => '\1',
+ );
+
+ foreach ($singular_rules as $rule => $replacement)
+ {
+ if (preg_match($rule, $result))
{
- $str = substr($str, 0, strlen($str)-1);
+ $result = preg_replace($rule, $replacement, $result);
+ break;
}
}
- return $str;
+ return $result;
}
}
@@ -83,40 +101,41 @@
if ( ! function_exists('plural'))
{
function plural($str, $force = FALSE)
- {
- $str = trim($str);
- $end = substr($str, -1);
+ {
+ $result = strval($str);
+
+ $plural_rules = array(
+ '/^(ox)$/' => '\1\2en', // ox
+ '/([m|l])ouse$/' => '\1ice', // mouse, louse
+ '/(matr|vert|ind)ix|ex$/' => '\1ices', // matrix, vertex, index
+ '/(x|ch|ss|sh)$/' => '\1es', // search, switch, fix, box, process, address
+ '/([^aeiouy]|qu)y$/' => '\1ies', // query, ability, agency
+ '/(hive)$/' => '\1s', // archive, hive
+ '/(?:([^f])fe|([lr])f)$/' => '\1\2ves', // half, safe, wife
+ '/sis$/' => 'ses', // basis, diagnosis
+ '/([ti])um$/' => '\1a', // datum, medium
+ '/(p)erson$/' => '\1eople', // person, salesperson
+ '/(m)an$/' => '\1en', // man, woman, spokesman
+ '/(c)hild$/' => '\1hildren', // child
+ '/(buffal|tomat)o$/' => '\1\2oes', // buffalo, tomato
+ '/(bu|campu)s$/' => '\1\2ses', // bus, campus
+ '/(alias|status|virus)/' => '\1es', // alias
+ '/(octop)us$/' => '\1i', // octopus
+ '/(ax|cris|test)is$/' => '\1es', // axis, crisis
+ '/s$/' => 's', // no change (compatibility)
+ '/$/' => 's',
+ );
- if (preg_match('/y/i',$end))
+ foreach ($plural_rules as $rule => $replacement)
{
- // Y preceded by vowel => regular plural
- $vowels = array('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U');
- $str = in_array(substr($str, -2, 1), $vowels) ? $str.'s' : substr($str, 0, -1).'ies';
- }
- elseif (preg_match('/h/i',$end))
- {
- if(preg_match('/^[c|s]h$/i',substr($str, -2)))
+ if (preg_match($rule, $result))
{
- $str .= 'es';
+ $result = preg_replace($rule, $replacement, $result);
+ break;
}
- else
- {
- $str .= 's';
- }
- }
- elseif (preg_match('/s/i',$end))
- {
- if ($force == TRUE)
- {
- $str .= 'es';
- }
- }
- else
- {
- $str .= 's';
}
- return $str;
+ return $result;
}
}
diff --git a/system/helpers/string_helper.php b/system/helpers/string_helper.php
index 7765bba..9fa69f4 100644
--- a/system/helpers/string_helper.php
+++ b/system/helpers/string_helper.php
@@ -243,6 +243,23 @@
// ------------------------------------------------------------------------
/**
+ * Add's _1 to a string or increment the ending number to allow _2, _3, etc
+ *
+ * @param string $str required
+ * @param string $separator What should the duplicate number be appended with
+ * @param string $first Which number should be used for the first dupe increment
+ * @return string
+ */
+function increment_string($str, $separator = '_', $first = 1)
+{
+ preg_match('/(.+)'.$separator.'([0-9]+)$/', $str, $match);
+
+ return isset($match[2]) ? $match[1].$separator.($match[2] + 1) : $str.$separator.$first;
+}
+
+// ------------------------------------------------------------------------
+
+/**
* Alternator
*
* Allows strings to be alternated. See docs...
diff --git a/system/helpers/url_helper.php b/system/helpers/url_helper.php
index d0516ce..9f4b852 100644
--- a/system/helpers/url_helper.php
+++ b/system/helpers/url_helper.php
@@ -50,18 +50,21 @@
/**
* Base URL
- *
- * Returns the "base_url" item from your config file
+ *
+ * Create a local URL based on your basepath.
+ * Segments can be passed in as a string or an array, same as site_url
+ * or a URL to a file can be passed in, e.g. to an image file.
*
* @access public
+ * @param string
* @return string
*/
if ( ! function_exists('base_url'))
{
- function base_url()
+ function base_url($uri = '')
{
$CI =& get_instance();
- return $CI->config->slash_item('base_url');
+ return $CI->config->base_url($uri);
}
}
diff --git a/system/libraries/Driver.php b/system/libraries/Driver.php
index c32f2c1..9881c1e 100644
--- a/system/libraries/Driver.php
+++ b/system/libraries/Driver.php
@@ -54,7 +54,7 @@
if ( ! class_exists($child_class))
{
// check application path first
- foreach (array(APPPATH, BASEPATH) as $path)
+ foreach (get_instance()->load->get_package_paths(TRUE) as $path)
{
// loves me some nesting!
foreach (array(ucfirst($driver_name), $driver_name) as $class)
@@ -226,4 +226,4 @@
// END CI_Driver CLASS
/* End of file Driver.php */
-/* Location: ./system/libraries/Driver.php */
+/* Location: ./system/libraries/Driver.php */
\ No newline at end of file
diff --git a/system/libraries/Email.php b/system/libraries/Email.php
index 9b3bc75..fb9a750 100644
--- a/system/libraries/Email.php
+++ b/system/libraries/Email.php
@@ -395,7 +395,7 @@
public function attach($filename, $disposition = 'attachment')
{
$this->_attach_name[] = $filename;
- $this->_attach_type[] = $this->_mime_types(next(explode('.', basename($filename))));
+ $this->_attach_type[] = $this->_mime_types(pathinfo($filename, PATHINFO_EXTENSION));
$this->_attach_disp[] = $disposition; // Can also be 'inline' Not sure if it matters
return $this;
}
diff --git a/system/libraries/Form_validation.php b/system/libraries/Form_validation.php
index fd95d76..a34809e 100644
--- a/system/libraries/Form_validation.php
+++ b/system/libraries/Form_validation.php
@@ -1378,4 +1378,4 @@
// END Form Validation Class
/* End of file Form_validation.php */
-/* Location: ./system/libraries/Form_validation.php */
\ No newline at end of file
+/* Location: ./system/libraries/Form_validation.php */
diff --git a/system/libraries/User_agent.php b/system/libraries/User_agent.php
index 016102a..0b77a7d 100644
--- a/system/libraries/User_agent.php
+++ b/system/libraries/User_agent.php
@@ -142,7 +142,7 @@
{
$this->_set_platform();
- foreach (array('_set_browser', '_set_robot', '_set_mobile') as $function)
+ foreach (array('_set_robot', '_set_browser', '_set_mobile') as $function)
{
if ($this->$function() === TRUE)
{
diff --git a/user_guide/changelog.html b/user_guide/changelog.html
index e0cefc3..20b1c4e 100644
--- a/user_guide/changelog.html
+++ b/user_guide/changelog.html
@@ -59,6 +59,44 @@
<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.1.0</h2>
+<p>Release Date: Not Released</p>
+
+<ul>
+ <li>General Changes
+ <ul>
+ <li class="reactor">Callback validation rules can now accept parameters like any other validation rule.</li>
+ </ul>
+ </li>
+ <li>Helpers
+ <ul>
+ <li class="reactor">Added <samp>increment_string()</samp> to <a href="helpers/string_helper.html">String Helper</a> to turn "foo" into "foo-1" or "foo-1" into "foo-2".</li>
+ </ul>
+ </li>
+ <li>Database
+ <ul>
+ <li class="reactor">Added a <a href="http://www.cubrid.org/" target="_blank">CUBRID</a> driver to the <a href="libraries/database.html">Database Driver</a>. Thanks to the CUBRID team for supplying this patch.</li>
+ <li class="reactor">Typecast limit and offset in the <a href="database/queries.html">Database Driver</a> to integers to avoid possible injection.</li>
+ </ul>
+ </li>
+ <li>Libraries
+ <ul>
+ <li class="reactor">Changed <kbd>$this->cart->insert()</kbd> in the <a href="libraries/cart.html">Cart Library</a> to return the Row ID if a single item was inserted successfully.</li>
+ <li class="reactor">Added support to set an optional parameter in your callback rules of validation using the <a href="libraries/form_validation.html">Form Validation Library</a>.</li>
+ <li class="reactor">Added a <a href="libraries/migration.html">Migration Library</a> to assist with applying incremental updates to your database schema.</li>
+ <li class="reactor">Driver children can be located in any package path.</li>
+ </ul>
+ </li>
+</ul>
+
+<h3>Bug fixes for 2.1.0</h3>
+<ul>
+ <li class="reactor">Fixed #378 Robots identified as regular browsers by the User Agent class.</li>
+ <li class="reactor">If a config class was loaded first then a library with the same name is loaded, the config would be ignored.</li>
+ <li class="reactor">Fixed a bug (Reactor #19) where 1) the 404_override route was being ignored in some cases, and 2) auto-loaded libraries were not available to the 404_override controller when a controller existed but the requested method did not.</li>
+ <li class="rector">Fixed a bug (Reactor #89) where MySQL export would fail if the table had hyphens or other non alphanumeric/underscore characters.</li>
+</ul>
+
<h2>Version 2.0.3</h2>
<p>Release Date: Not Released</p>
@@ -74,14 +112,15 @@
<li>Added Session Class userdata to the output profiler. Additionally, added a show/hide toggle on HTTP Headers, Session Data and Config Variables.</li>
<li>Removed internal usage of the <samp>EXT</samp> constant.</li>
<li>Visual updates to the welcome_message view file and default error templates. Thanks to <a href="https://bitbucket.org/danijelb">danijelb</a> for the pull request.</li>
- <li>Added <samp>insert_batch()</samp> function to the PostgreSQL database driver. Thanks to epallerols for the patch.</li>
- <li class="reactor">Callback validation rules can now accept parameters like any other validation rule.</li>
+ <li class="reactor">Added <samp>insert_batch()</samp> function to the PostgreSQL database driver. Thanks to epallerols for the patch.</li>
+ <li class="reactor">Added "application/x-csv" to mimes.php.</li>
</ul>
</li>
<li>Helpers
<ul>
<li>Added an optional third parameter to <samp>heading()</samp> which allows adding html attributes to the rendered heading tag.</li>
<li class="reactor"><kbd>form_open()</kbd> now only adds a hidden (Cross-site Reference Forgery) protection field when the form's action is internal and is set to the post method. (Reactor #165)</li>
+ <li class="reactor">Re-worked <samp>plural()</samp> and <samp>singular()</samp> functions in the <a href="helpers/inflector_helper.html">Inflector helper</a> to support considerably more words.</li>
</ul>
</li>
<li>Libraries
@@ -91,7 +130,7 @@
<li class="reactor">Added <kbd>$this->db->set_dbprefix()</kbd> to the <a href="database/queries.html">Database Driver</a>.</li>
<li class="reactor">Changed <kbd>$this->cart->insert()</kbd> in the <a href="libraries/cart.html">Cart Library</a> to return the Row ID if a single item was inserted successfully.</li>
<li class="reactor">Added <kbd>$this->load->get_var()</kbd> to the <a href="libraries/loader.html">Loader library</a> to retrieve global vars set with <kbd>$this->load->view()</kbd> and <kbd>$this->load->vars()</kbd>.</li>
- <li>Changed <kbd>$this->db->having()</kbd> to insert quotes using escape() rather than escape_str().</li>
+ <li class="reactor">Changed <kbd>$this->db->having()</kbd> to insert quotes using escape() rather than escape_str().</li>
</ul>
</li>
</ul>
@@ -104,9 +143,12 @@
<li>Fixed a bug (Reactor #231) where Sessions Library database table example SQL did not contain an index on last_activity. See <a href="installation/upgrade_203.html">Upgrade Notes</a>.</li>
<li>Fixed a bug (Reactor #229) where the Sessions Library example SQL in the documentation contained incorrect SQL.</li>
<li>Fixed a bug (Core #340) where when passing in the second parameter to $this->db->select(), column names in subsequent queries would not be properly escaped.</li>
+ <li class="reactor">Fixed issue #199 - Attributes passed as string does not include a space between it and the opening tag.</li>
<li class="reactor">Fixed a bug where the method <kbd>$this->cart->total_items()</kbd> from <a href="libraries/cart.html">Cart Library</a> now returns the sum of the quantity of all items in the cart instead of your total count.</li>
- <li>Fixed a bug where not setting 'null' when adding fields in db_forge for mysql and mysqli drivers would default to NULL instead of NOT NULL as the docs suggest.</li>
- <li class="reactor">Fixed a bug (Reactor #19) where 1) the 404_override route was being ignored in some cases, and 2) auto-loaded libraries were not available to the 404_override controller when a controller existed but the requested method did not.</li>
+ <li class="reactor">Fixed a bug where not setting 'null' when adding fields in db_forge for MySQL and MySQLi drivers would default to NULL instead of NOT NULL as the docs suggest.</li>
+ <li class="reactor">Fixed a bug where using <kbd>$this->db->select_max()</kdb>, <kbd>$this->db->select_min()</kdb>, etc could throw notices. Thanks to w43l for the patch.</li>
+ <li class="reactor">Replace checks for STDIN with php_sapi_name() == 'cli' which on the whole is more reliable. This should get parameters in crontab working.</li>
+ <li>Fixed a bug where <a href="libraries/email.html">Email library</a> attachments with a "." in the name would using invalid MIME-types.</li>
</ul>
<h2>Version 2.0.2</h2>
diff --git a/user_guide/general/cli.html b/user_guide/general/cli.html
index 962954b..ed6fc85 100644
--- a/user_guide/general/cli.html
+++ b/user_guide/general/cli.html
@@ -72,7 +72,7 @@
<a name="what"></a>
<h2>What is the CLI?</h2>
-<p><dfn>The command-line interface is a text-based method of interacting with computers that looks like what most people remember as DOS.</dfn></p>
+<p><dfn>The command-line interface is a text-based method of interacting with computers.</dfn> For more information, check the <a href="http://en.wikipedia.org/wiki/Command-line_interface">Wikipedia article</a>.</p>
<a name="why"></a>
diff --git a/user_guide/helpers/string_helper.html b/user_guide/helpers/string_helper.html
index 169ee4e..7c1d30c 100644
--- a/user_guide/helpers/string_helper.html
+++ b/user_guide/helpers/string_helper.html
@@ -90,6 +90,17 @@
<code>echo random_string('alnum', 16);</code>
+<h2>increment_string()</h2>
+
+<p>Increments a string by appending a number to it or increasing the number. Useful for creating "copies" or a file or duplicating database content which has unique titles or slugs.</p>
+
+<p>Usage example:</p>
+
+<code>echo increment_string('file', '_'); // "file_1"<br/>
+echo increment_string('file', '-', 2); // "file-2"<br/>
+echo increment_string('file-4'); // "file-5"<br/></code>
+
+
<h2>alternator()</h2>
<p>Allows two or more items to be alternated between, when cycling through a loop. Example:</p>
diff --git a/user_guide/helpers/url_helper.html b/user_guide/helpers/url_helper.html
index d20f1b1..ac9d0a6 100644
--- a/user_guide/helpers/url_helper.html
+++ b/user_guide/helpers/url_helper.html
@@ -70,7 +70,7 @@
<h2>site_url()</h2>
<p>Returns your site URL, as specified in your config file. The index.php file (or whatever you have set as your
-site <dfn>index_page</dfn> in your config file) will be added to the URL, as will any URI segments you pass to the function.</p>
+site <dfn>index_page</dfn> in your config file) will be added to the URL, as will any URI segments you pass to the function, and the <dfn>url_suffix</dfn> as set in your config file.</p>
<p>You are encouraged to use this function any time you need to generate a local URL so that your pages become more portable
in the event your URL changes.</p>
@@ -93,6 +93,20 @@
<p>Returns your site base URL, as specified in your config file. Example:</p>
<code>echo base_url();</code>
+<p>This function returns the same thing as site_url, without the <dfn>index_page</dfn> or <dfn>url_suffix</dfn> being appended.</p>
+
+<p>Also like site_url, you can supply segments as a string or an array. Here is a string example:</p>
+
+<code>echo base_url("blog/post/123");</code>
+
+<p>The above example would return something like: http://example.com/blog/post/123</p>
+
+<p>This is useful because unlike site_url(), you can supply a string to a file, such as an image or stylesheet. For example:</p>
+
+<code>echo base_url("images/icons/edit.png");</code>
+
+<p>This would give you something like: http://example.com/images/icons/edit.png</p>
+
<h2>current_url()</h2>
<p>Returns the full URL (including segments) of the page being currently viewed.</p>
diff --git a/user_guide/libraries/cart.html b/user_guide/libraries/cart.html
index 81b43e3..2e2beed 100644
--- a/user_guide/libraries/cart.html
+++ b/user_guide/libraries/cart.html
@@ -109,7 +109,8 @@
<li><strong>options</strong> - Any additional attributes that are needed to identify the product. These must be passed via an array.
</ul>
-<p>In addition to the five indexes above, there are two reserved words: <dfn>rowid</dfn> and <dfn>subtotal</dfn>. These are used internally by the Cart class, so please do NOT use those words as index names when inserting data into the cart.</p>
+<p>In addition to the five indexes above, there are two reserved words: <dfn>rowid</dfn> and <dfn>subtotal</dfn>. These are used internally by the Cart class, so
+please do NOT use those words as index names when inserting data into the cart.</p>
<p>Your array may contain additional data. Anything you include in your array will be stored in the session. However, it is best to standardize your data among all your products in order to make displaying the information in a table easier.</p>
@@ -264,7 +265,7 @@
</code>
-<p><strong>What is a Row ID?</strong> The <kbd>row ID</kbd> is a unique identifier that is generated by the cart code when an item is added to the cart. The reason a
+<p><strong>What is a Row ID?</strong> The <kbd>row ID</kbd> is a unique identifier that is generated by the cart code when an item is added to the cart. The reason a
unique ID is created is so that identical products with different options can be managed by the cart.</p>
<p>For example, let's say someone buys two identical t-shirts (same product ID), but in different sizes. The product ID (and other attributes) will be
diff --git a/user_guide/libraries/config.html b/user_guide/libraries/config.html
index 2433ec4..be75801 100644
--- a/user_guide/libraries/config.html
+++ b/user_guide/libraries/config.html
@@ -194,6 +194,11 @@
<h2>$this->config->site_url();</h2>
<p>This function retrieves the URL to your site, along with the "index" value you've specified in the config file.</p>
+<h2>$this->config->base_url();</h2>
+<p>This function retrieves the URL to your site, plus an optional path such as to a stylesheet or image.</p>
+
+<p>The two functions above are normally accessed via the corresponding functions in the <a href="../helpers/url_helper.html">URL Helper.</a></p>
+
<h2>$this->config->system_url();</h2>
<p>This function retrieves the URL to your <dfn>system folder</dfn>.</p>
diff --git a/user_guide/libraries/form_validation.html b/user_guide/libraries/form_validation.html
index bba8f50..da2f5e5 100644
--- a/user_guide/libraries/form_validation.html
+++ b/user_guide/libraries/form_validation.html
@@ -508,11 +508,9 @@
<code>$this->form_validation->set_rules('username', 'Username', '<kbd>callback_username_check</kbd>');</code>
-
<p>Then add a new function called <dfn>username_check</dfn> to your controller. Here's how your controller should now look:</p>
-
-<textarea class="textarea" style="width:100%" cols="50" rows="44"><?php
+<textarea class="textarea" style="width:100%" cols="50" rows="40"><?php
class Form extends CI_Controller {
@@ -556,14 +554,13 @@
<p><dfn>Reload your form and submit it with the word "test" as the username. You can see that the form field data was passed to your
callback function for you to process.</dfn></p>
-<p><strong>To invoke a callback just put the function name in a rule, with "callback_" as the rule prefix.</strong></p>
+<p>To invoke a callback just put the function name in a rule, with "callback_" as the rule <strong>prefix</strong>. If you need
+to receive an extra parameter in your callback function, just add it normally after the function name between square brackets,
+as in: "callback_foo<strong>[bar]</strong>", then it will be passed as the second argument of your callback function.</p>
-<p>You can also process the form data that is passed to your callback and return it. If your callback returns anything other than a boolean TRUE/FALSE
+<p><strong>Note:</strong> You can also process the form data that is passed to your callback and return it. If your callback returns anything other than a boolean TRUE/FALSE
it is assumed that the data is your newly processed form data.</p>
-
-
-
<a name="settingerrors"></a>
<h2>Setting Error Messages</h2>