a grand renaming so that the most significant portion of the name comes first

This commit is contained in:
Dan Buch
2012-03-03 21:45:20 -05:00
parent c8b8078175
commit f4f448926d
1300 changed files with 0 additions and 234 deletions

View File

@@ -0,0 +1,675 @@
<?php
/**
* Caching for CakePHP.
*
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs
* @since CakePHP(tm) v 1.2.0.4933
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Caching for CakePHP.
*
* @package cake
* @subpackage cake.cake.libs
*/
class Cache {
/**
* Cache configuration stack
* Keeps the permanent/default settings for each cache engine.
* These settings are used to reset the engines after temporary modification.
*
* @var array
* @access private
*/
var $__config = array();
/**
* Holds name of the current configuration name being used.
*
* @var array
* @access private
*/
var $__name = 'default';
/**
* Whether to reset the settings with the next call to Cache::set();
*
* @var array
* @access private
*/
var $__reset = false;
/**
* Engine instances keyed by configuration name.
*
* @var array
*/
var $_engines = array();
/**
* Returns a singleton instance
*
* @return object
* @access public
* @static
*/
function &getInstance() {
static $instance = array();
if (!$instance) {
$instance[0] =& new Cache();
}
return $instance[0];
}
/**
* Set the cache configuration to use. config() can
* both create new configurations, return the settings for already configured
* configurations. It also sets the 'default' configuration to use for subsequent
* operations.
*
* To create a new configuration:
*
* `Cache::config('my_config', array('engine' => 'File', 'path' => TMP));`
*
* To get the settings for a configuration, and set it as the currently selected configuration
*
* `Cache::config('default');`
*
* @see app/config/core.php for configuration settings
* @param string $name Name of the configuration
* @param array $settings Optional associative array of settings passed to the engine
* @return array(engine, settings) on success, false on failure
* @access public
* @static
*/
function config($name = null, $settings = array()) {
$self =& Cache::getInstance();
if (is_array($name)) {
$settings = $name;
}
if ($name === null || !is_string($name)) {
$name = $self->__name;
}
$current = array();
if (isset($self->__config[$name])) {
$current = $self->__config[$name];
}
if (!empty($settings)) {
$self->__config[$name] = array_merge($current, $settings);
}
if (empty($self->__config[$name]['engine'])) {
return false;
}
$engine = $self->__config[$name]['engine'];
$self->__name = $name;
if (!isset($self->_engines[$name])) {
$self->_buildEngine($name);
$settings = $self->__config[$name] = $self->settings($name);
} elseif ($settings = $self->set($self->__config[$name])) {
$self->__config[$name] = $settings;
}
return compact('engine', 'settings');
}
/**
* Finds and builds the instance of the required engine class.
*
* @param string $name Name of the config array that needs an engine instance built
* @return void
* @access protected
*/
function _buildEngine($name) {
$config = $this->__config[$name];
list($plugin, $class) = pluginSplit($config['engine']);
$cacheClass = $class . 'Engine';
if (!class_exists($cacheClass) && $this->__loadEngine($class, $plugin) === false) {
return false;
}
$cacheClass = $class . 'Engine';
$this->_engines[$name] =& new $cacheClass();
if ($this->_engines[$name]->init($config)) {
if (time() % $this->_engines[$name]->settings['probability'] === 0) {
$this->_engines[$name]->gc();
}
return true;
}
return false;
}
/**
* Returns an array containing the currently configured Cache settings.
*
* @return array Array of configured Cache config names.
*/
function configured() {
$self =& Cache::getInstance();
return array_keys($self->__config);
}
/**
* Drops a cache engine. Deletes the cache configuration information
* If the deleted configuration is the last configuration using an certain engine,
* the Engine instance is also unset.
*
* @param string $name A currently configured cache config you wish to remove.
* @return boolen success of the removal, returns false when the config does not exist.
*/
function drop($name) {
$self =& Cache::getInstance();
if (!isset($self->__config[$name])) {
return false;
}
unset($self->__config[$name]);
unset($self->_engines[$name]);
return true;
}
/**
* Tries to find and include a file for a cache engine and returns object instance
*
* @param $name Name of the engine (without 'Engine')
* @return mixed $engine object or null
* @access private
*/
function __loadEngine($name, $plugin = null) {
if ($plugin) {
return App::import('Lib', $plugin . '.cache' . DS . $name, false);
} else {
$core = App::core();
$path = $core['libs'][0] . 'cache' . DS . strtolower($name) . '.php';
if (file_exists($path)) {
require $path;
return true;
}
return App::import('Lib', 'cache' . DS . $name, false);
}
}
/**
* Temporarily change settings to current config options. if no params are passed, resets settings if needed
* Cache::write() will reset the configuration changes made
*
* @param mixed $settings Optional string for simple name-value pair or array
* @param string $value Optional for a simple name-value pair
* @return array Array of settings.
* @access public
* @static
*/
function set($settings = array(), $value = null) {
$self =& Cache::getInstance();
if (!isset($self->__config[$self->__name]) || !isset($self->_engines[$self->__name])) {
return false;
}
$name = $self->__name;
if (!empty($settings)) {
$self->__reset = true;
}
if ($self->__reset === true) {
if (empty($settings)) {
$self->__reset = false;
$settings = $self->__config[$name];
} else {
if (is_string($settings) && $value !== null) {
$settings = array($settings => $value);
}
$settings = array_merge($self->__config[$name], $settings);
if (isset($settings['duration']) && !is_numeric($settings['duration'])) {
$settings['duration'] = strtotime($settings['duration']) - time();
}
}
$self->_engines[$name]->settings = $settings;
}
return $self->settings($name);
}
/**
* Garbage collection
*
* Permanently remove all expired and deleted data
*
* @return void
* @access public
* @static
*/
function gc() {
$self =& Cache::getInstance();
$self->_engines[$self->__name]->gc();
}
/**
* Write data for key into cache. Will automatically use the currently
* active cache configuration. To set the currently active configuration use
* Cache::config()
*
* ### Usage:
*
* Writing to the active cache config:
*
* `Cache::write('cached_data', $data);`
*
* Writing to a specific cache config:
*
* `Cache::write('cached_data', $data, 'long_term');`
*
* @param string $key Identifier for the data
* @param mixed $value Data to be cached - anything except a resource
* @param string $config Optional string configuration name to write to.
* @return boolean True if the data was successfully cached, false on failure
* @access public
* @static
*/
function write($key, $value, $config = null) {
$self =& Cache::getInstance();
if (!$config) {
$config = $self->__name;
}
$settings = $self->settings($config);
if (empty($settings)) {
return null;
}
if (!$self->isInitialized($config)) {
return false;
}
$key = $self->_engines[$config]->key($key);
if (!$key || is_resource($value)) {
return false;
}
$success = $self->_engines[$config]->write($settings['prefix'] . $key, $value, $settings['duration']);
$self->set();
return $success;
}
/**
* Read a key from the cache. Will automatically use the currently
* active cache configuration. To set the currently active configuration use
* Cache::config()
*
* ### Usage:
*
* Reading from the active cache configuration.
*
* `Cache::read('my_data');`
*
* Reading from a specific cache configuration.
*
* `Cache::read('my_data', 'long_term');`
*
* @param string $key Identifier for the data
* @param string $config optional name of the configuration to use.
* @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
* @access public
* @static
*/
function read($key, $config = null) {
$self =& Cache::getInstance();
if (!$config) {
$config = $self->__name;
}
$settings = $self->settings($config);
if (empty($settings)) {
return null;
}
if (!$self->isInitialized($config)) {
return false;
}
$key = $self->_engines[$config]->key($key);
if (!$key) {
return false;
}
$success = $self->_engines[$config]->read($settings['prefix'] . $key);
if ($config !== null && $config !== $self->__name) {
$self->set();
}
return $success;
}
/**
* Increment a number under the key and return incremented value.
*
* @param string $key Identifier for the data
* @param integer $offset How much to add
* @param string $config Optional string configuration name. If not specified the current
* default config will be used.
* @return mixed new value, or false if the data doesn't exist, is not integer,
* or if there was an error fetching it.
* @access public
*/
function increment($key, $offset = 1, $config = null) {
$self =& Cache::getInstance();
if (!$config) {
$config = $self->__name;
}
$settings = $self->settings($config);
if (empty($settings)) {
return null;
}
if (!$self->isInitialized($config)) {
return false;
}
$key = $self->_engines[$config]->key($key);
if (!$key || !is_integer($offset) || $offset < 0) {
return false;
}
$success = $self->_engines[$config]->increment($settings['prefix'] . $key, $offset);
$self->set();
return $success;
}
/**
* Decrement a number under the key and return decremented value.
*
* @param string $key Identifier for the data
* @param integer $offset How much to substract
* @param string $config Optional string configuration name, if not specified the current
* default config will be used.
* @return mixed new value, or false if the data doesn't exist, is not integer,
* or if there was an error fetching it
* @access public
*/
function decrement($key, $offset = 1, $config = null) {
$self =& Cache::getInstance();
if (!$config) {
$config = $self->__name;
}
$settings = $self->settings($config);
if (empty($settings)) {
return null;
}
if (!$self->isInitialized($config)) {
return false;
}
$key = $self->_engines[$config]->key($key);
if (!$key || !is_integer($offset) || $offset < 0) {
return false;
}
$success = $self->_engines[$config]->decrement($settings['prefix'] . $key, $offset);
$self->set();
return $success;
}
/**
* Delete a key from the cache. Will automatically use the currently
* active cache configuration. To set the currently active configuration use
* Cache::config()
*
* ### Usage:
*
* Deleting from the active cache configuration.
*
* `Cache::delete('my_data');`
*
* Deleting from a specific cache configuration.
*
* `Cache::delete('my_data', 'long_term');`
*
* @param string $key Identifier for the data
* @param string $config name of the configuration to use
* @return boolean True if the value was succesfully deleted, false if it didn't exist or couldn't be removed
* @access public
* @static
*/
function delete($key, $config = null) {
$self =& Cache::getInstance();
if (!$config) {
$config = $self->__name;
}
$settings = $self->settings($config);
if (empty($settings)) {
return null;
}
if (!$self->isInitialized($config)) {
return false;
}
$key = $self->_engines[$config]->key($key);
if (!$key) {
return false;
}
$success = $self->_engines[$config]->delete($settings['prefix'] . $key);
$self->set();
return $success;
}
/**
* Delete all keys from the cache.
*
* @param boolean $check if true will check expiration, otherwise delete all
* @param string $config name of the configuration to use
* @return boolean True if the cache was succesfully cleared, false otherwise
* @access public
* @static
*/
function clear($check = false, $config = null) {
$self =& Cache::getInstance();
if (!$config) {
$config = $self->__name;
}
$settings = $self->settings($config);
if (empty($settings)) {
return null;
}
if (!$self->isInitialized($config)) {
return false;
}
$success = $self->_engines[$config]->clear($check);
$self->set();
return $success;
}
/**
* Check if Cache has initialized a working config for the given name.
*
* @param string $engine Name of the engine
* @param string $config Name of the configuration setting
* @return bool Whether or not the config name has been initialized.
* @access public
* @static
*/
function isInitialized($name = null) {
if (Configure::read('Cache.disable')) {
return false;
}
$self =& Cache::getInstance();
if (!$name && isset($self->__config[$self->__name])) {
$name = $self->__name;
}
return isset($self->_engines[$name]);
}
/**
* Return the settings for current cache engine. If no name is supplied the settings
* for the 'active default' configuration will be returned. To set the 'active default'
* configuration use `Cache::config()`
*
* @param string $engine Name of the configuration to get settings for.
* @return array list of settings for this engine
* @see Cache::config()
* @access public
* @static
*/
function settings($name = null) {
$self =& Cache::getInstance();
if (!$name && isset($self->__config[$self->__name])) {
$name = $self->__name;
}
if (!empty($self->_engines[$name])) {
return $self->_engines[$name]->settings();
}
return array();
}
}
/**
* Storage engine for CakePHP caching
*
* @package cake
* @subpackage cake.cake.libs
*/
class CacheEngine {
/**
* Settings of current engine instance
*
* @var int
* @access public
*/
var $settings = array();
/**
* Initialize the cache engine
*
* Called automatically by the cache frontend
*
* @param array $params Associative array of parameters for the engine
* @return boolean True if the engine has been succesfully initialized, false if not
* @access public
*/
function init($settings = array()) {
$this->settings = array_merge(
array('prefix' => 'cake_', 'duration'=> 3600, 'probability'=> 100),
$this->settings,
$settings
);
if (!is_numeric($this->settings['duration'])) {
$this->settings['duration'] = strtotime($this->settings['duration']) - time();
}
return true;
}
/**
* Garbage collection
*
* Permanently remove all expired and deleted data
*
* @access public
*/
function gc() {
}
/**
* Write value for a key into cache
*
* @param string $key Identifier for the data
* @param mixed $value Data to be cached
* @param mixed $duration How long to cache the data, in seconds
* @return boolean True if the data was succesfully cached, false on failure
* @access public
*/
function write($key, &$value, $duration) {
trigger_error(sprintf(__('Method write() not implemented in %s', true), get_class($this)), E_USER_ERROR);
}
/**
* Read a key from the cache
*
* @param string $key Identifier for the data
* @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
* @access public
*/
function read($key) {
trigger_error(sprintf(__('Method read() not implemented in %s', true), get_class($this)), E_USER_ERROR);
}
/**
* Increment a number under the key and return incremented value
*
* @param string $key Identifier for the data
* @param integer $offset How much to add
* @return New incremented value, false otherwise
* @access public
*/
function increment($key, $offset = 1) {
trigger_error(sprintf(__('Method increment() not implemented in %s', true), get_class($this)), E_USER_ERROR);
}
/**
* Decrement a number under the key and return decremented value
*
* @param string $key Identifier for the data
* @param integer $value How much to substract
* @return New incremented value, false otherwise
* @access public
*/
function decrement($key, $offset = 1) {
trigger_error(sprintf(__('Method decrement() not implemented in %s', true), get_class($this)), E_USER_ERROR);
}
/**
* Delete a key from the cache
*
* @param string $key Identifier for the data
* @return boolean True if the value was succesfully deleted, false if it didn't exist or couldn't be removed
* @access public
*/
function delete($key) {
}
/**
* Delete all keys from the cache
*
* @param boolean $check if true will check expiration, otherwise delete all
* @return boolean True if the cache was succesfully cleared, false otherwise
* @access public
*/
function clear($check) {
}
/**
* Cache Engine settings
*
* @return array settings
* @access public
*/
function settings() {
return $this->settings;
}
/**
* Generates a safe key for use with cache engine storage engines.
*
* @param string $key the key passed over
* @return mixed string $key or false
* @access public
*/
function key($key) {
if (empty($key)) {
return false;
}
$key = Inflector::underscore(str_replace(array(DS, '/', '.'), '_', strval($key)));
return $key;
}
}

View File

@@ -0,0 +1,123 @@
<?php
/**
* APC storage engine for cache.
*
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.cache
* @since CakePHP(tm) v 1.2.0.4933
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* APC storage engine for cache
*
* @package cake
* @subpackage cake.cake.libs.cache
*/
class ApcEngine extends CacheEngine {
/**
* Initialize the Cache Engine
*
* Called automatically by the cache frontend
* To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array());
*
* @param array $setting array of setting for the engine
* @return boolean True if the engine has been successfully initialized, false if not
* @see CacheEngine::__defaults
* @access public
*/
function init($settings = array()) {
parent::init(array_merge(array('engine' => 'Apc', 'prefix' => Inflector::slug(APP_DIR) . '_'), $settings));
return function_exists('apc_cache_info');
}
/**
* Write data for key into cache
*
* @param string $key Identifier for the data
* @param mixed $value Data to be cached
* @param integer $duration How long to cache the data, in seconds
* @return boolean True if the data was succesfully cached, false on failure
* @access public
*/
function write($key, &$value, $duration) {
$expires = time() + $duration;
apc_store($key.'_expires', $expires, $duration);
return apc_store($key, $value, $duration);
}
/**
* Read a key from the cache
*
* @param string $key Identifier for the data
* @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
* @access public
*/
function read($key) {
$time = time();
$cachetime = intval(apc_fetch($key.'_expires'));
if ($cachetime < $time || ($time + $this->settings['duration']) < $cachetime) {
return false;
}
return apc_fetch($key);
}
/**
* Increments the value of an integer cached key
*
* @param string $key Identifier for the data
* @param integer $offset How much to increment
* @param integer $duration How long to cache the data, in seconds
* @return New incremented value, false otherwise
* @access public
*/
function increment($key, $offset = 1) {
return apc_inc($key, $offset);
}
/**
* Decrements the value of an integer cached key
*
* @param string $key Identifier for the data
* @param integer $offset How much to substract
* @param integer $duration How long to cache the data, in seconds
* @return New decremented value, false otherwise
* @access public
*/
function decrement($key, $offset = 1) {
return apc_dec($key, $offset);
}
/**
* Delete a key from the cache
*
* @param string $key Identifier for the data
* @return boolean True if the value was succesfully deleted, false if it didn't exist or couldn't be removed
* @access public
*/
function delete($key) {
return apc_delete($key);
}
/**
* Delete all keys from the cache
*
* @return boolean True if the cache was succesfully cleared, false otherwise
* @access public
*/
function clear() {
return apc_clear_cache('user');
}
}

View File

@@ -0,0 +1,272 @@
<?php
/**
* File Storage engine for cache
*
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.cache
* @since CakePHP(tm) v 1.2.0.4933
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
if (!class_exists('File')) {
require LIBS . 'file.php';
}
/**
* File Storage engine for cache
*
* @todo use the File and Folder classes (if it's not a too big performance hit)
* @package cake
* @subpackage cake.cake.libs.cache
*/
class FileEngine extends CacheEngine {
/**
* Instance of File class
*
* @var File
* @access protected
*/
var $_File = null;
/**
* Settings
*
* - path = absolute path to cache directory, default => CACHE
* - prefix = string prefix for filename, default => cake_
* - lock = enable file locking on write, default => false
* - serialize = serialize the data, default => true
*
* @var array
* @see CacheEngine::__defaults
* @access public
*/
var $settings = array();
/**
* True unless FileEngine::__active(); fails
*
* @var boolean
* @access protected
*/
var $_init = true;
/**
* Initialize the Cache Engine
*
* Called automatically by the cache frontend
* To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array());
*
* @param array $setting array of setting for the engine
* @return boolean True if the engine has been successfully initialized, false if not
* @access public
*/
function init($settings = array()) {
parent::init(array_merge(
array(
'engine' => 'File', 'path' => CACHE, 'prefix'=> 'cake_', 'lock'=> false,
'serialize'=> true, 'isWindows' => false
),
$settings
));
if (!isset($this->_File)) {
$this->_File =& new File($this->settings['path'] . DS . 'cake');
}
if (DIRECTORY_SEPARATOR === '\\') {
$this->settings['isWindows'] = true;
}
$path = $this->_File->Folder->cd($this->settings['path']);
if ($path) {
$this->settings['path'] = $path;
}
return $this->__active();
}
/**
* Garbage collection. Permanently remove all expired and deleted data
*
* @return boolean True if garbage collection was succesful, false on failure
* @access public
*/
function gc() {
return $this->clear(true);
}
/**
* Write data for key into cache
*
* @param string $key Identifier for the data
* @param mixed $data Data to be cached
* @param mixed $duration How long to cache the data, in seconds
* @return boolean True if the data was succesfully cached, false on failure
* @access public
*/
function write($key, &$data, $duration) {
if ($data === '' || !$this->_init) {
return false;
}
if ($this->_setKey($key) === false) {
return false;
}
$lineBreak = "\n";
if ($this->settings['isWindows']) {
$lineBreak = "\r\n";
}
if (!empty($this->settings['serialize'])) {
if ($this->settings['isWindows']) {
$data = str_replace('\\', '\\\\\\\\', serialize($data));
} else {
$data = serialize($data);
}
}
if ($this->settings['lock']) {
$this->_File->lock = true;
}
$expires = time() + $duration;
$contents = $expires . $lineBreak . $data . $lineBreak;
$success = $this->_File->write($contents);
$this->_File->close();
return $success;
}
/**
* Read a key from the cache
*
* @param string $key Identifier for the data
* @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
* @access public
*/
function read($key) {
if ($this->_setKey($key) === false || !$this->_init || !$this->_File->exists()) {
return false;
}
if ($this->settings['lock']) {
$this->_File->lock = true;
}
$time = time();
$cachetime = intval($this->_File->read(11));
if ($cachetime !== false && ($cachetime < $time || ($time + $this->settings['duration']) < $cachetime)) {
$this->_File->close();
return false;
}
$data = $this->_File->read(true);
if ($data !== '' && !empty($this->settings['serialize'])) {
if ($this->settings['isWindows']) {
$data = str_replace('\\\\\\\\', '\\', $data);
}
$data = unserialize((string)$data);
}
$this->_File->close();
return $data;
}
/**
* Delete a key from the cache
*
* @param string $key Identifier for the data
* @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed
* @access public
*/
function delete($key) {
if ($this->_setKey($key) === false || !$this->_init) {
return false;
}
return $this->_File->delete();
}
/**
* Delete all values from the cache
*
* @param boolean $check Optional - only delete expired cache items
* @return boolean True if the cache was succesfully cleared, false otherwise
* @access public
*/
function clear($check) {
if (!$this->_init) {
return false;
}
$dir = dir($this->settings['path']);
if ($check) {
$now = time();
$threshold = $now - $this->settings['duration'];
}
$prefixLength = strlen($this->settings['prefix']);
while (($entry = $dir->read()) !== false) {
if (substr($entry, 0, $prefixLength) !== $this->settings['prefix']) {
continue;
}
if ($this->_setKey($entry) === false) {
continue;
}
if ($check) {
$mtime = $this->_File->lastChange();
if ($mtime === false || $mtime > $threshold) {
continue;
}
$expires = $this->_File->read(11);
$this->_File->close();
if ($expires > $now) {
continue;
}
}
$this->_File->delete();
}
$dir->close();
return true;
}
/**
* Get absolute file for a given key
*
* @param string $key The key
* @return mixed Absolute cache file for the given key or false if erroneous
* @access private
*/
function _setKey($key) {
$this->_File->Folder->cd($this->settings['path']);
if ($key !== $this->_File->name) {
$this->_File->name = $key;
$this->_File->path = null;
}
if (!$this->_File->Folder->inPath($this->_File->pwd(), true)) {
return false;
}
}
/**
* Determine is cache directory is writable
*
* @return boolean
* @access private
*/
function __active() {
if ($this->_init && !is_writable($this->settings['path'])) {
$this->_init = false;
trigger_error(sprintf(__('%s is not writable', true), $this->settings['path']), E_USER_WARNING);
return false;
}
return true;
}
}

View File

@@ -0,0 +1,217 @@
<?php
/**
* Memcache storage engine for cache
*
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.cache
* @since CakePHP(tm) v 1.2.0.4933
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Memcache storage engine for cache. Memcache has some limitations in the amount of
* control you have over expire times far in the future. See MemcacheEngine::write() for
* more information.
*
* @package cake
* @subpackage cake.cake.libs.cache
*/
class MemcacheEngine extends CacheEngine {
/**
* Memcache wrapper.
*
* @var Memcache
* @access private
*/
var $__Memcache = null;
/**
* Settings
*
* - servers = string or array of memcache servers, default => 127.0.0.1. If an
* array MemcacheEngine will use them as a pool.
* - compress = boolean, default => false
*
* @var array
* @access public
*/
var $settings = array();
/**
* Initialize the Cache Engine
*
* Called automatically by the cache frontend
* To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array());
*
* @param array $setting array of setting for the engine
* @return boolean True if the engine has been successfully initialized, false if not
* @access public
*/
function init($settings = array()) {
if (!class_exists('Memcache')) {
return false;
}
parent::init(array_merge(array(
'engine'=> 'Memcache',
'prefix' => Inflector::slug(APP_DIR) . '_',
'servers' => array('127.0.0.1'),
'compress'=> false
), $settings)
);
if ($this->settings['compress']) {
$this->settings['compress'] = MEMCACHE_COMPRESSED;
}
if (!is_array($this->settings['servers'])) {
$this->settings['servers'] = array($this->settings['servers']);
}
if (!isset($this->__Memcache)) {
$return = false;
$this->__Memcache =& new Memcache();
foreach ($this->settings['servers'] as $server) {
list($host, $port) = $this->_parseServerString($server);
if ($this->__Memcache->addServer($host, $port)) {
$return = true;
}
}
return $return;
}
return true;
}
/**
* Parses the server address into the host/port. Handles both IPv6 and IPv4
* addresses
*
* @param string $server The server address string.
* @return array Array containing host, port
*/
function _parseServerString($server) {
if (substr($server, 0, 1) == '[') {
$position = strpos($server, ']:');
if ($position !== false) {
$position++;
}
} else {
$position = strpos($server, ':');
}
$port = 11211;
$host = $server;
if ($position !== false) {
$host = substr($server, 0, $position);
$port = substr($server, $position + 1);
}
return array($host, $port);
}
/**
* Write data for key into cache. When using memcache as your cache engine
* remember that the Memcache pecl extension does not support cache expiry times greater
* than 30 days in the future. If you wish to create cache entries that do not expire, set the duration
* to `0` in your cache configuration.
*
* @param string $key Identifier for the data
* @param mixed $value Data to be cached
* @param integer $duration How long to cache the data, in seconds
* @return boolean True if the data was succesfully cached, false on failure
* @see http://php.net/manual/en/memcache.set.php
* @access public
*/
function write($key, &$value, $duration) {
return $this->__Memcache->set($key, $value, $this->settings['compress'], $duration);
}
/**
* Read a key from the cache
*
* @param string $key Identifier for the data
* @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
* @access public
*/
function read($key) {
return $this->__Memcache->get($key);
}
/**
* Increments the value of an integer cached key
*
* @param string $key Identifier for the data
* @param integer $offset How much to increment
* @param integer $duration How long to cache the data, in seconds
* @return New incremented value, false otherwise
* @access public
*/
function increment($key, $offset = 1) {
if ($this->settings['compress']) {
trigger_error(sprintf(__('Method increment() not implemented for compressed cache in %s', true), get_class($this)), E_USER_ERROR);
}
return $this->__Memcache->increment($key, $offset);
}
/**
* Decrements the value of an integer cached key
*
* @param string $key Identifier for the data
* @param integer $offset How much to substract
* @param integer $duration How long to cache the data, in seconds
* @return New decremented value, false otherwise
* @access public
*/
function decrement($key, $offset = 1) {
if ($this->settings['compress']) {
trigger_error(sprintf(__('Method decrement() not implemented for compressed cache in %s', true), get_class($this)), E_USER_ERROR);
}
return $this->__Memcache->decrement($key, $offset);
}
/**
* Delete a key from the cache
*
* @param string $key Identifier for the data
* @return boolean True if the value was succesfully deleted, false if it didn't exist or couldn't be removed
* @access public
*/
function delete($key) {
return $this->__Memcache->delete($key);
}
/**
* Delete all keys from the cache
*
* @return boolean True if the cache was succesfully cleared, false otherwise
* @access public
*/
function clear() {
return $this->__Memcache->flush();
}
/**
* Connects to a server in connection pool
*
* @param string $host host ip address or name
* @param integer $port Server port
* @return boolean True if memcache server was connected
* @access public
*/
function connect($host, $port = 11211) {
if ($this->__Memcache->getServerStatus($host, $port) === 0) {
if ($this->__Memcache->connect($host, $port)) {
return true;
}
return false;
}
return true;
}
}

View File

@@ -0,0 +1,183 @@
<?php
/**
* Xcache storage engine for cache.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.cache
* @since CakePHP(tm) v 1.2.0.4947
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Xcache storage engine for cache
*
* @link http://trac.lighttpd.net/xcache/ Xcache
* @package cake
* @subpackage cake.cake.libs.cache
*/
class XcacheEngine extends CacheEngine {
/**
* Settings
*
* - PHP_AUTH_USER = xcache.admin.user, default cake
* - PHP_AUTH_PW = xcache.admin.password, default cake
*
* @var array
* @access public
*/
var $settings = array();
/**
* Initialize the Cache Engine
*
* Called automatically by the cache frontend
* To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array());
*
* @param array $setting array of setting for the engine
* @return boolean True if the engine has been successfully initialized, false if not
* @access public
*/
function init($settings) {
parent::init(array_merge(array(
'engine' => 'Xcache', 'prefix' => Inflector::slug(APP_DIR) . '_', 'PHP_AUTH_USER' => 'user', 'PHP_AUTH_PW' => 'password'
), $settings)
);
return function_exists('xcache_info');
}
/**
* Write data for key into cache
*
* @param string $key Identifier for the data
* @param mixed $value Data to be cached
* @param integer $duration How long to cache the data, in seconds
* @return boolean True if the data was succesfully cached, false on failure
* @access public
*/
function write($key, &$value, $duration) {
$expires = time() + $duration;
xcache_set($key . '_expires', $expires, $duration);
return xcache_set($key, $value, $duration);
}
/**
* Read a key from the cache
*
* @param string $key Identifier for the data
* @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
* @access public
*/
function read($key) {
if (xcache_isset($key)) {
$time = time();
$cachetime = intval(xcache_get($key . '_expires'));
if ($cachetime < $time || ($time + $this->settings['duration']) < $cachetime) {
return false;
}
return xcache_get($key);
}
return false;
}
/**
* Increments the value of an integer cached key
* If the cache key is not an integer it will be treated as 0
*
* @param string $key Identifier for the data
* @param integer $offset How much to increment
* @param integer $duration How long to cache the data, in seconds
* @return New incremented value, false otherwise
* @access public
*/
function increment($key, $offset = 1) {
return xcache_inc($key, $offset);
}
/**
* Decrements the value of an integer cached key.
* If the cache key is not an integer it will be treated as 0
*
* @param string $key Identifier for the data
* @param integer $offset How much to substract
* @param integer $duration How long to cache the data, in seconds
* @return New decremented value, false otherwise
* @access public
*/
function decrement($key, $offset = 1) {
return xcache_dec($key, $offset);
}
/**
* Delete a key from the cache
*
* @param string $key Identifier for the data
* @return boolean True if the value was succesfully deleted, false if it didn't exist or couldn't be removed
* @access public
*/
function delete($key) {
return xcache_unset($key);
}
/**
* Delete all keys from the cache
*
* @return boolean True if the cache was succesfully cleared, false otherwise
* @access public
*/
function clear() {
$this->__auth();
$max = xcache_count(XC_TYPE_VAR);
for ($i = 0; $i < $max; $i++) {
xcache_clear_cache(XC_TYPE_VAR, $i);
}
$this->__auth(true);
return true;
}
/**
* Populates and reverses $_SERVER authentication values
* Makes necessary changes (and reverting them back) in $_SERVER
*
* This has to be done because xcache_clear_cache() needs to pass Basic Http Auth
* (see xcache.admin configuration settings)
*
* @param boolean Revert changes
* @access private
*/
function __auth($reverse = false) {
static $backup = array();
$keys = array('PHP_AUTH_USER' => 'user', 'PHP_AUTH_PW' => 'password');
foreach ($keys as $key => $setting) {
if ($reverse) {
if (isset($backup[$key])) {
$_SERVER[$key] = $backup[$key];
unset($backup[$key]);
} else {
unset($_SERVER[$key]);
}
} else {
$value = env($key);
if (!empty($value)) {
$backup[$key] = $value;
}
if (!empty($this->settings[$setting])) {
$_SERVER[$key] = $this->settings[$setting];
} else if (!empty($this->settings[$key])) {
$_SERVER[$key] = $this->settings[$key];
} else {
$_SERVER[$key] = $value;
}
}
}
}
}

View File

@@ -0,0 +1,291 @@
<?php
/**
* Logging.
*
* Log messages to text files.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs
* @since CakePHP(tm) v 0.2.9
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Set up error level constants to be used within the framework if they are not defined within the
* system.
*
*/
if (!defined('LOG_WARNING')) {
define('LOG_WARNING', 3);
}
if (!defined('LOG_NOTICE')) {
define('LOG_NOTICE', 4);
}
if (!defined('LOG_DEBUG')) {
define('LOG_DEBUG', 5);
}
if (!defined('LOG_INFO')) {
define('LOG_INFO', 6);
}
/**
* Logs messages to configured Log adapters. One or more adapters can be configured
* using CakeLogs's methods. If you don't configure any adapters, and write to the logs
* a default FileLog will be autoconfigured for you.
*
* @package cake
* @subpackage cake.cake.libs
*/
class CakeLog {
/**
* An array of connected streams.
* Each stream represents a callable that will be called when write() is called.
*
* @var array
* @access protected
*/
var $_streams = array();
/**
* Get an instance
*
* @return void
* @static
*/
function &getInstance() {
static $instance = array();
if (!isset($instance[0])) {
$instance[0] =& new CakeLog();
}
return $instance[0];
}
/**
* Configure and add a new logging stream to CakeLog
* You can use add loggers from app/libs use app.loggername, or any plugin/libs using plugin.loggername.
*
* ### Usage:
*
* {{{
* CakeLog::config('second_file', array(
* 'engine' => 'FileLog',
* 'path' => '/var/logs/my_app/'
* ));
* }}}
*
* Will configure a FileLog instance to use the specified path. All options that are not `engine`
* are passed onto the logging adapter, and handled there. Any class can be configured as a logging
* adapter as long as it implements a `write` method with the following signature.
*
* `write($type, $message)`
*
* For an explaination of these parameters, see CakeLog::write()
*
* @param string $key The keyname for this logger, used to remove the logger later.
* @param array $config Array of configuration information for the logger
* @return boolean success of configuration.
* @static
*/
function config($key, $config) {
if (empty($config['engine'])) {
trigger_error(__('Missing logger classname', true), E_USER_WARNING);
return false;
}
$self =& CakeLog::getInstance();
$className = $self->_getLogger($config['engine']);
if (!$className) {
return false;
}
unset($config['engine']);
$self->_streams[$key] = new $className($config);
return true;
}
/**
* Attempts to import a logger class from the various paths it could be on.
* Checks that the logger class implements a write method as well.
*
* @param string $loggerName the plugin.className of the logger class you want to build.
* @return mixed boolean false on any failures, string of classname to use if search was successful.
* @access protected
*/
function _getLogger($loggerName) {
list($plugin, $loggerName) = pluginSplit($loggerName);
if ($plugin) {
App::import('Lib', $plugin . '.log/' . $loggerName);
} else {
if (!App::import('Lib', 'log/' . $loggerName)) {
App::import('Core', 'log/' . $loggerName);
}
}
if (!class_exists($loggerName)) {
trigger_error(sprintf(__('Could not load logger class %s', true), $loggerName), E_USER_WARNING);
return false;
}
if (!is_callable(array($loggerName, 'write'))) {
trigger_error(
sprintf(__('logger class %s does not implement a write method.', true), $loggerName),
E_USER_WARNING
);
return false;
}
return $loggerName;
}
/**
* Returns the keynames of the currently active streams
*
* @return array Array of configured log streams.
* @access public
* @static
*/
function configured() {
$self =& CakeLog::getInstance();
return array_keys($self->_streams);
}
/**
* Removes a stream from the active streams. Once a stream has been removed
* it will no longer have messages sent to it.
*
* @param string $keyname Key name of a configured stream to remove.
* @return void
* @access public
* @static
*/
function drop($streamName) {
$self =& CakeLog::getInstance();
unset($self->_streams[$streamName]);
}
/**
* Configures the automatic/default stream a FileLog.
*
* @return void
* @access protected
*/
function _autoConfig() {
if (!class_exists('FileLog')) {
App::import('Core', 'log/FileLog');
}
$this->_streams['default'] =& new FileLog(array('path' => LOGS));
}
/**
* Writes the given message and type to all of the configured log adapters.
* Configured adapters are passed both the $type and $message variables. $type
* is one of the following strings/values.
*
* ### Types:
*
* - `LOG_WARNING` => 'warning',
* - `LOG_NOTICE` => 'notice',
* - `LOG_INFO` => 'info',
* - `LOG_DEBUG` => 'debug',
* - `LOG_ERR` => 'error',
* - `LOG_ERROR` => 'error'
*
* ### Usage:
*
* Write a message to the 'warning' log:
*
* `CakeLog::write('warning', 'Stuff is broken here');`
*
* @param string $type Type of message being written
* @param string $message Message content to log
* @return boolean Success
* @access public
* @static
*/
function write($type, $message) {
if (!defined('LOG_ERROR')) {
define('LOG_ERROR', 2);
}
if (!defined('LOG_ERR')) {
define('LOG_ERR', LOG_ERROR);
}
$levels = array(
LOG_WARNING => 'warning',
LOG_NOTICE => 'notice',
LOG_INFO => 'info',
LOG_DEBUG => 'debug',
LOG_ERR => 'error',
LOG_ERROR => 'error'
);
if (is_int($type) && isset($levels[$type])) {
$type = $levels[$type];
}
$self =& CakeLog::getInstance();
if (empty($self->_streams)) {
$self->_autoConfig();
}
$keys = array_keys($self->_streams);
foreach ($keys as $key) {
$logger =& $self->_streams[$key];
$logger->write($type, $message);
}
return true;
}
/**
* An error_handler that will log errors to file using CakeLog::write();
* You can control how verbose and what type of errors this error_handler will
* catch using `Configure::write('log', $value)`. See core.php for more information.
*
*
* @param integer $code Code of error
* @param string $description Error description
* @param string $file File on which error occurred
* @param integer $line Line that triggered the error
* @param array $context Context
* @return void
*/
function handleError($code, $description, $file = null, $line = null, $context = null) {
if ($code === 2048 || $code === 8192) {
return;
}
switch ($code) {
case E_PARSE:
case E_ERROR:
case E_CORE_ERROR:
case E_COMPILE_ERROR:
case E_USER_ERROR:
$error = 'Fatal Error';
$level = LOG_ERROR;
break;
case E_WARNING:
case E_USER_WARNING:
case E_COMPILE_WARNING:
case E_RECOVERABLE_ERROR:
$error = 'Warning';
$level = LOG_WARNING;
break;
case E_NOTICE:
case E_USER_NOTICE:
$error = 'Notice';
$level = LOG_NOTICE;
break;
default:
return;
break;
}
$message = $error . ' (' . $code . '): ' . $description . ' in [' . $file . ', line ' . $line . ']';
CakeLog::write($level, $message);
}
}
if (!defined('DISABLE_DEFAULT_ERROR_HANDLING')) {
set_error_handler(array('CakeLog', 'handleError'));
}

View File

@@ -0,0 +1,791 @@
<?php
/**
* Session class for Cake.
*
* Cake abstracts the handling of sessions.
* There are several convenient methods to access session information.
* This class is the implementation of those methods.
* They are mostly used by the Session Component.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs
* @since CakePHP(tm) v .0.10.0.1222
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Session class for Cake.
*
* Cake abstracts the handling of sessions. There are several convenient methods to access session information.
* This class is the implementation of those methods. They are mostly used by the Session Component.
*
* @package cake
* @subpackage cake.cake.libs
*/
class CakeSession extends Object {
/**
* True if the Session is still valid
*
* @var boolean
* @access public
*/
var $valid = false;
/**
* Error messages for this session
*
* @var array
* @access public
*/
var $error = false;
/**
* User agent string
*
* @var string
* @access protected
*/
var $_userAgent = '';
/**
* Path to where the session is active.
*
* @var string
* @access public
*/
var $path = '/';
/**
* Error number of last occurred error
*
* @var integer
* @access public
*/
var $lastError = null;
/**
* 'Security.level' setting, "high", "medium", or "low".
*
* @var string
* @access public
*/
var $security = null;
/**
* Start time for this session.
*
* @var integer
* @access public
*/
var $time = false;
/**
* Time when this session becomes invalid.
*
* @var integer
* @access public
*/
var $sessionTime = false;
/**
* The number of seconds to set for session.cookie_lifetime. 0 means
* at browser close.
*
* @var integer
*/
var $cookieLifeTime = false;
/**
* Keeps track of keys to watch for writes on
*
* @var array
* @access public
*/
var $watchKeys = array();
/**
* Current Session id
*
* @var string
* @access public
*/
var $id = null;
/**
* Hostname
*
* @var string
* @access public
*/
var $host = null;
/**
* Session timeout multiplier factor
*
* @var integer
* @access public
*/
var $timeout = null;
/**
* Constructor.
*
* @param string $base The base path for the Session
* @param boolean $start Should session be started right now
* @access public
*/
function __construct($base = null, $start = true) {
App::import('Core', array('Set', 'Security'));
$this->time = time();
if (Configure::read('Session.checkAgent') === true || Configure::read('Session.checkAgent') === null) {
if (env('HTTP_USER_AGENT') != null) {
$this->_userAgent = md5(env('HTTP_USER_AGENT') . Configure::read('Security.salt'));
}
}
if (Configure::read('Session.save') === 'database') {
$modelName = Configure::read('Session.model');
$database = Configure::read('Session.database');
$table = Configure::read('Session.table');
if (empty($database)) {
$database = 'default';
}
$settings = array(
'class' => 'Session',
'alias' => 'Session',
'table' => 'cake_sessions',
'ds' => $database
);
if (!empty($modelName)) {
$settings['class'] = $modelName;
}
if (!empty($table)) {
$settings['table'] = $table;
}
ClassRegistry::init($settings);
}
if ($start === true) {
if (!empty($base)) {
$this->path = $base;
if (strpos($base, 'index.php') !== false) {
$this->path = str_replace('index.php', '', $base);
}
if (strpos($base, '?') !== false) {
$this->path = str_replace('?', '', $base);
}
}
$this->host = env('HTTP_HOST');
if (strpos($this->host, ':') !== false) {
$this->host = substr($this->host, 0, strpos($this->host, ':'));
}
}
if (isset($_SESSION) || $start === true) {
if (!class_exists('Security')) {
App::import('Core', 'Security');
}
$this->sessionTime = $this->time + (Security::inactiveMins() * Configure::read('Session.timeout'));
$this->security = Configure::read('Security.level');
}
parent::__construct();
}
/**
* Starts the Session.
*
* @return boolean True if session was started
* @access public
*/
function start() {
if ($this->started()) {
return true;
}
if (function_exists('session_write_close')) {
session_write_close();
}
$this->__initSession();
$this->__startSession();
return $this->started();
}
/**
* Determine if Session has been started.
*
* @access public
* @return boolean True if session has been started.
*/
function started() {
if (isset($_SESSION) && session_id()) {
return true;
}
return false;
}
/**
* Returns true if given variable is set in session.
*
* @param string $name Variable name to check for
* @return boolean True if variable is there
* @access public
*/
function check($name) {
if (empty($name)) {
return false;
}
$result = Set::classicExtract($_SESSION, $name);
return isset($result);
}
/**
* Returns the Session id
*
* @param id $name string
* @return string Session id
* @access public
*/
function id($id = null) {
if ($id) {
$this->id = $id;
session_id($this->id);
}
if ($this->started()) {
return session_id();
} else {
return $this->id;
}
}
/**
* Removes a variable from session.
*
* @param string $name Session variable to remove
* @return boolean Success
* @access public
*/
function delete($name) {
if ($this->check($name)) {
if (in_array($name, $this->watchKeys)) {
trigger_error(sprintf(__('Deleting session key {%s}', true), $name), E_USER_NOTICE);
}
$this->__overwrite($_SESSION, Set::remove($_SESSION, $name));
return ($this->check($name) == false);
}
$this->__setError(2, sprintf(__("%s doesn't exist", true), $name));
return false;
}
/**
* Used to write new data to _SESSION, since PHP doesn't like us setting the _SESSION var itself
*
* @param array $old Set of old variables => values
* @param array $new New set of variable => value
* @access private
*/
function __overwrite(&$old, $new) {
if (!empty($old)) {
foreach ($old as $key => $var) {
if (!isset($new[$key])) {
unset($old[$key]);
}
}
}
foreach ($new as $key => $var) {
$old[$key] = $var;
}
}
/**
* Return error description for given error number.
*
* @param integer $errorNumber Error to set
* @return string Error as string
* @access private
*/
function __error($errorNumber) {
if (!is_array($this->error) || !array_key_exists($errorNumber, $this->error)) {
return false;
} else {
return $this->error[$errorNumber];
}
}
/**
* Returns last occurred error as a string, if any.
*
* @return mixed Error description as a string, or false.
* @access public
*/
function error() {
if ($this->lastError) {
return $this->__error($this->lastError);
} else {
return false;
}
}
/**
* Returns true if session is valid.
*
* @return boolean Success
* @access public
*/
function valid() {
if ($this->read('Config')) {
if ((Configure::read('Session.checkAgent') === false || $this->_userAgent == $this->read('Config.userAgent')) && $this->time <= $this->read('Config.time')) {
if ($this->error === false) {
$this->valid = true;
}
} else {
$this->valid = false;
$this->__setError(1, 'Session Highjacking Attempted !!!');
}
}
return $this->valid;
}
/**
* Returns given session variable, or all of them, if no parameters given.
*
* @param mixed $name The name of the session variable (or a path as sent to Set.extract)
* @return mixed The value of the session variable
* @access public
*/
function read($name = null) {
if (is_null($name)) {
return $this->__returnSessionVars();
}
if (empty($name)) {
return false;
}
$result = Set::classicExtract($_SESSION, $name);
if (!is_null($result)) {
return $result;
}
$this->__setError(2, "$name doesn't exist");
return null;
}
/**
* Returns all session variables.
*
* @return mixed Full $_SESSION array, or false on error.
* @access private
*/
function __returnSessionVars() {
if (!empty($_SESSION)) {
return $_SESSION;
}
$this->__setError(2, "No Session vars set");
return false;
}
/**
* Tells Session to write a notification when a certain session path or subpath is written to
*
* @param mixed $var The variable path to watch
* @return void
* @access public
*/
function watch($var) {
if (empty($var)) {
return false;
}
if (!in_array($var, $this->watchKeys, true)) {
$this->watchKeys[] = $var;
}
}
/**
* Tells Session to stop watching a given key path
*
* @param mixed $var The variable path to watch
* @return void
* @access public
*/
function ignore($var) {
if (!in_array($var, $this->watchKeys)) {
return;
}
foreach ($this->watchKeys as $i => $key) {
if ($key == $var) {
unset($this->watchKeys[$i]);
$this->watchKeys = array_values($this->watchKeys);
return;
}
}
}
/**
* Writes value to given session variable name.
*
* @param mixed $name Name of variable
* @param string $value Value to write
* @return boolean True if the write was successful, false if the write failed
* @access public
*/
function write($name, $value) {
if (empty($name)) {
return false;
}
if (in_array($name, $this->watchKeys)) {
trigger_error(sprintf(__('Writing session key {%s}: %s', true), $name, Debugger::exportVar($value)), E_USER_NOTICE);
}
$this->__overwrite($_SESSION, Set::insert($_SESSION, $name, $value));
return (Set::classicExtract($_SESSION, $name) === $value);
}
/**
* Helper method to destroy invalid sessions.
*
* @return void
* @access public
*/
function destroy() {
if ($this->started()) {
session_destroy();
}
$_SESSION = null;
$this->__construct($this->path);
$this->start();
$this->renew();
$this->_checkValid();
}
/**
* Helper method to initialize a session, based on Cake core settings.
*
* @access private
*/
function __initSession() {
$iniSet = function_exists('ini_set');
if ($iniSet && env('HTTPS')) {
ini_set('session.cookie_secure', 1);
}
if ($iniSet && ($this->security === 'high' || $this->security === 'medium')) {
ini_set('session.referer_check', $this->host);
}
if ($this->security == 'high') {
$this->cookieLifeTime = 0;
} else {
$this->cookieLifeTime = Configure::read('Session.timeout') * (Security::inactiveMins() * 60);
}
switch (Configure::read('Session.save')) {
case 'cake':
if (empty($_SESSION)) {
if ($iniSet) {
ini_set('session.use_trans_sid', 0);
ini_set('url_rewriter.tags', '');
ini_set('session.serialize_handler', 'php');
ini_set('session.use_cookies', 1);
ini_set('session.name', Configure::read('Session.cookie'));
ini_set('session.cookie_lifetime', $this->cookieLifeTime);
ini_set('session.cookie_path', $this->path);
ini_set('session.auto_start', 0);
ini_set('session.save_path', TMP . 'sessions');
}
}
break;
case 'database':
if (empty($_SESSION)) {
if (Configure::read('Session.model') === null) {
trigger_error(__("You must set the all Configure::write('Session.*') in core.php to use database storage"), E_USER_WARNING);
$this->_stop();
}
if ($iniSet) {
ini_set('session.use_trans_sid', 0);
ini_set('url_rewriter.tags', '');
ini_set('session.save_handler', 'user');
ini_set('session.serialize_handler', 'php');
ini_set('session.use_cookies', 1);
ini_set('session.name', Configure::read('Session.cookie'));
ini_set('session.cookie_lifetime', $this->cookieLifeTime);
ini_set('session.cookie_path', $this->path);
ini_set('session.auto_start', 0);
}
}
session_set_save_handler(
array('CakeSession','__open'),
array('CakeSession', '__close'),
array('CakeSession', '__read'),
array('CakeSession', '__write'),
array('CakeSession', '__destroy'),
array('CakeSession', '__gc')
);
break;
case 'php':
if (empty($_SESSION)) {
if ($iniSet) {
ini_set('session.use_trans_sid', 0);
ini_set('session.name', Configure::read('Session.cookie'));
ini_set('session.cookie_lifetime', $this->cookieLifeTime);
ini_set('session.cookie_path', $this->path);
}
}
break;
case 'cache':
if (empty($_SESSION)) {
if (!class_exists('Cache')) {
require LIBS . 'cache.php';
}
if ($iniSet) {
ini_set('session.use_trans_sid', 0);
ini_set('url_rewriter.tags', '');
ini_set('session.save_handler', 'user');
ini_set('session.use_cookies', 1);
ini_set('session.name', Configure::read('Session.cookie'));
ini_set('session.cookie_lifetime', $this->cookieLifeTime);
ini_set('session.cookie_path', $this->path);
}
}
session_set_save_handler(
array('CakeSession','__open'),
array('CakeSession', '__close'),
array('Cache', 'read'),
array('Cache', 'write'),
array('Cache', 'delete'),
array('Cache', 'gc')
);
break;
default:
$config = CONFIGS . Configure::read('Session.save') . '.php';
if (is_file($config)) {
require($config);
}
break;
}
}
/**
* Helper method to start a session
*
* @access private
*/
function __startSession() {
if (headers_sent()) {
if (empty($_SESSION)) {
$_SESSION = array();
}
return true;
} elseif (!isset($_SESSION)) {
session_cache_limiter ("must-revalidate");
session_start();
header ('P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"');
return true;
} else {
session_start();
return true;
}
}
/**
* Helper method to create a new session.
*
* @return void
* @access protected
*/
function _checkValid() {
if ($this->read('Config')) {
if ((Configure::read('Session.checkAgent') === false || $this->_userAgent == $this->read('Config.userAgent')) && $this->time <= $this->read('Config.time')) {
$time = $this->read('Config.time');
$this->write('Config.time', $this->sessionTime);
if (Configure::read('Security.level') === 'high') {
$check = $this->read('Config.timeout');
$check -= 1;
$this->write('Config.timeout', $check);
if (time() > ($time - (Security::inactiveMins() * Configure::read('Session.timeout')) + 2) || $check < 1) {
$this->renew();
$this->write('Config.timeout', 10);
}
}
$this->valid = true;
} else {
$this->destroy();
$this->valid = false;
$this->__setError(1, 'Session Highjacking Attempted !!!');
}
} else {
$this->write('Config.userAgent', $this->_userAgent);
$this->write('Config.time', $this->sessionTime);
$this->write('Config.timeout', 10);
$this->valid = true;
$this->__setError(1, 'Session is valid');
}
}
/**
* Helper method to restart a session.
*
* @return void
* @access private
*/
function __regenerateId() {
$oldSessionId = session_id();
if ($oldSessionId) {
if (session_id() != ''|| isset($_COOKIE[session_name()])) {
setcookie(Configure::read('Session.cookie'), '', time() - 42000, $this->path);
}
session_regenerate_id(true);
if (PHP_VERSION < 5.1) {
$sessionPath = session_save_path();
if (empty($sessionPath)) {
$sessionPath = '/tmp';
}
$newSessid = session_id();
if (function_exists('session_write_close')) {
session_write_close();
}
$this->__initSession();
session_id($oldSessionId);
session_start();
session_destroy();
$file = $sessionPath . DS . 'sess_' . $oldSessionId;
@unlink($file);
$this->__initSession();
session_id($newSessid);
session_start();
}
}
}
/**
* Restarts this session.
*
* @access public
*/
function renew() {
$this->__regenerateId();
}
/**
* Helper method to set an internal error message.
*
* @param integer $errorNumber Number of the error
* @param string $errorMessage Description of the error
* @return void
* @access private
*/
function __setError($errorNumber, $errorMessage) {
if ($this->error === false) {
$this->error = array();
}
$this->error[$errorNumber] = $errorMessage;
$this->lastError = $errorNumber;
}
/**
* Method called on open of a database session.
*
* @return boolean Success
* @access private
*/
function __open() {
return true;
}
/**
* Method called on close of a database session.
*
* @return boolean Success
* @access private
*/
function __close() {
$probability = mt_rand(1, 150);
if ($probability <= 3) {
switch (Configure::read('Session.save')) {
case 'cache':
Cache::gc();
break;
default:
CakeSession::__gc();
break;
}
}
return true;
}
/**
* Method used to read from a database session.
*
* @param mixed $id The key of the value to read
* @return mixed The value of the key or false if it does not exist
* @access private
*/
function __read($id) {
$model =& ClassRegistry::getObject('Session');
$row = $model->find('first', array(
'conditions' => array($model->primaryKey => $id)
));
if (empty($row[$model->alias]['data'])) {
return false;
}
return $row[$model->alias]['data'];
}
/**
* Helper function called on write for database sessions.
*
* @param integer $id ID that uniquely identifies session in database
* @param mixed $data The value of the data to be saved.
* @return boolean True for successful write, false otherwise.
* @access private
*/
function __write($id, $data) {
$expires = time() + Configure::read('Session.timeout') * Security::inactiveMins();
$model =& ClassRegistry::getObject('Session');
$return = $model->save(array($model->primaryKey => $id) + compact('data', 'expires'));
return $return;
}
/**
* Method called on the destruction of a database session.
*
* @param integer $id ID that uniquely identifies session in database
* @return boolean True for successful delete, false otherwise.
* @access private
*/
function __destroy($id) {
$model =& ClassRegistry::getObject('Session');
$return = $model->delete($id);
return $return;
}
/**
* Helper function called on gc for database sessions.
*
* @param integer $expires Timestamp (defaults to current time)
* @return boolean Success
* @access private
*/
function __gc($expires = null) {
$model =& ClassRegistry::getObject('Session');
if (!$expires) {
$expires = time();
}
$return = $model->deleteAll(array($model->alias . ".expires <" => $expires), false, false);
return $return;
}
}

View File

@@ -0,0 +1,304 @@
<?php
/**
* Cake Socket connection class.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs
* @since CakePHP(tm) v 1.2.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Core', 'Validation');
/**
* Cake network socket connection class.
*
* Core base class for network communication.
*
* @package cake
* @subpackage cake.cake.libs
*/
class CakeSocket extends Object {
/**
* Object description
*
* @var string
* @access public
*/
var $description = 'Remote DataSource Network Socket Interface';
/**
* Base configuration settings for the socket connection
*
* @var array
* @access protected
*/
var $_baseConfig = array(
'persistent' => false,
'host' => 'localhost',
'protocol' => 'tcp',
'port' => 80,
'timeout' => 30
);
/**
* Configuration settings for the socket connection
*
* @var array
* @access public
*/
var $config = array();
/**
* Reference to socket connection resource
*
* @var resource
* @access public
*/
var $connection = null;
/**
* This boolean contains the current state of the CakeSocket class
*
* @var boolean
* @access public
*/
var $connected = false;
/**
* This variable contains an array with the last error number (num) and string (str)
*
* @var array
* @access public
*/
var $lastError = array();
/**
* Constructor.
*
* @param array $config Socket configuration, which will be merged with the base configuration
* @see CakeSocket::$_baseConfig
*/
function __construct($config = array()) {
parent::__construct();
$this->config = array_merge($this->_baseConfig, $config);
if (!is_numeric($this->config['protocol'])) {
$this->config['protocol'] = getprotobyname($this->config['protocol']);
}
}
/**
* Connect the socket to the given host and port.
*
* @return boolean Success
* @access public
*/
function connect() {
if ($this->connection != null) {
$this->disconnect();
}
$scheme = null;
if (isset($this->config['request']) && $this->config['request']['uri']['scheme'] == 'https') {
$scheme = 'ssl://';
}
if ($this->config['persistent'] == true) {
$tmp = null;
$this->connection = @pfsockopen($scheme.$this->config['host'], $this->config['port'], $errNum, $errStr, $this->config['timeout']);
} else {
$this->connection = @fsockopen($scheme.$this->config['host'], $this->config['port'], $errNum, $errStr, $this->config['timeout']);
}
if (!empty($errNum) || !empty($errStr)) {
$this->setLastError($errStr, $errNum);
}
$this->connected = is_resource($this->connection);
if ($this->connected) {
stream_set_timeout($this->connection, $this->config['timeout']);
}
return $this->connected;
}
/**
* Get the host name of the current connection.
*
* @return string Host name
* @access public
*/
function host() {
if (Validation::ip($this->config['host'])) {
return gethostbyaddr($this->config['host']);
} else {
return gethostbyaddr($this->address());
}
}
/**
* Get the IP address of the current connection.
*
* @return string IP address
* @access public
*/
function address() {
if (Validation::ip($this->config['host'])) {
return $this->config['host'];
} else {
return gethostbyname($this->config['host']);
}
}
/**
* Get all IP addresses associated with the current connection.
*
* @return array IP addresses
* @access public
*/
function addresses() {
if (Validation::ip($this->config['host'])) {
return array($this->config['host']);
} else {
return gethostbynamel($this->config['host']);
}
}
/**
* Get the last error as a string.
*
* @return string Last error
* @access public
*/
function lastError() {
if (!empty($this->lastError)) {
return $this->lastError['num'] . ': ' . $this->lastError['str'];
} else {
return null;
}
}
/**
* Set the last error.
*
* @param integer $errNum Error code
* @param string $errStr Error string
* @access public
*/
function setLastError($errNum, $errStr) {
$this->lastError = array('num' => $errNum, 'str' => $errStr);
}
/**
* Write data to the socket.
*
* @param string $data The data to write to the socket
* @return boolean Success
* @access public
*/
function write($data) {
if (!$this->connected) {
if (!$this->connect()) {
return false;
}
}
return fwrite($this->connection, $data, strlen($data));
}
/**
* Read data from the socket. Returns false if no data is available or no connection could be
* established.
*
* @param integer $length Optional buffer length to read; defaults to 1024
* @return mixed Socket data
* @access public
*/
function read($length = 1024) {
if (!$this->connected) {
if (!$this->connect()) {
return false;
}
}
if (!feof($this->connection)) {
$buffer = fread($this->connection, $length);
$info = stream_get_meta_data($this->connection);
if ($info['timed_out']) {
$this->setLastError(E_WARNING, __('Connection timed out', true));
return false;
}
return $buffer;
} else {
return false;
}
}
/**
* Abort socket operation.
*
* @return boolean Success
* @access public
*/
function abort() {
}
/**
* Disconnect the socket from the current connection.
*
* @return boolean Success
* @access public
*/
function disconnect() {
if (!is_resource($this->connection)) {
$this->connected = false;
return true;
}
$this->connected = !fclose($this->connection);
if (!$this->connected) {
$this->connection = null;
}
return !$this->connected;
}
/**
* Destructor, used to disconnect from current connection.
*
* @access private
*/
function __destruct() {
$this->disconnect();
}
/**
* Resets the state of this Socket instance to it's initial state (before Object::__construct got executed)
*
* @return boolean True on success
* @access public
*/
function reset($state = null) {
if (empty($state)) {
static $initalState = array();
if (empty($initalState)) {
$initalState = get_class_vars(__CLASS__);
}
$state = $initalState;
}
foreach ($state as $property => $value) {
$this->{$property} = $value;
}
return true;
}
}

View File

@@ -0,0 +1,364 @@
<?php
/**
* Class collections.
*
* A repository for class objects, each registered with a key.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs
* @since CakePHP(tm) v 0.9.2
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Class Collections.
*
* A repository for class objects, each registered with a key.
* If you try to add an object with the same key twice, nothing will come of it.
* If you need a second instance of an object, give it another key.
*
* @package cake
* @subpackage cake.cake.libs
*/
class ClassRegistry {
/**
* Names of classes with their objects.
*
* @var array
* @access private
*/
var $__objects = array();
/**
* Names of class names mapped to the object in the registry.
*
* @var array
* @access private
*/
var $__map = array();
/**
* Default constructor parameter settings, indexed by type
*
* @var array
* @access private
*/
var $__config = array();
/**
* Return a singleton instance of the ClassRegistry.
*
* @return ClassRegistry instance
* @access public
*/
function &getInstance() {
static $instance = array();
if (!$instance) {
$instance[0] =& new ClassRegistry();
}
return $instance[0];
}
/**
* Loads a class, registers the object in the registry and returns instance of the object.
*
* Examples
* Simple Use: Get a Post model instance ```ClassRegistry::init('Post');```
*
* Exapanded: ```array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry', 'type' => 'TypeOfClass');```
*
* Model Classes can accept optional ```array('id' => $id, 'table' => $table, 'ds' => $ds, 'alias' => $alias);```
*
* When $class is a numeric keyed array, multiple class instances will be stored in the registry,
* no instance of the object will be returned
* {{{
* array(
* array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry', 'type' => 'TypeOfClass'),
* array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry', 'type' => 'TypeOfClass'),
* array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry', 'type' => 'TypeOfClass')
* );
* }}}
* @param mixed $class as a string or a single key => value array instance will be created,
* stored in the registry and returned.
* @param string $type TypeOfClass
* @return object instance of ClassName
* @access public
* @static
*/
function &init($class, $type = null) {
$_this =& ClassRegistry::getInstance();
$id = $false = false;
$true = true;
if (!$type) {
$type = 'Model';
}
if (is_array($class)) {
$objects = $class;
if (!isset($class[0])) {
$objects = array($class);
}
} else {
$objects = array(array('class' => $class));
}
$defaults = isset($_this->__config[$type]) ? $_this->__config[$type] : array();
$count = count($objects);
foreach ($objects as $key => $settings) {
if (is_array($settings)) {
$pluginPath = null;
$settings = array_merge($defaults, $settings);
$class = $settings['class'];
list($plugin, $class) = pluginSplit($class);
if ($plugin) {
$pluginPath = $plugin . '.';
}
if (empty($settings['alias'])) {
$settings['alias'] = $class;
}
$alias = $settings['alias'];
if ($model =& $_this->__duplicate($alias, $class)) {
$_this->map($alias, $class);
return $model;
}
if (class_exists($class) || App::import($type, $pluginPath . $class)) {
${$class} =& new $class($settings);
} elseif ($type === 'Model') {
if ($plugin && class_exists($plugin . 'AppModel')) {
$appModel = $plugin . 'AppModel';
} else {
$appModel = 'AppModel';
}
$settings['name'] = $class;
${$class} =& new $appModel($settings);
}
if (!isset(${$class})) {
trigger_error(sprintf(__('(ClassRegistry::init() could not create instance of %1$s class %2$s ', true), $class, $type), E_USER_WARNING);
return $false;
}
if ($type !== 'Model') {
$_this->addObject($alias, ${$class});
} else {
$_this->map($alias, $class);
}
} elseif (is_numeric($settings)) {
trigger_error(__('(ClassRegistry::init() Attempted to create instance of a class with a numeric name', true), E_USER_WARNING);
return $false;
}
}
if ($count > 1) {
return $true;
}
return ${$class};
}
/**
* Add $object to the registry, associating it with the name $key.
*
* @param string $key Key for the object in registry
* @param mixed $object Object to store
* @return boolean True if the object was written, false if $key already exists
* @access public
* @static
*/
function addObject($key, &$object) {
$_this =& ClassRegistry::getInstance();
$key = Inflector::underscore($key);
if (!isset($_this->__objects[$key])) {
$_this->__objects[$key] =& $object;
return true;
}
return false;
}
/**
* Remove object which corresponds to given key.
*
* @param string $key Key of object to remove from registry
* @return void
* @access public
* @static
*/
function removeObject($key) {
$_this =& ClassRegistry::getInstance();
$key = Inflector::underscore($key);
if (isset($_this->__objects[$key])) {
unset($_this->__objects[$key]);
}
}
/**
* Returns true if given key is present in the ClassRegistry.
*
* @param string $key Key to look for
* @return boolean true if key exists in registry, false otherwise
* @access public
* @static
*/
function isKeySet($key) {
$_this =& ClassRegistry::getInstance();
$key = Inflector::underscore($key);
if (isset($_this->__objects[$key])) {
return true;
} elseif (isset($_this->__map[$key])) {
return true;
}
return false;
}
/**
* Get all keys from the registry.
*
* @return array Set of keys stored in registry
* @access public
* @static
*/
function keys() {
$_this =& ClassRegistry::getInstance();
return array_keys($_this->__objects);
}
/**
* Return object which corresponds to given key.
*
* @param string $key Key of object to look for
* @return mixed Object stored in registry or boolean false if the object does not exist.
* @access public
* @static
*/
function &getObject($key) {
$_this =& ClassRegistry::getInstance();
$key = Inflector::underscore($key);
$return = false;
if (isset($_this->__objects[$key])) {
$return =& $_this->__objects[$key];
} else {
$key = $_this->__getMap($key);
if (isset($_this->__objects[$key])) {
$return =& $_this->__objects[$key];
}
}
return $return;
}
/**
* Sets the default constructor parameter for an object type
*
* @param string $type Type of object. If this parameter is omitted, defaults to "Model"
* @param array $param The parameter that will be passed to object constructors when objects
* of $type are created
* @return mixed Void if $param is being set. Otherwise, if only $type is passed, returns
* the previously-set value of $param, or null if not set.
* @access public
* @static
*/
function config($type, $param = array()) {
$_this =& ClassRegistry::getInstance();
if (empty($param) && is_array($type)) {
$param = $type;
$type = 'Model';
} elseif (is_null($param)) {
unset($_this->__config[$type]);
} elseif (empty($param) && is_string($type)) {
return isset($_this->__config[$type]) ? $_this->__config[$type] : null;
}
$_this->__config[$type] = $param;
}
/**
* Checks to see if $alias is a duplicate $class Object
*
* @param string $alias
* @param string $class
* @return boolean
* @access private
* @static
*/
function &__duplicate($alias, $class) {
$duplicate = false;
if ($this->isKeySet($alias)) {
$model =& $this->getObject($alias);
if (is_object($model) && (is_a($model, $class) || $model->alias === $class)) {
$duplicate =& $model;
}
unset($model);
}
return $duplicate;
}
/**
* Add a key name pair to the registry to map name to class in the registry.
*
* @param string $key Key to include in map
* @param string $name Key that is being mapped
* @access public
* @static
*/
function map($key, $name) {
$_this =& ClassRegistry::getInstance();
$key = Inflector::underscore($key);
$name = Inflector::underscore($name);
if (!isset($_this->__map[$key])) {
$_this->__map[$key] = $name;
}
}
/**
* Get all keys from the map in the registry.
*
* @return array Keys of registry's map
* @access public
* @static
*/
function mapKeys() {
$_this =& ClassRegistry::getInstance();
return array_keys($_this->__map);
}
/**
* Return the name of a class in the registry.
*
* @param string $key Key to find in map
* @return string Mapped value
* @access private
* @static
*/
function __getMap($key) {
if (isset($this->__map[$key])) {
return $this->__map[$key];
}
}
/**
* Flushes all objects from the ClassRegistry.
*
* @return void
* @access public
* @static
*/
function flush() {
$_this =& ClassRegistry::getInstance();
$_this->__objects = array();
$_this->__map = array();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
<?php
/**
* Application level Controller
*
* This file is application-wide controller file. You can put all
* application-wide controller-related methods here.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.controller
* @since CakePHP(tm) v 0.2.9
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* This is a placeholder class.
* Create the same file in app/app_controller.php
*
* Add your application-wide methods in the class below, your controllers
* will inherit them.
*
* @package cake
* @subpackage cake.cake.libs.controller
* @link http://book.cakephp.org/view/957/The-App-Controller
*/
class AppController extends Controller {
}

View File

@@ -0,0 +1,263 @@
<?php
/**
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.controller
* @since CakePHP(tm) v TBD
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Handler for Controller::$components
*
* @package cake
* @subpackage cake.cake.libs.controller
* @link http://book.cakephp.org/view/993/Components
*/
class Component extends Object {
/**
* Contains various controller variable information (plugin, name, base).
*
* @var object
* @access private
*/
var $__controllerVars = array('plugin' => null, 'name' => null, 'base' => null);
/**
* List of loaded components.
*
* @var object
* @access protected
*/
var $_loaded = array();
/**
* List of components attached directly to the controller, which callbacks
* should be executed on.
*
* @var object
* @access protected
*/
var $_primary = array();
/**
* Settings for loaded components.
*
* @var array
* @access private
*/
var $__settings = array();
/**
* Used to initialize the components for current controller.
*
* @param object $controller Controller with components to load
* @return void
* @access public
*/
function init(&$controller) {
if (!is_array($controller->components)) {
return;
}
$this->__controllerVars = array(
'plugin' => $controller->plugin, 'name' => $controller->name,
'base' => $controller->base
);
$this->_loadComponents($controller);
}
/**
* Called before the Controller::beforeFilter().
*
* @param object $controller Controller with components to initialize
* @return void
* @access public
* @link http://book.cakephp.org/view/998/MVC-Class-Access-Within-Components
*/
function initialize(&$controller) {
foreach (array_keys($this->_loaded) as $name) {
$component =& $this->_loaded[$name];
if (method_exists($component,'initialize') && $component->enabled === true) {
$settings = array();
if (isset($this->__settings[$name])) {
$settings = $this->__settings[$name];
}
$component->initialize($controller, $settings);
}
}
}
/**
* Called after the Controller::beforeFilter() and before the controller action
*
* @param object $controller Controller with components to startup
* @return void
* @access public
* @link http://book.cakephp.org/view/998/MVC-Class-Access-Within-Components
* @deprecated See Component::triggerCallback()
*/
function startup(&$controller) {
$this->triggerCallback('startup', $controller);
}
/**
* Called after the Controller::beforeRender(), after the view class is loaded, and before the
* Controller::render()
*
* @param object $controller Controller with components to beforeRender
* @return void
* @access public
* @deprecated See Component::triggerCallback()
*/
function beforeRender(&$controller) {
$this->triggerCallback('beforeRender', $controller);
}
/**
* Called before Controller::redirect().
*
* @param object $controller Controller with components to beforeRedirect
* @return void
* @access public
*/
function beforeRedirect(&$controller, $url, $status = null, $exit = true) {
$response = array();
foreach ($this->_primary as $name) {
$component =& $this->_loaded[$name];
if ($component->enabled === true && method_exists($component, 'beforeRedirect')) {
$resp = $component->beforeRedirect($controller, $url, $status, $exit);
if ($resp === false) {
return false;
}
$response[] = $resp;
}
}
return $response;
}
/**
* Called after Controller::render() and before the output is printed to the browser.
*
* @param object $controller Controller with components to shutdown
* @return void
* @access public
* @deprecated See Component::triggerCallback()
*/
function shutdown(&$controller) {
$this->triggerCallback('shutdown', $controller);
}
/**
* Trigger a callback on all primary components. Will fire $callback on all components
* that have such a method. You can implement and fire custom callbacks in addition to the
* standard ones.
*
* example use, from inside a controller:
*
* `$this->Component->triggerCallback('beforeFilter', $this);`
*
* will trigger the beforeFilter callback on all components that have implemented one. You
* can trigger any method in this fashion.
*
* @param Controller $controller Controller instance
* @param string $callback Callback to trigger.
* @return void
* @access public
*/
function triggerCallback($callback, &$controller) {
foreach ($this->_primary as $name) {
$component =& $this->_loaded[$name];
if (method_exists($component, $callback) && $component->enabled === true) {
$component->{$callback}($controller);
}
}
}
/**
* Loads components used by this component.
*
* @param object $object Object with a Components array
* @param object $parent the parent of the current object
* @return void
* @access protected
*/
function _loadComponents(&$object, $parent = null) {
$base = $this->__controllerVars['base'];
$normal = Set::normalize($object->components);
foreach ((array)$normal as $component => $config) {
$plugin = isset($this->__controllerVars['plugin']) ? $this->__controllerVars['plugin'] . '.' : null;
list($plugin, $component) = pluginSplit($component, true, $plugin);
$componentCn = $component . 'Component';
if (!class_exists($componentCn)) {
if (is_null($plugin) || !App::import('Component', $plugin . $component)) {
if (!App::import('Component', $component)) {
$this->cakeError('missingComponentFile', array(array(
'className' => $this->__controllerVars['name'],
'component' => $component,
'file' => Inflector::underscore($component) . '.php',
'base' => $base,
'code' => 500
)));
return false;
}
}
if (!class_exists($componentCn)) {
$this->cakeError('missingComponentClass', array(array(
'className' => $this->__controllerVars['name'],
'component' => $component,
'file' => Inflector::underscore($component) . '.php',
'base' => $base,
'code' => 500
)));
return false;
}
}
if ($parent === null) {
$this->_primary[] = $component;
}
if (isset($this->_loaded[$component])) {
$object->{$component} =& $this->_loaded[$component];
if (!empty($config) && isset($this->__settings[$component])) {
$this->__settings[$component] = array_merge($this->__settings[$component], $config);
} elseif (!empty($config)) {
$this->__settings[$component] = $config;
}
} else {
if ($componentCn === 'SessionComponent') {
$object->{$component} =& new $componentCn($base);
} else {
$object->{$component} =& new $componentCn();
}
$object->{$component}->enabled = true;
$this->_loaded[$component] =& $object->{$component};
if (!empty($config)) {
$this->__settings[$component] = $config;
}
}
if (isset($object->{$component}->components) && is_array($object->{$component}->components) && (!isset($object->{$component}->{$parent}))) {
$this->_loadComponents($object->{$component}, $component);
}
}
}
}

View File

@@ -0,0 +1,638 @@
<?php
/**
* Access Control List factory class.
*
* Permissions system.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.controller.components
* @since CakePHP(tm) v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Access Control List factory class.
*
* Uses a strategy pattern to allow custom ACL implementations to be used with the same component interface.
* You can define by changing `Configure::write('Acl.classname', 'DbAcl');` in your core.php. Concrete ACL
* implementations should extend `AclBase` and implement the methods it defines.
*
* @package cake
* @subpackage cake.cake.libs.controller.components
* @link http://book.cakephp.org/view/1242/Access-Control-Lists
*/
class AclComponent extends Object {
/**
* Instance of an ACL class
*
* @var object
* @access protected
*/
var $_Instance = null;
/**
* Constructor. Will return an instance of the correct ACL class as defined in `Configure::read('Acl.classname')`
*
*/
function __construct() {
$name = Inflector::camelize(strtolower(Configure::read('Acl.classname')));
if (!class_exists($name)) {
if (App::import('Component', $name)) {
list($plugin, $name) = pluginSplit($name);
$name .= 'Component';
} else {
trigger_error(sprintf(__('Could not find %s.', true), $name), E_USER_WARNING);
}
}
$this->_Instance =& new $name();
$this->_Instance->initialize($this);
}
/**
* Startup is not used
*
* @param object $controller Controller using this component
* @return boolean Proceed with component usage (true), or fail (false)
* @access public
*/
function startup(&$controller) {
return true;
}
/**
* Empty class defintion, to be overridden in subclasses.
*
* @access protected
*/
function _initACL() {
}
/**
* Pass-thru function for ACL check instance. Check methods
* are used to check whether or not an ARO can access an ACO
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @return boolean Success
* @access public
*/
function check($aro, $aco, $action = "*") {
return $this->_Instance->check($aro, $aco, $action);
}
/**
* Pass-thru function for ACL allow instance. Allow methods
* are used to grant an ARO access to an ACO.
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @return boolean Success
* @access public
*/
function allow($aro, $aco, $action = "*") {
return $this->_Instance->allow($aro, $aco, $action);
}
/**
* Pass-thru function for ACL deny instance. Deny methods
* are used to remove permission from an ARO to access an ACO.
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @return boolean Success
* @access public
*/
function deny($aro, $aco, $action = "*") {
return $this->_Instance->deny($aro, $aco, $action);
}
/**
* Pass-thru function for ACL inherit instance. Inherit methods
* modify the permission for an ARO to be that of its parent object.
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @return boolean Success
* @access public
*/
function inherit($aro, $aco, $action = "*") {
return $this->_Instance->inherit($aro, $aco, $action);
}
/**
* Pass-thru function for ACL grant instance. An alias for AclComponent::allow()
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @return boolean Success
* @access public
*/
function grant($aro, $aco, $action = "*") {
return $this->_Instance->grant($aro, $aco, $action);
}
/**
* Pass-thru function for ACL grant instance. An alias for AclComponent::deny()
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @return boolean Success
* @access public
*/
function revoke($aro, $aco, $action = "*") {
return $this->_Instance->revoke($aro, $aco, $action);
}
}
/**
* Access Control List abstract class. Not to be instantiated.
* Subclasses of this class are used by AclComponent to perform ACL checks in Cake.
*
* @package cake
* @subpackage cake.cake.libs.controller.components
* @abstract
*/
class AclBase extends Object {
/**
* This class should never be instantiated, just subclassed.
*
*/
function __construct() {
if (strcasecmp(get_class($this), "AclBase") == 0 || !is_subclass_of($this, "AclBase")) {
trigger_error(__("[acl_base] The AclBase class constructor has been called, or the class was instantiated. This class must remain abstract. Please refer to the Cake docs for ACL configuration.", true), E_USER_ERROR);
return NULL;
}
}
/**
* Empty method to be overridden in subclasses
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @access public
*/
function check($aro, $aco, $action = "*") {
}
/**
* Empty method to be overridden in subclasses
*
* @param object $component Component
* @access public
*/
function initialize(&$component) {
}
}
/**
* DbAcl implements an ACL control system in the database. ARO's and ACO's are
* structured into trees and a linking table is used to define permissions. You
* can install the schema for DbAcl with the Schema Shell.
*
* `$aco` and `$aro` parameters can be slash delimited paths to tree nodes.
*
* eg. `controllers/Users/edit`
*
* Would point to a tree structure like
*
* {{{
* controllers
* Users
* edit
* }}}
*
* @package cake
* @subpackage cake.cake.libs.model
*/
class DbAcl extends AclBase {
/**
* Constructor
*
*/
function __construct() {
parent::__construct();
if (!class_exists('AclNode')) {
require LIBS . 'model' . DS . 'db_acl.php';
}
$this->Aro =& ClassRegistry::init(array('class' => 'Aro', 'alias' => 'Aro'));
$this->Aco =& ClassRegistry::init(array('class' => 'Aco', 'alias' => 'Aco'));
}
/**
* Initializes the containing component and sets the Aro/Aco objects to it.
*
* @param AclComponent $component
* @return void
* @access public
*/
function initialize(&$component) {
$component->Aro =& $this->Aro;
$component->Aco =& $this->Aco;
}
/**
* Checks if the given $aro has access to action $action in $aco
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @return boolean Success (true if ARO has access to action in ACO, false otherwise)
* @access public
* @link http://book.cakephp.org/view/1249/Checking-Permissions-The-ACL-Component
*/
function check($aro, $aco, $action = "*") {
if ($aro == null || $aco == null) {
return false;
}
$permKeys = $this->_getAcoKeys($this->Aro->Permission->schema());
$aroPath = $this->Aro->node($aro);
$acoPath = $this->Aco->node($aco);
if (empty($aroPath) || empty($acoPath)) {
trigger_error(__("DbAcl::check() - Failed ARO/ACO node lookup in permissions check. Node references:\nAro: ", true) . print_r($aro, true) . "\nAco: " . print_r($aco, true), E_USER_WARNING);
return false;
}
if ($acoPath == null || $acoPath == array()) {
trigger_error(__("DbAcl::check() - Failed ACO node lookup in permissions check. Node references:\nAro: ", true) . print_r($aro, true) . "\nAco: " . print_r($aco, true), E_USER_WARNING);
return false;
}
$aroNode = $aroPath[0];
$acoNode = $acoPath[0];
if ($action != '*' && !in_array('_' . $action, $permKeys)) {
trigger_error(sprintf(__("ACO permissions key %s does not exist in DbAcl::check()", true), $action), E_USER_NOTICE);
return false;
}
$inherited = array();
$acoIDs = Set::extract($acoPath, '{n}.' . $this->Aco->alias . '.id');
$count = count($aroPath);
for ($i = 0 ; $i < $count; $i++) {
$permAlias = $this->Aro->Permission->alias;
$perms = $this->Aro->Permission->find('all', array(
'conditions' => array(
"{$permAlias}.aro_id" => $aroPath[$i][$this->Aro->alias]['id'],
"{$permAlias}.aco_id" => $acoIDs
),
'order' => array($this->Aco->alias . '.lft' => 'desc'),
'recursive' => 0
));
if (empty($perms)) {
continue;
} else {
$perms = Set::extract($perms, '{n}.' . $this->Aro->Permission->alias);
foreach ($perms as $perm) {
if ($action == '*') {
foreach ($permKeys as $key) {
if (!empty($perm)) {
if ($perm[$key] == -1) {
return false;
} elseif ($perm[$key] == 1) {
$inherited[$key] = 1;
}
}
}
if (count($inherited) === count($permKeys)) {
return true;
}
} else {
switch ($perm['_' . $action]) {
case -1:
return false;
case 0:
continue;
break;
case 1:
return true;
break;
}
}
}
}
}
return false;
}
/**
* Allow $aro to have access to action $actions in $aco
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @param string $actions Action (defaults to *)
* @param integer $value Value to indicate access type (1 to give access, -1 to deny, 0 to inherit)
* @return boolean Success
* @access public
* @link http://book.cakephp.org/view/1248/Assigning-Permissions
*/
function allow($aro, $aco, $actions = "*", $value = 1) {
$perms = $this->getAclLink($aro, $aco);
$permKeys = $this->_getAcoKeys($this->Aro->Permission->schema());
$save = array();
if ($perms == false) {
trigger_error(__('DbAcl::allow() - Invalid node', true), E_USER_WARNING);
return false;
}
if (isset($perms[0])) {
$save = $perms[0][$this->Aro->Permission->alias];
}
if ($actions == "*") {
$permKeys = $this->_getAcoKeys($this->Aro->Permission->schema());
$save = array_combine($permKeys, array_pad(array(), count($permKeys), $value));
} else {
if (!is_array($actions)) {
$actions = array('_' . $actions);
}
if (is_array($actions)) {
foreach ($actions as $action) {
if ($action{0} != '_') {
$action = '_' . $action;
}
if (in_array($action, $permKeys)) {
$save[$action] = $value;
}
}
}
}
list($save['aro_id'], $save['aco_id']) = array($perms['aro'], $perms['aco']);
if ($perms['link'] != null && !empty($perms['link'])) {
$save['id'] = $perms['link'][0][$this->Aro->Permission->alias]['id'];
} else {
unset($save['id']);
$this->Aro->Permission->id = null;
}
return ($this->Aro->Permission->save($save) !== false);
}
/**
* Deny access for $aro to action $action in $aco
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @param string $actions Action (defaults to *)
* @return boolean Success
* @access public
* @link http://book.cakephp.org/view/1248/Assigning-Permissions
*/
function deny($aro, $aco, $action = "*") {
return $this->allow($aro, $aco, $action, -1);
}
/**
* Let access for $aro to action $action in $aco be inherited
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @param string $actions Action (defaults to *)
* @return boolean Success
* @access public
*/
function inherit($aro, $aco, $action = "*") {
return $this->allow($aro, $aco, $action, 0);
}
/**
* Allow $aro to have access to action $actions in $aco
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @param string $actions Action (defaults to *)
* @return boolean Success
* @see allow()
* @access public
*/
function grant($aro, $aco, $action = "*") {
return $this->allow($aro, $aco, $action);
}
/**
* Deny access for $aro to action $action in $aco
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @param string $actions Action (defaults to *)
* @return boolean Success
* @see deny()
* @access public
*/
function revoke($aro, $aco, $action = "*") {
return $this->deny($aro, $aco, $action);
}
/**
* Get an array of access-control links between the given Aro and Aco
*
* @param string $aro ARO The requesting object identifier.
* @param string $aco ACO The controlled object identifier.
* @return array Indexed array with: 'aro', 'aco' and 'link'
* @access public
*/
function getAclLink($aro, $aco) {
$obj = array();
$obj['Aro'] = $this->Aro->node($aro);
$obj['Aco'] = $this->Aco->node($aco);
if (empty($obj['Aro']) || empty($obj['Aco'])) {
return false;
}
return array(
'aro' => Set::extract($obj, 'Aro.0.'.$this->Aro->alias.'.id'),
'aco' => Set::extract($obj, 'Aco.0.'.$this->Aco->alias.'.id'),
'link' => $this->Aro->Permission->find('all', array('conditions' => array(
$this->Aro->Permission->alias . '.aro_id' => Set::extract($obj, 'Aro.0.'.$this->Aro->alias.'.id'),
$this->Aro->Permission->alias . '.aco_id' => Set::extract($obj, 'Aco.0.'.$this->Aco->alias.'.id')
)))
);
}
/**
* Get the keys used in an ACO
*
* @param array $keys Permission model info
* @return array ACO keys
* @access protected
*/
function _getAcoKeys($keys) {
$newKeys = array();
$keys = array_keys($keys);
foreach ($keys as $key) {
if (!in_array($key, array('id', 'aro_id', 'aco_id'))) {
$newKeys[] = $key;
}
}
return $newKeys;
}
}
/**
* IniAcl implements an access control system using an INI file. An example
* of the ini file used can be found in /config/acl.ini.php.
*
* @package cake
* @subpackage cake.cake.libs.model.iniacl
*/
class IniAcl extends AclBase {
/**
* Array with configuration, parsed from ini file
*
* @var array
* @access public
*/
var $config = null;
/**
* The constructor must be overridden, as AclBase is abstract.
*
*/
function __construct() {
}
/**
* Main ACL check function. Checks to see if the ARO (access request object) has access to the
* ACO (access control object).Looks at the acl.ini.php file for permissions
* (see instructions in /config/acl.ini.php).
*
* @param string $aro ARO
* @param string $aco ACO
* @param string $aco_action Action
* @return boolean Success
* @access public
*/
function check($aro, $aco, $aco_action = null) {
if ($this->config == null) {
$this->config = $this->readConfigFile(CONFIGS . 'acl.ini.php');
}
$aclConfig = $this->config;
if (isset($aclConfig[$aro]['deny'])) {
$userDenies = $this->arrayTrim(explode(",", $aclConfig[$aro]['deny']));
if (array_search($aco, $userDenies)) {
return false;
}
}
if (isset($aclConfig[$aro]['allow'])) {
$userAllows = $this->arrayTrim(explode(",", $aclConfig[$aro]['allow']));
if (array_search($aco, $userAllows)) {
return true;
}
}
if (isset($aclConfig[$aro]['groups'])) {
$userGroups = $this->arrayTrim(explode(",", $aclConfig[$aro]['groups']));
foreach ($userGroups as $group) {
if (array_key_exists($group, $aclConfig)) {
if (isset($aclConfig[$group]['deny'])) {
$groupDenies=$this->arrayTrim(explode(",", $aclConfig[$group]['deny']));
if (array_search($aco, $groupDenies)) {
return false;
}
}
if (isset($aclConfig[$group]['allow'])) {
$groupAllows = $this->arrayTrim(explode(",", $aclConfig[$group]['allow']));
if (array_search($aco, $groupAllows)) {
return true;
}
}
}
}
}
return false;
}
/**
* Parses an INI file and returns an array that reflects the INI file's section structure. Double-quote friendly.
*
* @param string $fileName File
* @return array INI section structure
* @access public
*/
function readConfigFile($fileName) {
$fileLineArray = file($fileName);
foreach ($fileLineArray as $fileLine) {
$dataLine = trim($fileLine);
$firstChar = substr($dataLine, 0, 1);
if ($firstChar != ';' && $dataLine != '') {
if ($firstChar == '[' && substr($dataLine, -1, 1) == ']') {
$sectionName = preg_replace('/[\[\]]/', '', $dataLine);
} else {
$delimiter = strpos($dataLine, '=');
if ($delimiter > 0) {
$key = strtolower(trim(substr($dataLine, 0, $delimiter)));
$value = trim(substr($dataLine, $delimiter + 1));
if (substr($value, 0, 1) == '"' && substr($value, -1) == '"') {
$value = substr($value, 1, -1);
}
$iniSetting[$sectionName][$key]=stripcslashes($value);
} else {
if (!isset($sectionName)) {
$sectionName = '';
}
$iniSetting[$sectionName][strtolower(trim($dataLine))]='';
}
}
}
}
return $iniSetting;
}
/**
* Removes trailing spaces on all array elements (to prepare for searching)
*
* @param array $array Array to trim
* @return array Trimmed array
* @access public
*/
function arrayTrim($array) {
foreach ($array as $key => $value) {
$array[$key] = trim($value);
}
array_unshift($array, "");
return $array;
}
}

View File

@@ -0,0 +1,958 @@
<?php
/**
* Authentication component
*
* Manages user logins and permissions.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.controller.components
* @since CakePHP(tm) v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Core', array('Router', 'Security'), false);
/**
* Authentication control component class
*
* Binds access control with user authentication and session management.
*
* @package cake
* @subpackage cake.cake.libs.controller.components
* @link http://book.cakephp.org/view/1250/Authentication
*/
class AuthComponent extends Object {
/**
* Maintains current user login state.
*
* @var boolean
* @access private
*/
var $_loggedIn = false;
/**
* Other components utilized by AuthComponent
*
* @var array
* @access public
*/
var $components = array('Session', 'RequestHandler');
/**
* A reference to the object used for authentication
*
* @var object
* @access public
* @link http://book.cakephp.org/view/1278/authenticate
*/
var $authenticate = null;
/**
* The name of the component to use for Authorization or set this to
* 'controller' will validate against Controller::isAuthorized()
* 'actions' will validate Controller::action against an AclComponent::check()
* 'crud' will validate mapActions against an AclComponent::check()
* array('model'=> 'name'); will validate mapActions against model $name::isAuthorized(user, controller, mapAction)
* 'object' will validate Controller::action against object::isAuthorized(user, controller, action)
*
* @var mixed
* @access public
* @link http://book.cakephp.org/view/1275/authorize
*/
var $authorize = false;
/**
* The name of an optional view element to render when an Ajax request is made
* with an invalid or expired session
*
* @var string
* @access public
* @link http://book.cakephp.org/view/1277/ajaxLogin
*/
var $ajaxLogin = null;
/**
* The name of the element used for SessionComponent::setFlash
*
* @var string
* @access public
*/
var $flashElement = 'default';
/**
* The name of the model that represents users which will be authenticated. Defaults to 'User'.
*
* @var string
* @access public
* @link http://book.cakephp.org/view/1266/userModel
*/
var $userModel = 'User';
/**
* Additional query conditions to use when looking up and authenticating users,
* i.e. array('User.is_active' => 1).
*
* @var array
* @access public
* @link http://book.cakephp.org/view/1268/userScope
*/
var $userScope = array();
/**
* Allows you to specify non-default login name and password fields used in
* $userModel, i.e. array('username' => 'login_name', 'password' => 'passwd').
*
* @var array
* @access public
* @link http://book.cakephp.org/view/1267/fields
*/
var $fields = array('username' => 'username', 'password' => 'password');
/**
* The session key name where the record of the current user is stored. If
* unspecified, it will be "Auth.{$userModel name}".
*
* @var string
* @access public
* @link http://book.cakephp.org/view/1276/sessionKey
*/
var $sessionKey = null;
/**
* If using action-based access control, this defines how the paths to action
* ACO nodes is computed. If, for example, all controller nodes are nested
* under an ACO node named 'Controllers', $actionPath should be set to
* "Controllers/".
*
* @var string
* @access public
* @link http://book.cakephp.org/view/1279/actionPath
*/
var $actionPath = null;
/**
* A URL (defined as a string or array) to the controller action that handles
* logins.
*
* @var mixed
* @access public
* @link http://book.cakephp.org/view/1269/loginAction
*/
var $loginAction = null;
/**
* Normally, if a user is redirected to the $loginAction page, the location they
* were redirected from will be stored in the session so that they can be
* redirected back after a successful login. If this session value is not
* set, the user will be redirected to the page specified in $loginRedirect.
*
* @var mixed
* @access public
* @link http://book.cakephp.org/view/1270/loginRedirect
*/
var $loginRedirect = null;
/**
* The default action to redirect to after the user is logged out. While AuthComponent does
* not handle post-logout redirection, a redirect URL will be returned from AuthComponent::logout().
* Defaults to AuthComponent::$loginAction.
*
* @var mixed
* @access public
* @see AuthComponent::$loginAction
* @see AuthComponent::logout()
* @link http://book.cakephp.org/view/1271/logoutRedirect
*/
var $logoutRedirect = null;
/**
* The name of model or model object, or any other object has an isAuthorized method.
*
* @var string
* @access public
*/
var $object = null;
/**
* Error to display when user login fails. For security purposes, only one error is used for all
* login failures, so as not to expose information on why the login failed.
*
* @var string
* @access public
* @link http://book.cakephp.org/view/1272/loginError
*/
var $loginError = null;
/**
* Error to display when user attempts to access an object or action to which they do not have
* acccess.
*
* @var string
* @access public
* @link http://book.cakephp.org/view/1273/authError
*/
var $authError = null;
/**
* Determines whether AuthComponent will automatically redirect and exit if login is successful.
*
* @var boolean
* @access public
* @link http://book.cakephp.org/view/1274/autoRedirect
*/
var $autoRedirect = true;
/**
* Controller actions for which user validation is not required.
*
* @var array
* @access public
* @see AuthComponent::allow()
* @link http://book.cakephp.org/view/1251/Setting-Auth-Component-Variables
*/
var $allowedActions = array();
/**
* Maps actions to CRUD operations. Used for controller-based validation ($validate = 'controller').
*
* @var array
* @access public
* @see AuthComponent::mapActions()
*/
var $actionMap = array(
'index' => 'read',
'add' => 'create',
'edit' => 'update',
'view' => 'read',
'remove' => 'delete'
);
/**
* Form data from Controller::$data
*
* @var array
* @access public
*/
var $data = array();
/**
* Parameter data from Controller::$params
*
* @var array
* @access public
*/
var $params = array();
/**
* Method list for bound controller
*
* @var array
* @access protected
*/
var $_methods = array();
/**
* Initializes AuthComponent for use in the controller
*
* @param object $controller A reference to the instantiating controller object
* @return void
* @access public
*/
function initialize(&$controller, $settings = array()) {
$this->params = $controller->params;
$crud = array('create', 'read', 'update', 'delete');
$this->actionMap = array_merge($this->actionMap, array_combine($crud, $crud));
$this->_methods = $controller->methods;
$prefixes = Router::prefixes();
if (!empty($prefixes)) {
foreach ($prefixes as $prefix) {
$this->actionMap = array_merge($this->actionMap, array(
$prefix . '_index' => 'read',
$prefix . '_add' => 'create',
$prefix . '_edit' => 'update',
$prefix . '_view' => 'read',
$prefix . '_remove' => 'delete',
$prefix . '_create' => 'create',
$prefix . '_read' => 'read',
$prefix . '_update' => 'update',
$prefix . '_delete' => 'delete'
));
}
}
$this->_set($settings);
if (Configure::read() > 0) {
App::import('Debugger');
Debugger::checkSecurityKeys();
}
}
/**
* Main execution method. Handles redirecting of invalid users, and processing
* of login form data.
*
* @param object $controller A reference to the instantiating controller object
* @return boolean
* @access public
*/
function startup(&$controller) {
$isErrorOrTests = (
strtolower($controller->name) == 'cakeerror' ||
(strtolower($controller->name) == 'tests' && Configure::read() > 0)
);
if ($isErrorOrTests) {
return true;
}
$methods = array_flip($controller->methods);
$action = strtolower($controller->params['action']);
$isMissingAction = (
$controller->scaffold === false &&
!isset($methods[$action])
);
if ($isMissingAction) {
return true;
}
if (!$this->__setDefaults()) {
return false;
}
$this->data = $controller->data = $this->hashPasswords($controller->data);
$url = '';
if (isset($controller->params['url']['url'])) {
$url = $controller->params['url']['url'];
}
$url = Router::normalize($url);
$loginAction = Router::normalize($this->loginAction);
$allowedActions = array_map('strtolower', $this->allowedActions);
$isAllowed = (
$this->allowedActions == array('*') ||
in_array($action, $allowedActions)
);
if ($loginAction != $url && $isAllowed) {
return true;
}
if ($loginAction == $url) {
$model =& $this->getModel();
if (empty($controller->data) || !isset($controller->data[$model->alias])) {
if (!$this->Session->check('Auth.redirect') && !$this->loginRedirect && env('HTTP_REFERER')) {
$this->Session->write('Auth.redirect', $controller->referer(null, true));
}
return false;
}
$isValid = !empty($controller->data[$model->alias][$this->fields['username']]) &&
!empty($controller->data[$model->alias][$this->fields['password']]);
if ($isValid) {
$username = $controller->data[$model->alias][$this->fields['username']];
$password = $controller->data[$model->alias][$this->fields['password']];
$data = array(
$model->alias . '.' . $this->fields['username'] => $username,
$model->alias . '.' . $this->fields['password'] => $password
);
if ($this->login($data)) {
if ($this->autoRedirect) {
$controller->redirect($this->redirect(), null, true);
}
return true;
}
}
$this->Session->setFlash($this->loginError, $this->flashElement, array(), 'auth');
$controller->data[$model->alias][$this->fields['password']] = null;
return false;
} else {
if (!$this->user()) {
if (!$this->RequestHandler->isAjax()) {
$this->Session->setFlash($this->authError, $this->flashElement, array(), 'auth');
if (!empty($controller->params['url']) && count($controller->params['url']) >= 2) {
$query = $controller->params['url'];
unset($query['url'], $query['ext']);
$url .= Router::queryString($query, array());
}
$this->Session->write('Auth.redirect', $url);
$controller->redirect($loginAction);
return false;
} elseif (!empty($this->ajaxLogin)) {
$controller->viewPath = 'elements';
echo $controller->render($this->ajaxLogin, $this->RequestHandler->ajaxLayout);
$this->_stop();
return false;
} else {
$controller->redirect(null, 403);
}
}
}
if (!$this->authorize) {
return true;
}
extract($this->__authType());
switch ($type) {
case 'controller':
$this->object =& $controller;
break;
case 'crud':
case 'actions':
if (isset($controller->Acl)) {
$this->Acl =& $controller->Acl;
} else {
trigger_error(__('Could not find AclComponent. Please include Acl in Controller::$components.', true), E_USER_WARNING);
}
break;
case 'model':
if (!isset($object)) {
$hasModel = (
isset($controller->{$controller->modelClass}) &&
is_object($controller->{$controller->modelClass})
);
$isUses = (
!empty($controller->uses) && isset($controller->{$controller->uses[0]}) &&
is_object($controller->{$controller->uses[0]})
);
if ($hasModel) {
$object = $controller->modelClass;
} elseif ($isUses) {
$object = $controller->uses[0];
}
}
$type = array('model' => $object);
break;
}
if ($this->isAuthorized($type)) {
return true;
}
$this->Session->setFlash($this->authError, $this->flashElement, array(), 'auth');
$controller->redirect($controller->referer(), null, true);
return false;
}
/**
* Attempts to introspect the correct values for object properties including
* $userModel and $sessionKey.
*
* @param object $controller A reference to the instantiating controller object
* @return boolean
* @access private
*/
function __setDefaults() {
if (empty($this->userModel)) {
trigger_error(__("Could not find \$userModel. Please set AuthComponent::\$userModel in beforeFilter().", true), E_USER_WARNING);
return false;
}
list($plugin, $model) = pluginSplit($this->userModel);
$defaults = array(
'loginAction' => array(
'controller' => Inflector::underscore(Inflector::pluralize($model)),
'action' => 'login',
'plugin' => Inflector::underscore($plugin),
),
'sessionKey' => 'Auth.' . $model,
'logoutRedirect' => $this->loginAction,
'loginError' => __('Login failed. Invalid username or password.', true),
'authError' => __('You are not authorized to access that location.', true)
);
foreach ($defaults as $key => $value) {
if (empty($this->{$key})) {
$this->{$key} = $value;
}
}
return true;
}
/**
* Determines whether the given user is authorized to perform an action. The type of
* authorization used is based on the value of AuthComponent::$authorize or the
* passed $type param.
*
* Types:
* 'controller' will validate against Controller::isAuthorized() if controller instance is
* passed in $object
* 'actions' will validate Controller::action against an AclComponent::check()
* 'crud' will validate mapActions against an AclComponent::check()
* array('model'=> 'name'); will validate mapActions against model
* $name::isAuthorized(user, controller, mapAction)
* 'object' will validate Controller::action against
* object::isAuthorized(user, controller, action)
*
* @param string $type Type of authorization
* @param mixed $object object, model object, or model name
* @param mixed $user The user to check the authorization of
* @return boolean True if $user is authorized, otherwise false
* @access public
*/
function isAuthorized($type = null, $object = null, $user = null) {
if (empty($user) && !$this->user()) {
return false;
} elseif (empty($user)) {
$user = $this->user();
}
extract($this->__authType($type));
if (!$object) {
$object = $this->object;
}
$valid = false;
switch ($type) {
case 'controller':
$valid = $object->isAuthorized();
break;
case 'actions':
$valid = $this->Acl->check($user, $this->action());
break;
case 'crud':
if (!isset($this->actionMap[$this->params['action']])) {
trigger_error(
sprintf(__('Auth::startup() - Attempted access of un-mapped action "%1$s" in controller "%2$s"', true), $this->params['action'], $this->params['controller']),
E_USER_WARNING
);
} else {
$valid = $this->Acl->check(
$user,
$this->action(':controller'),
$this->actionMap[$this->params['action']]
);
}
break;
case 'model':
$action = $this->params['action'];
if (isset($this->actionMap[$action])) {
$action = $this->actionMap[$action];
}
if (is_string($object)) {
$object = $this->getModel($object);
}
case 'object':
if (!isset($action)) {
$action = $this->action(':action');
}
if (empty($object)) {
trigger_error(sprintf(__('Could not find %s. Set AuthComponent::$object in beforeFilter() or pass a valid object', true), get_class($object)), E_USER_WARNING);
return;
}
if (method_exists($object, 'isAuthorized')) {
$valid = $object->isAuthorized($user, $this->action(':controller'), $action);
} elseif ($object) {
trigger_error(sprintf(__('%s::isAuthorized() is not defined.', true), get_class($object)), E_USER_WARNING);
}
break;
case null:
case false:
return true;
break;
default:
trigger_error(__('Auth::isAuthorized() - $authorize is set to an incorrect value. Allowed settings are: "actions", "crud", "model" or null.', true), E_USER_WARNING);
break;
}
return $valid;
}
/**
* Get authorization type
*
* @param string $auth Type of authorization
* @return array Associative array with: type, object
* @access private
*/
function __authType($auth = null) {
if ($auth == null) {
$auth = $this->authorize;
}
$object = null;
if (is_array($auth)) {
$type = key($auth);
$object = $auth[$type];
} else {
$type = $auth;
return compact('type');
}
return compact('type', 'object');
}
/**
* Takes a list of actions in the current controller for which authentication is not required, or
* no parameters to allow all actions.
*
* @param mixed $action Controller action name or array of actions
* @param string $action Controller action name
* @param string ... etc.
* @return void
* @access public
* @link http://book.cakephp.org/view/1257/allow
*/
function allow() {
$args = func_get_args();
if (empty($args) || $args == array('*')) {
$this->allowedActions = $this->_methods;
} else {
if (isset($args[0]) && is_array($args[0])) {
$args = $args[0];
}
$this->allowedActions = array_merge($this->allowedActions, array_map('strtolower', $args));
}
}
/**
* Removes items from the list of allowed actions.
*
* @param mixed $action Controller action name or array of actions
* @param string $action Controller action name
* @param string ... etc.
* @return void
* @see AuthComponent::allow()
* @access public
* @link http://book.cakephp.org/view/1258/deny
*/
function deny() {
$args = func_get_args();
if (isset($args[0]) && is_array($args[0])) {
$args = $args[0];
}
foreach ($args as $arg) {
$i = array_search(strtolower($arg), $this->allowedActions);
if (is_int($i)) {
unset($this->allowedActions[$i]);
}
}
$this->allowedActions = array_values($this->allowedActions);
}
/**
* Maps action names to CRUD operations. Used for controller-based authentication.
*
* @param array $map Actions to map
* @return void
* @access public
* @link http://book.cakephp.org/view/1260/mapActions
*/
function mapActions($map = array()) {
$crud = array('create', 'read', 'update', 'delete');
foreach ($map as $action => $type) {
if (in_array($action, $crud) && is_array($type)) {
foreach ($type as $typedAction) {
$this->actionMap[$typedAction] = $action;
}
} else {
$this->actionMap[$action] = $type;
}
}
}
/**
* Manually log-in a user with the given parameter data. The $data provided can be any data
* structure used to identify a user in AuthComponent::identify(). If $data is empty or not
* specified, POST data from Controller::$data will be used automatically.
*
* After (if) login is successful, the user record is written to the session key specified in
* AuthComponent::$sessionKey.
*
* @param mixed $data User object
* @return boolean True on login success, false on failure
* @access public
* @link http://book.cakephp.org/view/1261/login
*/
function login($data = null) {
$this->__setDefaults();
$this->_loggedIn = false;
if (empty($data)) {
$data = $this->data;
}
if ($user = $this->identify($data)) {
$this->Session->write($this->sessionKey, $user);
$this->_loggedIn = true;
}
return $this->_loggedIn;
}
/**
* Logs a user out, and returns the login action to redirect to.
*
* @param mixed $url Optional URL to redirect the user to after logout
* @return string AuthComponent::$loginAction
* @see AuthComponent::$loginAction
* @access public
* @link http://book.cakephp.org/view/1262/logout
*/
function logout() {
$this->__setDefaults();
$this->Session->delete($this->sessionKey);
$this->Session->delete('Auth.redirect');
$this->_loggedIn = false;
return Router::normalize($this->logoutRedirect);
}
/**
* Get the current user from the session.
*
* @param string $key field to retrive. Leave null to get entire User record
* @return mixed User record. or null if no user is logged in.
* @access public
* @link http://book.cakephp.org/view/1264/user
*/
function user($key = null) {
$this->__setDefaults();
if (!$this->Session->check($this->sessionKey)) {
return null;
}
if ($key == null) {
$model =& $this->getModel();
return array($model->alias => $this->Session->read($this->sessionKey));
} else {
$user = $this->Session->read($this->sessionKey);
if (isset($user[$key])) {
return $user[$key];
}
return null;
}
}
/**
* If no parameter is passed, gets the authentication redirect URL.
*
* @param mixed $url Optional URL to write as the login redirect URL.
* @return string Redirect URL
* @access public
*/
function redirect($url = null) {
if (!is_null($url)) {
$redir = $url;
$this->Session->write('Auth.redirect', $redir);
} elseif ($this->Session->check('Auth.redirect')) {
$redir = $this->Session->read('Auth.redirect');
$this->Session->delete('Auth.redirect');
if (Router::normalize($redir) == Router::normalize($this->loginAction)) {
$redir = $this->loginRedirect;
}
} else {
$redir = $this->loginRedirect;
}
return Router::normalize($redir);
}
/**
* Validates a user against an abstract object.
*
* @param mixed $object The object to validate the user against.
* @param mixed $user Optional. The identity of the user to be validated.
* Uses the current user session if none specified. For
* valid forms of identifying users, see
* AuthComponent::identify().
* @param string $action Optional. The action to validate against.
* @see AuthComponent::identify()
* @return boolean True if the user validates, false otherwise.
* @access public
*/
function validate($object, $user = null, $action = null) {
if (empty($user)) {
$user = $this->user();
}
if (empty($user)) {
return false;
}
return $this->Acl->check($user, $object, $action);
}
/**
* Returns the path to the ACO node bound to a controller/action.
*
* @param string $action Optional. The controller/action path to validate the
* user against. The current request action is used if
* none is specified.
* @return boolean ACO node path
* @access public
* @link http://book.cakephp.org/view/1256/action
*/
function action($action = ':plugin/:controller/:action') {
$plugin = empty($this->params['plugin']) ? null : Inflector::camelize($this->params['plugin']) . '/';
return str_replace(
array(':controller', ':action', ':plugin/'),
array(Inflector::camelize($this->params['controller']), $this->params['action'], $plugin),
$this->actionPath . $action
);
}
/**
* Returns a reference to the model object specified, and attempts
* to load it if it is not found.
*
* @param string $name Model name (defaults to AuthComponent::$userModel)
* @return object A reference to a model object
* @access public
*/
function &getModel($name = null) {
$model = null;
if (!$name) {
$name = $this->userModel;
}
if (PHP5) {
$model = ClassRegistry::init($name);
} else {
$model =& ClassRegistry::init($name);
}
if (empty($model)) {
trigger_error(__('Auth::getModel() - Model is not set or could not be found', true), E_USER_WARNING);
return null;
}
return $model;
}
/**
* Identifies a user based on specific criteria.
*
* @param mixed $user Optional. The identity of the user to be validated.
* Uses the current user session if none specified.
* @param array $conditions Optional. Additional conditions to a find.
* @return array User record data, or null, if the user could not be identified.
* @access public
*/
function identify($user = null, $conditions = null) {
if ($conditions === false) {
$conditions = null;
} elseif (is_array($conditions)) {
$conditions = array_merge((array)$this->userScope, $conditions);
} else {
$conditions = $this->userScope;
}
$model =& $this->getModel();
if (empty($user)) {
$user = $this->user();
if (empty($user)) {
return null;
}
} elseif (is_object($user) && is_a($user, 'Model')) {
if (!$user->exists()) {
return null;
}
$user = $user->read();
$user = $user[$model->alias];
} elseif (is_array($user) && isset($user[$model->alias])) {
$user = $user[$model->alias];
}
if (is_array($user) && (isset($user[$this->fields['username']]) || isset($user[$model->alias . '.' . $this->fields['username']]))) {
if (isset($user[$this->fields['username']]) && !empty($user[$this->fields['username']]) && !empty($user[$this->fields['password']])) {
if (trim($user[$this->fields['username']]) == '=' || trim($user[$this->fields['password']]) == '=') {
return false;
}
$find = array(
$model->alias.'.'.$this->fields['username'] => $user[$this->fields['username']],
$model->alias.'.'.$this->fields['password'] => $user[$this->fields['password']]
);
} elseif (isset($user[$model->alias . '.' . $this->fields['username']]) && !empty($user[$model->alias . '.' . $this->fields['username']])) {
if (trim($user[$model->alias . '.' . $this->fields['username']]) == '=' || trim($user[$model->alias . '.' . $this->fields['password']]) == '=') {
return false;
}
$find = array(
$model->alias.'.'.$this->fields['username'] => $user[$model->alias . '.' . $this->fields['username']],
$model->alias.'.'.$this->fields['password'] => $user[$model->alias . '.' . $this->fields['password']]
);
} else {
return false;
}
$data = $model->find('first', array(
'conditions' => array_merge($find, $conditions),
'recursive' => 0
));
if (empty($data) || empty($data[$model->alias])) {
return null;
}
} elseif (!empty($user) && is_string($user)) {
$data = $model->find('first', array(
'conditions' => array_merge(array($model->escapeField() => $user), $conditions),
));
if (empty($data) || empty($data[$model->alias])) {
return null;
}
}
if (!empty($data)) {
if (!empty($data[$model->alias][$this->fields['password']])) {
unset($data[$model->alias][$this->fields['password']]);
}
return $data[$model->alias];
}
return null;
}
/**
* Hash any passwords found in $data using $userModel and $fields['password']
*
* @param array $data Set of data to look for passwords
* @return array Data with passwords hashed
* @access public
* @link http://book.cakephp.org/view/1259/hashPasswords
*/
function hashPasswords($data) {
if (is_object($this->authenticate) && method_exists($this->authenticate, 'hashPasswords')) {
return $this->authenticate->hashPasswords($data);
}
if (is_array($data)) {
$model =& $this->getModel();
if(isset($data[$model->alias])) {
if (isset($data[$model->alias][$this->fields['username']]) && isset($data[$model->alias][$this->fields['password']])) {
$data[$model->alias][$this->fields['password']] = $this->password($data[$model->alias][$this->fields['password']]);
}
}
}
return $data;
}
/**
* Hash a password with the application's salt value (as defined with Configure::write('Security.salt');
*
* @param string $password Password to hash
* @return string Hashed password
* @access public
* @link http://book.cakephp.org/view/1263/password
*/
function password($password) {
return Security::hash($password, null, true);
}
/**
* Component shutdown. If user is logged in, wipe out redirect.
*
* @param object $controller Instantiating controller
* @access public
*/
function shutdown(&$controller) {
if ($this->_loggedIn) {
$this->Session->delete('Auth.redirect');
}
}
}

View File

@@ -0,0 +1,471 @@
<?php
/**
* Cookie Component
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.controller.components
* @since CakePHP(tm) v 1.2.0.4213
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Load Security class
*/
App::import('Core', 'Security');
/**
* Cookie Component.
*
* Cookie handling for the controller.
*
* @package cake
* @subpackage cake.cake.libs.controller.components
* @link http://book.cakephp.org/view/1280/Cookies
*
*/
class CookieComponent extends Object {
/**
* The name of the cookie.
*
* Overridden with the controller beforeFilter();
* $this->Cookie->name = 'CookieName';
*
* @var string
* @access public
*/
var $name = 'CakeCookie';
/**
* The time a cookie will remain valid.
*
* Can be either integer Unix timestamp or a date string.
*
* Overridden with the controller beforeFilter();
* $this->Cookie->time = '5 Days';
*
* @var mixed
* @access public
*/
var $time = null;
/**
* Cookie path.
*
* Overridden with the controller beforeFilter();
* $this->Cookie->path = '/';
*
* The path on the server in which the cookie will be available on.
* If var $cookiePath is set to '/foo/', the cookie will only be available
* within the /foo/ directory and all sub-directories such as /foo/bar/ of domain.
* The default value is the entire domain.
*
* @var string
* @access public
*/
var $path = '/';
/**
* Domain path.
*
* The domain that the cookie is available.
*
* Overridden with the controller beforeFilter();
* $this->Cookie->domain = '.example.com';
*
* To make the cookie available on all subdomains of example.com.
* Set $this->Cookie->domain = '.example.com'; in your controller beforeFilter
*
* @var string
* @access public
*/
var $domain = '';
/**
* Secure HTTPS only cookie.
*
* Overridden with the controller beforeFilter();
* $this->Cookie->secure = true;
*
* Indicates that the cookie should only be transmitted over a secure HTTPS connection.
* When set to true, the cookie will only be set if a secure connection exists.
*
* @var boolean
* @access public
*/
var $secure = false;
/**
* Encryption key.
*
* Overridden with the controller beforeFilter();
* $this->Cookie->key = 'SomeRandomString';
*
* @var string
* @access protected
*/
var $key = null;
/**
* Values stored in the cookie.
*
* Accessed in the controller using $this->Cookie->read('Name.key');
*
* @see CookieComponent::read();
* @var string
* @access private
*/
var $__values = array();
/**
* Type of encryption to use.
*
* Currently only one method is available
* Defaults to Security::cipher();
*
* @var string
* @access private
* @todo add additional encryption methods
*/
var $__type = 'cipher';
/**
* Used to reset cookie time if $expire is passed to CookieComponent::write()
*
* @var string
* @access private
*/
var $__reset = null;
/**
* Expire time of the cookie
*
* This is controlled by CookieComponent::time;
*
* @var string
* @access private
*/
var $__expires = 0;
/**
* Main execution method.
*
* @param object $controller A reference to the instantiating controller object
* @access public
*/
function initialize(&$controller, $settings) {
$this->key = Configure::read('Security.salt');
$this->_set($settings);
}
/**
* Start CookieComponent for use in the controller
*
* @access public
*/
function startup() {
$this->__expire($this->time);
if (isset($_COOKIE[$this->name])) {
$this->__values = $this->__decrypt($_COOKIE[$this->name]);
}
}
/**
* Write a value to the $_COOKIE[$key];
*
* Optional [Name.], reguired key, optional $value, optional $encrypt, optional $expires
* $this->Cookie->write('[Name.]key, $value);
*
* By default all values are encrypted.
* You must pass $encrypt false to store values in clear test
*
* You must use this method before any output is sent to the browser.
* Failure to do so will result in header already sent errors.
*
* @param mixed $key Key for the value
* @param mixed $value Value
* @param boolean $encrypt Set to true to encrypt value, false otherwise
* @param string $expires Can be either Unix timestamp, or date string
* @access public
*/
function write($key, $value = null, $encrypt = true, $expires = null) {
if (is_null($encrypt)) {
$encrypt = true;
}
$this->__encrypted = $encrypt;
$this->__expire($expires);
if (!is_array($key)) {
$key = array($key => $value);
}
foreach ($key as $name => $value) {
if (strpos($name, '.') === false) {
$this->__values[$name] = $value;
$this->__write("[$name]", $value);
} else {
$names = explode('.', $name, 2);
if (!isset($this->__values[$names[0]])) {
$this->__values[$names[0]] = array();
}
$this->__values[$names[0]] = Set::insert($this->__values[$names[0]], $names[1], $value);
$this->__write('[' . implode('][', $names) . ']', $value);
}
}
$this->__encrypted = true;
}
/**
* Read the value of the $_COOKIE[$key];
*
* Optional [Name.], reguired key
* $this->Cookie->read(Name.key);
*
* @param mixed $key Key of the value to be obtained. If none specified, obtain map key => values
* @return string or null, value for specified key
* @access public
*/
function read($key = null) {
if (empty($this->__values) && isset($_COOKIE[$this->name])) {
$this->__values = $this->__decrypt($_COOKIE[$this->name]);
}
if (is_null($key)) {
return $this->__values;
}
if (strpos($key, '.') !== false) {
$names = explode('.', $key, 2);
$key = $names[0];
}
if (!isset($this->__values[$key])) {
return null;
}
if (!empty($names[1])) {
return Set::extract($this->__values[$key], $names[1]);
}
return $this->__values[$key];
}
/**
* Delete a cookie value
*
* Optional [Name.], reguired key
* $this->Cookie->read('Name.key);
*
* You must use this method before any output is sent to the browser.
* Failure to do so will result in header already sent errors.
*
* @param string $key Key of the value to be deleted
* @return void
* @access public
*/
function delete($key) {
if (empty($this->__values)) {
$this->read();
}
if (strpos($key, '.') === false) {
unset($this->__values[$key]);
$this->__delete("[$key]");
return;
}
$names = explode('.', $key, 2);
$this->__values[$names[0]] = Set::remove($this->__values[$names[0]], $names[1]);
$this->__delete('[' . implode('][', $names) . ']');
}
/**
* Destroy current cookie
*
* You must use this method before any output is sent to the browser.
* Failure to do so will result in header already sent errors.
*
* @return void
* @access public
*/
function destroy() {
if (isset($_COOKIE[$this->name])) {
$this->__values = $this->__decrypt($_COOKIE[$this->name]);
}
foreach ($this->__values as $name => $value) {
if (is_array($value)) {
foreach ($value as $key => $val) {
unset($this->__values[$name][$key]);
$this->__delete("[$name][$key]");
}
}
unset($this->__values[$name]);
$this->__delete("[$name]");
}
}
/**
* Will allow overriding default encryption method.
*
* @param string $type Encryption method
* @access public
* @todo NOT IMPLEMENTED
*/
function type($type = 'cipher') {
$this->__type = 'cipher';
}
/**
* Set the expire time for a session variable.
*
* Creates a new expire time for a session variable.
* $expire can be either integer Unix timestamp or a date string.
*
* Used by write()
* CookieComponent::write(string, string, boolean, 8400);
* CookieComponent::write(string, string, boolean, '5 Days');
*
* @param mixed $expires Can be either Unix timestamp, or date string
* @return int Unix timestamp
* @access private
*/
function __expire($expires = null) {
$now = time();
if (is_null($expires)) {
return $this->__expires;
}
$this->__reset = $this->__expires;
if ($expires == 0) {
return $this->__expires = 0;
}
if (is_integer($expires) || is_numeric($expires)) {
return $this->__expires = $now + intval($expires);
}
return $this->__expires = strtotime($expires, $now);
}
/**
* Set cookie
*
* @param string $name Name for cookie
* @param string $value Value for cookie
* @access private
*/
function __write($name, $value) {
setcookie($this->name . $name, $this->__encrypt($value), $this->__expires, $this->path, $this->domain, $this->secure);
if (!is_null($this->__reset)) {
$this->__expires = $this->__reset;
$this->__reset = null;
}
}
/**
* Sets a cookie expire time to remove cookie value
*
* @param string $name Name of cookie
* @access private
*/
function __delete($name) {
setcookie($this->name . $name, '', time() - 42000, $this->path, $this->domain, $this->secure);
}
/**
* Encrypts $value using var $type method in Security class
*
* @param string $value Value to encrypt
* @return string encrypted string
* @access private
*/
function __encrypt($value) {
if (is_array($value)) {
$value = $this->__implode($value);
}
if ($this->__encrypted === true) {
$type = $this->__type;
$value = "Q2FrZQ==." .base64_encode(Security::$type($value, $this->key));
}
return $value;
}
/**
* Decrypts $value using var $type method in Security class
*
* @param array $values Values to decrypt
* @return string decrypted string
* @access private
*/
function __decrypt($values) {
$decrypted = array();
$type = $this->__type;
foreach ($values as $name => $value) {
if (is_array($value)) {
foreach ($value as $key => $val) {
$pos = strpos($val, 'Q2FrZQ==.');
$decrypted[$name][$key] = $this->__explode($val);
if ($pos !== false) {
$val = substr($val, 8);
$decrypted[$name][$key] = $this->__explode(Security::$type(base64_decode($val), $this->key));
}
}
} else {
$pos = strpos($value, 'Q2FrZQ==.');
$decrypted[$name] = $this->__explode($value);
if ($pos !== false) {
$value = substr($value, 8);
$decrypted[$name] = $this->__explode(Security::$type(base64_decode($value), $this->key));
}
}
}
return $decrypted;
}
/**
* Implode method to keep keys are multidimensional arrays
*
* @param array $array Map of key and values
* @return string String in the form key1|value1,key2|value2
* @access private
*/
function __implode($array) {
$string = '';
foreach ($array as $key => $value) {
$string .= ',' . $key . '|' . $value;
}
return substr($string, 1);
}
/**
* Explode method to return array from string set in CookieComponent::__implode()
*
* @param string $string String in the form key1|value1,key2|value2
* @return array Map of key and values
* @access private
*/
function __explode($string) {
$array = array();
foreach (explode(',', $string) as $pair) {
$key = explode('|', $pair);
if (!isset($key[1])) {
return $key[0];
}
$array[$key[0]] = $key[1];
}
return $array;
}
}

View File

@@ -0,0 +1,962 @@
<?php
/**
* Email Component
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.controller.components
* @since CakePHP(tm) v 1.2.0.3467
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Core', 'Multibyte');
/**
* EmailComponent
*
* This component is used for handling Internet Message Format based
* based on the standard outlined in http://www.rfc-editor.org/rfc/rfc2822.txt
*
* @package cake
* @subpackage cake.cake.libs.controller.components
* @link http://book.cakephp.org/view/1283/Email
*
*/
class EmailComponent extends Object{
/**
* Recipient of the email
*
* @var string
* @access public
*/
var $to = null;
/**
* The mail which the email is sent from
*
* @var string
* @access public
*/
var $from = null;
/**
* The email the recipient will reply to
*
* @var string
* @access public
*/
var $replyTo = null;
/**
* The read receipt email
*
* @var string
* @access public
*/
var $readReceipt = null;
/**
* The mail that will be used in case of any errors like
* - Remote mailserver down
* - Remote user has exceeded his quota
* - Unknown user
*
* @var string
* @access public
*/
var $return = null;
/**
* Carbon Copy
*
* List of email's that should receive a copy of the email.
* The Recipient WILL be able to see this list
*
* @var array
* @access public
*/
var $cc = array();
/**
* Blind Carbon Copy
*
* List of email's that should receive a copy of the email.
* The Recipient WILL NOT be able to see this list
*
* @var array
* @access public
*/
var $bcc = array();
/**
* The subject of the email
*
* @var string
* @access public
*/
var $subject = null;
/**
* Associative array of a user defined headers
* Keys will be prefixed 'X-' as per RFC2822 Section 4.7.5
*
* @var array
* @access public
*/
var $headers = array();
/**
* List of additional headers
*
* These will NOT be used if you are using safemode and mail()
*
* @var string
* @access public
*/
var $additionalParams = null;
/**
* Layout for the View
*
* @var string
* @access public
*/
var $layout = 'default';
/**
* Template for the view
*
* @var string
* @access public
*/
var $template = null;
/**
* as per RFC2822 Section 2.1.1
*
* @var integer
* @access public
*/
var $lineLength = 70;
/**
* @deprecated see lineLength
*/
var $_lineLength = null;
/**
* What format should the email be sent in
*
* Supported formats:
* - text
* - html
* - both
*
* @var string
* @access public
*/
var $sendAs = 'text';
/**
* What method should the email be sent by
*
* Supported methods:
* - mail
* - smtp
* - debug
*
* @var string
* @access public
*/
var $delivery = 'mail';
/**
* charset the email is sent in
*
* @var string
* @access public
*/
var $charset = 'utf-8';
/**
* List of files that should be attached to the email.
*
* Can be both absolute and relative paths
*
* @var array
* @access public
*/
var $attachments = array();
/**
* What mailer should EmailComponent identify itself as
*
* @var string
* @access public
*/
var $xMailer = 'CakePHP Email Component';
/**
* The list of paths to search if an attachment isnt absolute
*
* @var array
* @access public
*/
var $filePaths = array();
/**
* List of options to use for smtp mail method
*
* Options is:
* - port
* - host
* - timeout
* - username
* - password
* - client
*
* @var array
* @access public
* @link http://book.cakephp.org/view/1290/Sending-A-Message-Using-SMTP
*/
var $smtpOptions = array();
/**
* Placeholder for any errors that might happen with the
* smtp mail methods
*
* @var string
* @access public
*/
var $smtpError = null;
/**
* Contains the rendered plain text message if one was sent.
*
* @var string
* @access public
*/
var $textMessage = null;
/**
* Contains the rendered HTML message if one was sent.
*
* @var string
* @access public
*/
var $htmlMessage = null;
/**
* Whether to generate a Message-ID header for the
* e-mail. True to generate a Message-ID, False to let
* it be handled by sendmail (or similar) or a string
* to completely override the Message-ID.
*
* @var mixed
* @access public
*/
var $messageId = true;
/**
* Temporary store of message header lines
*
* @var array
* @access private
*/
var $__header = array();
/**
* If set, boundary to use for multipart mime messages
*
* @var string
* @access private
*/
var $__boundary = null;
/**
* Temporary store of message lines
*
* @var array
* @access private
*/
var $__message = array();
/**
* Variable that holds SMTP connection
*
* @var resource
* @access private
*/
var $__smtpConnection = null;
/**
* Initialize component
*
* @param object $controller Instantiating controller
* @access public
*/
function initialize(&$controller, $settings = array()) {
$this->Controller =& $controller;
if (Configure::read('App.encoding') !== null) {
$this->charset = Configure::read('App.encoding');
}
$this->_set($settings);
}
/**
* Startup component
*
* @param object $controller Instantiating controller
* @access public
*/
function startup(&$controller) {}
/**
* Send an email using the specified content, template and layout
*
* @param mixed $content Either an array of text lines, or a string with contents
* If you are rendering a template this variable will be sent to the templates as `$content`
* @param string $template Template to use when sending email
* @param string $layout Layout to use to enclose email body
* @return boolean Success
* @access public
*/
function send($content = null, $template = null, $layout = null) {
$this->_createHeader();
if ($template) {
$this->template = $template;
}
if ($layout) {
$this->layout = $layout;
}
if (is_array($content)) {
$content = implode("\n", $content) . "\n";
}
$this->htmlMessage = $this->textMessage = null;
if ($content) {
if ($this->sendAs === 'html') {
$this->htmlMessage = $content;
} elseif ($this->sendAs === 'text') {
$this->textMessage = $content;
} else {
$this->htmlMessage = $this->textMessage = $content;
}
}
if ($this->sendAs === 'text') {
$message = $this->_wrap($content);
} else {
$message = $this->_wrap($content, 998);
}
if ($this->template === null) {
$message = $this->_formatMessage($message);
} else {
$message = $this->_render($message);
}
$message[] = '';
$this->__message = $message;
if (!empty($this->attachments)) {
$this->_attachFiles();
}
if (!is_null($this->__boundary)) {
$this->__message[] = '';
$this->__message[] = '--' . $this->__boundary . '--';
$this->__message[] = '';
}
$_method = '_' . $this->delivery;
$sent = $this->$_method();
$this->__header = array();
$this->__message = array();
return $sent;
}
/**
* Reset all EmailComponent internal variables to be able to send out a new email.
*
* @access public
* @link http://book.cakephp.org/view/1285/Sending-Multiple-Emails-in-a-loop
*/
function reset() {
$this->template = null;
$this->to = array();
$this->from = null;
$this->replyTo = null;
$this->return = null;
$this->cc = array();
$this->bcc = array();
$this->subject = null;
$this->additionalParams = null;
$this->smtpError = null;
$this->attachments = array();
$this->htmlMessage = null;
$this->textMessage = null;
$this->messageId = true;
$this->__header = array();
$this->__boundary = null;
$this->__message = array();
}
/**
* Render the contents using the current layout and template.
*
* @param string $content Content to render
* @return array Email ready to be sent
* @access private
*/
function _render($content) {
$viewClass = $this->Controller->view;
if ($viewClass != 'View') {
list($plugin, $viewClass) = pluginSplit($viewClass);
$viewClass = $viewClass . 'View';
App::import('View', $this->Controller->view);
}
$View = new $viewClass($this->Controller, false);
$View->layout = $this->layout;
$msg = array();
$content = implode("\n", $content);
if ($this->sendAs === 'both') {
$htmlContent = $content;
if (!empty($this->attachments)) {
$msg[] = '--' . $this->__boundary;
$msg[] = 'Content-Type: multipart/alternative; boundary="alt-' . $this->__boundary . '"';
$msg[] = '';
}
$msg[] = '--alt-' . $this->__boundary;
$msg[] = 'Content-Type: text/plain; charset=' . $this->charset;
$msg[] = 'Content-Transfer-Encoding: 7bit';
$msg[] = '';
$content = $View->element('email' . DS . 'text' . DS . $this->template, array('content' => $content), true);
$View->layoutPath = 'email' . DS . 'text';
$content = explode("\n", $this->textMessage = str_replace(array("\r\n", "\r"), "\n", $View->renderLayout($content)));
$msg = array_merge($msg, $content);
$msg[] = '';
$msg[] = '--alt-' . $this->__boundary;
$msg[] = 'Content-Type: text/html; charset=' . $this->charset;
$msg[] = 'Content-Transfer-Encoding: 7bit';
$msg[] = '';
$htmlContent = $View->element('email' . DS . 'html' . DS . $this->template, array('content' => $htmlContent), true);
$View->layoutPath = 'email' . DS . 'html';
$htmlContent = explode("\n", $this->htmlMessage = str_replace(array("\r\n", "\r"), "\n", $View->renderLayout($htmlContent)));
$msg = array_merge($msg, $htmlContent);
$msg[] = '';
$msg[] = '--alt-' . $this->__boundary . '--';
$msg[] = '';
return $msg;
}
if (!empty($this->attachments)) {
if ($this->sendAs === 'html') {
$msg[] = '';
$msg[] = '--' . $this->__boundary;
$msg[] = 'Content-Type: text/html; charset=' . $this->charset;
$msg[] = 'Content-Transfer-Encoding: 7bit';
$msg[] = '';
} else {
$msg[] = '--' . $this->__boundary;
$msg[] = 'Content-Type: text/plain; charset=' . $this->charset;
$msg[] = 'Content-Transfer-Encoding: 7bit';
$msg[] = '';
}
}
$content = $View->element('email' . DS . $this->sendAs . DS . $this->template, array('content' => $content), true);
$View->layoutPath = 'email' . DS . $this->sendAs;
$content = explode("\n", $rendered = str_replace(array("\r\n", "\r"), "\n", $View->renderLayout($content)));
if ($this->sendAs === 'html') {
$this->htmlMessage = $rendered;
} else {
$this->textMessage = $rendered;
}
$msg = array_merge($msg, $content);
return $msg;
}
/**
* Create unique boundary identifier
*
* @access private
*/
function _createboundary() {
$this->__boundary = md5(uniqid(time()));
}
/**
* Sets headers for the message
*
* @access public
* @param array Associative array containing headers to be set.
*/
function header($headers) {
foreach ($headers as $header => $value) {
$this->__header[] = sprintf('%s: %s', trim($header), trim($value));
}
}
/**
* Create emails headers including (but not limited to) from email address, reply to,
* bcc and cc.
*
* @access private
*/
function _createHeader() {
$headers = array();
if ($this->delivery == 'smtp') {
if (is_array($this->to)) {
$headers['To'] = implode(', ', array_map(array($this, '_formatAddress'), $this->to));
} else {
$headers['To'] = $this->_formatAddress($this->to);
}
}
$headers['From'] = $this->_formatAddress($this->from);
if (!empty($this->replyTo)) {
$headers['Reply-To'] = $this->_formatAddress($this->replyTo);
}
if (!empty($this->return)) {
$headers['Return-Path'] = $this->_formatAddress($this->return);
}
if (!empty($this->readReceipt)) {
$headers['Disposition-Notification-To'] = $this->_formatAddress($this->readReceipt);
}
if (!empty($this->cc)) {
$headers['cc'] = implode(', ', array_map(array($this, '_formatAddress'), $this->cc));
}
if (!empty($this->bcc) && $this->delivery != 'smtp') {
$headers['Bcc'] = implode(', ', array_map(array($this, '_formatAddress'), $this->bcc));
}
if ($this->delivery == 'smtp') {
$headers['Subject'] = $this->_encode($this->subject);
}
if ($this->messageId !== false) {
if ($this->messageId === true) {
$headers['Message-ID'] = '<' . String::UUID() . '@' . env('HTTP_HOST') . '>';
} else {
$headers['Message-ID'] = $this->messageId;
}
}
$headers['X-Mailer'] = $this->xMailer;
if (!empty($this->headers)) {
foreach ($this->headers as $key => $val) {
$headers['X-' . $key] = $val;
}
}
if (!empty($this->attachments)) {
$this->_createBoundary();
$headers['MIME-Version'] = '1.0';
$headers['Content-Type'] = 'multipart/mixed; boundary="' . $this->__boundary . '"';
$headers[] = 'This part of the E-mail should never be seen. If';
$headers[] = 'you are reading this, consider upgrading your e-mail';
$headers[] = 'client to a MIME-compatible client.';
} elseif ($this->sendAs === 'text') {
$headers['Content-Type'] = 'text/plain; charset=' . $this->charset;
} elseif ($this->sendAs === 'html') {
$headers['Content-Type'] = 'text/html; charset=' . $this->charset;
} elseif ($this->sendAs === 'both') {
$headers['Content-Type'] = 'multipart/alternative; boundary="alt-' . $this->__boundary . '"';
}
$headers['Content-Transfer-Encoding'] = '7bit';
$this->header($headers);
}
/**
* Format the message by seeing if it has attachments.
*
* @param string $message Message to format
* @access private
*/
function _formatMessage($message) {
if (!empty($this->attachments)) {
$prefix = array('--' . $this->__boundary);
if ($this->sendAs === 'text') {
$prefix[] = 'Content-Type: text/plain; charset=' . $this->charset;
} elseif ($this->sendAs === 'html') {
$prefix[] = 'Content-Type: text/html; charset=' . $this->charset;
} elseif ($this->sendAs === 'both') {
$prefix[] = 'Content-Type: multipart/alternative; boundary="alt-' . $this->__boundary . '"';
}
$prefix[] = 'Content-Transfer-Encoding: 7bit';
$prefix[] = '';
$message = array_merge($prefix, $message);
}
return $message;
}
/**
* Attach files by adding file contents inside boundaries.
*
* @access private
* @TODO: modify to use the core File class?
*/
function _attachFiles() {
$files = array();
foreach ($this->attachments as $filename => $attachment) {
$file = $this->_findFiles($attachment);
if (!empty($file)) {
if (is_int($filename)) {
$filename = basename($file);
}
$files[$filename] = $file;
}
}
foreach ($files as $filename => $file) {
$handle = fopen($file, 'rb');
$data = fread($handle, filesize($file));
$data = chunk_split(base64_encode($data)) ;
fclose($handle);
$this->__message[] = '--' . $this->__boundary;
$this->__message[] = 'Content-Type: application/octet-stream';
$this->__message[] = 'Content-Transfer-Encoding: base64';
$this->__message[] = 'Content-Disposition: attachment; filename="' . basename($filename) . '"';
$this->__message[] = '';
$this->__message[] = $data;
$this->__message[] = '';
}
}
/**
* Find the specified attachment in the list of file paths
*
* @param string $attachment Attachment file name to find
* @return string Path to located file
* @access private
*/
function _findFiles($attachment) {
if (file_exists($attachment)) {
return $attachment;
}
foreach ($this->filePaths as $path) {
if (file_exists($path . DS . $attachment)) {
$file = $path . DS . $attachment;
return $file;
}
}
return null;
}
/**
* Wrap the message using EmailComponent::$lineLength
*
* @param string $message Message to wrap
* @param integer $lineLength Max length of line
* @return array Wrapped message
* @access protected
*/
function _wrap($message, $lineLength = null) {
$message = $this->_strip($message, true);
$message = str_replace(array("\r\n","\r"), "\n", $message);
$lines = explode("\n", $message);
$formatted = array();
if ($this->_lineLength !== null) {
trigger_error(__('_lineLength cannot be accessed please use lineLength', true), E_USER_WARNING);
$this->lineLength = $this->_lineLength;
}
if (!$lineLength) {
$lineLength = $this->lineLength;
}
foreach ($lines as $line) {
if (substr($line, 0, 1) == '.') {
$line = '.' . $line;
}
$formatted = array_merge($formatted, explode("\n", wordwrap($line, $lineLength, "\n", true)));
}
$formatted[] = '';
return $formatted;
}
/**
* Encode the specified string using the current charset
*
* @param string $subject String to encode
* @return string Encoded string
* @access private
*/
function _encode($subject) {
$subject = $this->_strip($subject);
$nl = "\r\n";
if ($this->delivery == 'mail') {
$nl = '';
}
$internalEncoding = function_exists('mb_internal_encoding');
if ($internalEncoding) {
$restore = mb_internal_encoding();
mb_internal_encoding($this->charset);
}
$return = mb_encode_mimeheader($subject, $this->charset, 'B', $nl);
if ($internalEncoding) {
mb_internal_encoding($restore);
}
return $return;
}
/**
* Format a string as an email address
*
* @param string $string String representing an email address
* @return string Email address suitable for email headers or smtp pipe
* @access private
*/
function _formatAddress($string, $smtp = false) {
$hasAlias = preg_match('/((.*)\s)?<(.+)>/', $string, $matches);
if ($smtp && $hasAlias) {
return $this->_strip('<' . $matches[3] . '>');
} elseif ($smtp) {
return $this->_strip('<' . $string . '>');
}
if ($hasAlias && !empty($matches[2])) {
return $this->_strip($matches[2] . ' <' . $matches[3] . '>');
}
return $this->_strip($string);
}
/**
* Remove certain elements (such as bcc:, to:, %0a) from given value.
* Helps prevent header injection / mainipulation on user content.
*
* @param string $value Value to strip
* @param boolean $message Set to true to indicate main message content
* @return string Stripped value
* @access private
*/
function _strip($value, $message = false) {
$search = '%0a|%0d|Content-(?:Type|Transfer-Encoding)\:';
$search .= '|charset\=|mime-version\:|multipart/mixed|(?:[^a-z]to|b?cc)\:.*';
if ($message !== true) {
$search .= '|\r|\n';
}
$search = '#(?:' . $search . ')#i';
while (preg_match($search, $value)) {
$value = preg_replace($search, '', $value);
}
return $value;
}
/**
* Wrapper for PHP mail function used for sending out emails
*
* @return bool Success
* @access private
*/
function _mail() {
$header = implode("\r\n", $this->__header);
$message = implode("\r\n", $this->__message);
if (is_array($this->to)) {
$to = implode(', ', array_map(array($this, '_formatAddress'), $this->to));
} else {
$to = $this->to;
}
if (ini_get('safe_mode')) {
return @mail($to, $this->_encode($this->subject), $message, $header);
}
return @mail($to, $this->_encode($this->subject), $message, $header, $this->additionalParams);
}
/**
* Sends out email via SMTP
*
* @return bool Success
* @access private
*/
function _smtp() {
App::import('Core', array('CakeSocket'));
$defaults = array(
'host' => 'localhost',
'port' => 25,
'protocol' => 'smtp',
'timeout' => 30
);
$this->smtpOptions = array_merge($defaults, $this->smtpOptions);
$this->__smtpConnection =& new CakeSocket($this->smtpOptions);
if (!$this->__smtpConnection->connect()) {
$this->smtpError = $this->__smtpConnection->lastError();
return false;
} elseif (!$this->_smtpSend(null, '220')) {
return false;
}
$httpHost = env('HTTP_HOST');
if (isset($this->smtpOptions['client'])) {
$host = $this->smtpOptions['client'];
} elseif (!empty($httpHost)) {
$host = $httpHost;
} else {
$host = 'localhost';
}
if (!$this->_smtpSend("EHLO {$host}", '250') && !$this->_smtpSend("HELO {$host}", '250')) {
return false;
}
if (isset($this->smtpOptions['username']) && isset($this->smtpOptions['password'])) {
$authRequired = $this->_smtpSend('AUTH LOGIN', '334|503');
if ($authRequired == '334') {
if (!$this->_smtpSend(base64_encode($this->smtpOptions['username']), '334')) {
return false;
}
if (!$this->_smtpSend(base64_encode($this->smtpOptions['password']), '235')) {
return false;
}
} elseif ($authRequired != '503') {
return false;
}
}
if (!$this->_smtpSend('MAIL FROM: ' . $this->_formatAddress($this->from, true))) {
return false;
}
if (!is_array($this->to)) {
$tos = array($this->to);
} else {
$tos = $this->to;
}
foreach ($tos as $to) {
if (!$this->_smtpSend('RCPT TO: ' . $this->_formatAddress($to, true))) {
return false;
}
}
foreach ($this->cc as $cc) {
if (!$this->_smtpSend('RCPT TO: ' . $this->_formatAddress($cc, true))) {
return false;
}
}
foreach ($this->bcc as $bcc) {
if (!$this->_smtpSend('RCPT TO: ' . $this->_formatAddress($bcc, true))) {
return false;
}
}
if (!$this->_smtpSend('DATA', '354')) {
return false;
}
$header = implode("\r\n", $this->__header);
$message = implode("\r\n", $this->__message);
if (!$this->_smtpSend($header . "\r\n\r\n" . $message . "\r\n\r\n\r\n.")) {
return false;
}
$this->_smtpSend('QUIT', false);
$this->__smtpConnection->disconnect();
return true;
}
/**
* Protected method for sending data to SMTP connection
*
* @param string $data data to be sent to SMTP server
* @param mixed $checkCode code to check for in server response, false to skip
* @return bool Success
* @access protected
*/
function _smtpSend($data, $checkCode = '250') {
if (!is_null($data)) {
$this->__smtpConnection->write($data . "\r\n");
}
while ($checkCode !== false) {
$response = '';
$startTime = time();
while (substr($response, -2) !== "\r\n" && ((time() - $startTime) < $this->smtpOptions['timeout'])) {
$response .= $this->__smtpConnection->read();
}
if (substr($response, -2) !== "\r\n") {
$this->smtpError = 'timeout';
return false;
}
$response = end(explode("\r\n", rtrim($response, "\r\n")));
if (preg_match('/^(' . $checkCode . ')(.)/', $response, $code)) {
if ($code[2] === '-') {
continue;
}
return $code[1];
}
$this->smtpError = $response;
return false;
}
return true;
}
/**
* Set as controller flash message a debug message showing current settings in component
*
* @return boolean Success
* @access private
*/
function _debug() {
$nl = "\n";
$header = implode($nl, $this->__header);
$message = implode($nl, $this->__message);
$fm = '<pre>';
if (is_array($this->to)) {
$to = implode(', ', array_map(array($this, '_formatAddress'), $this->to));
} else {
$to = $this->to;
}
$fm .= sprintf('%s %s%s', 'To:', $to, $nl);
$fm .= sprintf('%s %s%s', 'From:', $this->from, $nl);
$fm .= sprintf('%s %s%s', 'Subject:', $this->_encode($this->subject), $nl);
$fm .= sprintf('%s%3$s%3$s%s', 'Header:', $header, $nl);
$fm .= sprintf('%s%3$s%3$s%s', 'Parameters:', $this->additionalParams, $nl);
$fm .= sprintf('%s%3$s%3$s%s', 'Message:', $message, $nl);
$fm .= '</pre>';
if (isset($this->Controller->Session)) {
$this->Controller->Session->setFlash($fm, 'default', null, 'email');
return true;
}
return $fm;
}
}

View File

@@ -0,0 +1,826 @@
<?php
/**
* Request object for handling alternative HTTP requests
*
* Alternative HTTP requests can come from wireless units like mobile phones, palmtop computers,
* and the like. These units have no use for Ajax requests, and this Component can tell how Cake
* should respond to the different needs of a handheld computer and a desktop machine.
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.controller.components
* @since CakePHP(tm) v 0.10.4.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Request object for handling HTTP requests
*
* @package cake
* @subpackage cake.cake.libs.controller.components
* @link http://book.cakephp.org/view/1291/Request-Handling
*
*/
class RequestHandlerComponent extends Object {
/**
* The layout that will be switched to for Ajax requests
*
* @var string
* @access public
* @see RequestHandler::setAjax()
*/
var $ajaxLayout = 'ajax';
/**
* Determines whether or not callbacks will be fired on this component
*
* @var boolean
* @access public
*/
var $enabled = true;
/**
* Holds the content-type of the response that is set when using
* RequestHandler::respondAs()
*
* @var string
* @access private
*/
var $__responseTypeSet = null;
/**
* Holds the copy of Controller::$params
*
* @var array
* @access public
*/
var $params = array();
/**
* Friendly content-type mappings used to set response types and determine
* request types. Can be modified with RequestHandler::setContent()
*
* @var array
* @access private
* @see RequestHandlerComponent::setContent
*/
var $__requestContent = array(
'javascript' => 'text/javascript',
'js' => 'text/javascript',
'json' => 'application/json',
'css' => 'text/css',
'html' => array('text/html', '*/*'),
'text' => 'text/plain',
'txt' => 'text/plain',
'csv' => array('application/vnd.ms-excel', 'text/plain'),
'form' => 'application/x-www-form-urlencoded',
'file' => 'multipart/form-data',
'xhtml' => array('application/xhtml+xml', 'application/xhtml', 'text/xhtml'),
'xhtml-mobile' => 'application/vnd.wap.xhtml+xml',
'xml' => array('application/xml', 'text/xml'),
'rss' => 'application/rss+xml',
'atom' => 'application/atom+xml',
'amf' => 'application/x-amf',
'wap' => array(
'text/vnd.wap.wml',
'text/vnd.wap.wmlscript',
'image/vnd.wap.wbmp'
),
'wml' => 'text/vnd.wap.wml',
'wmlscript' => 'text/vnd.wap.wmlscript',
'wbmp' => 'image/vnd.wap.wbmp',
'pdf' => 'application/pdf',
'zip' => 'application/x-zip',
'tar' => 'application/x-tar'
);
/**
* List of regular expressions for matching mobile device's user agent string
*
* @var array
* @access public
*/
var $mobileUA = array(
'Android',
'AvantGo',
'BlackBerry',
'DoCoMo',
'iPod',
'iPhone',
'J2ME',
'MIDP',
'NetFront',
'Nokia',
'Opera Mini',
'PalmOS',
'PalmSource',
'portalmmm',
'Plucker',
'ReqwirelessWeb',
'SonyEricsson',
'Symbian',
'UP\.Browser',
'webOS',
'Windows CE',
'Xiino'
);
/**
* Content-types accepted by the client. If extension parsing is enabled in the
* Router, and an extension is detected, the corresponding content-type will be
* used as the overriding primary content-type accepted.
*
* @var array
* @access private
* @see Router::parseExtensions()
*/
var $__acceptTypes = array();
/**
* The template to use when rendering the given content type.
*
* @var string
* @access private
*/
var $__renderType = null;
/**
* Contains the file extension parsed out by the Router
*
* @var string
* @access public
* @see Router::parseExtensions()
*/
var $ext = null;
/**
* Flag set when MIME types have been initialized
*
* @var boolean
* @access private
* @see RequestHandler::__initializeTypes()
*/
var $__typesInitialized = false;
/**
* Constructor. Parses the accepted content types accepted by the client using HTTP_ACCEPT
*
*/
function __construct() {
$this->__acceptTypes = explode(',', env('HTTP_ACCEPT'));
foreach ($this->__acceptTypes as $i => $type) {
if (strpos($type, ';')) {
$type = explode(';', $type);
$this->__acceptTypes[$i] = $type[0];
}
}
parent::__construct();
}
/**
* Initializes the component, gets a reference to Controller::$parameters, and
* checks to see if a file extension has been parsed by the Router. If yes, the
* corresponding content-type is pushed onto the list of accepted content-types
* as the first item.
*
* @param object $controller A reference to the controller
* @param array $settings Array of settings to _set().
* @return void
* @see Router::parseExtensions()
* @access public
*/
function initialize(&$controller, $settings = array()) {
if (isset($controller->params['url']['ext'])) {
$this->ext = $controller->params['url']['ext'];
}
$this->_set($settings);
}
/**
* The startup method of the RequestHandler enables several automatic behaviors
* related to the detection of certain properties of the HTTP request, including:
*
* - Disabling layout rendering for Ajax requests (based on the HTTP_X_REQUESTED_WITH header)
* - If Router::parseExtensions() is enabled, the layout and template type are
* switched based on the parsed extension. For example, if controller/action.xml
* is requested, the view path becomes <i>app/views/controller/xml/action.ctp</i>.
* - If a helper with the same name as the extension exists, it is added to the controller.
* - If the extension is of a type that RequestHandler understands, it will set that
* Content-type in the response header.
* - If the XML data is POSTed, the data is parsed into an XML object, which is assigned
* to the $data property of the controller, which can then be saved to a model object.
*
* @param object $controller A reference to the controller
* @return void
* @access public
*/
function startup(&$controller) {
if (!$this->enabled) {
return;
}
$this->__initializeTypes();
$controller->params['isAjax'] = $this->isAjax();
$isRecognized = (
!in_array($this->ext, array('html', 'htm')) &&
in_array($this->ext, array_keys($this->__requestContent))
);
if (!empty($this->ext) && $isRecognized) {
$this->renderAs($controller, $this->ext);
} elseif ($this->isAjax()) {
$this->renderAs($controller, 'ajax');
} elseif (empty($this->ext) || in_array($this->ext, array('html', 'htm'))) {
$this->respondAs('html', array('charset' => Configure::read('App.encoding')));
}
if ($this->requestedWith('xml')) {
if (!class_exists('XmlNode')) {
App::import('Core', 'Xml');
}
$xml = new Xml(trim(file_get_contents('php://input')));
if (count($xml->children) == 1 && is_object($dataNode = $xml->child('data'))) {
$controller->data = $dataNode->toArray();
} else {
$controller->data = $xml->toArray();
}
}
}
/**
* Handles (fakes) redirects for Ajax requests using requestAction()
*
* @param object $controller A reference to the controller
* @param mixed $url A string or array containing the redirect location
* @param mixed HTTP Status for redirect
* @access public
*/
function beforeRedirect(&$controller, $url, $status = null) {
if (!$this->isAjax()) {
return;
}
foreach ($_POST as $key => $val) {
unset($_POST[$key]);
}
if (is_array($url)) {
$url = Router::url($url + array('base' => false));
}
if (!empty($status)) {
$statusCode = $controller->httpCodes($status);
$code = key($statusCode);
$msg = $statusCode[$code];
$controller->header("HTTP/1.1 {$code} {$msg}");
}
echo $this->requestAction($url, array('return', 'bare' => false));
$this->_stop();
}
/**
* Returns true if the current HTTP request is Ajax, false otherwise
*
* @return boolean True if call is Ajax
* @access public
*/
function isAjax() {
return env('HTTP_X_REQUESTED_WITH') === "XMLHttpRequest";
}
/**
* Returns true if the current HTTP request is coming from a Flash-based client
*
* @return boolean True if call is from Flash
* @access public
*/
function isFlash() {
return (preg_match('/^(Shockwave|Adobe) Flash/', env('HTTP_USER_AGENT')) == 1);
}
/**
* Returns true if the current request is over HTTPS, false otherwise.
*
* @return bool True if call is over HTTPS
* @access public
*/
function isSSL() {
return env('HTTPS');
}
/**
* Returns true if the current call accepts an XML response, false otherwise
*
* @return boolean True if client accepts an XML response
* @access public
*/
function isXml() {
return $this->prefers('xml');
}
/**
* Returns true if the current call accepts an RSS response, false otherwise
*
* @return boolean True if client accepts an RSS response
* @access public
*/
function isRss() {
return $this->prefers('rss');
}
/**
* Returns true if the current call accepts an Atom response, false otherwise
*
* @return boolean True if client accepts an RSS response
* @access public
*/
function isAtom() {
return $this->prefers('atom');
}
/**
* Returns true if user agent string matches a mobile web browser, or if the
* client accepts WAP content.
*
* @return boolean True if user agent is a mobile web browser
* @access public
* @deprecated Use of constant REQUEST_MOBILE_UA is deprecated and will be removed in future versions
*/
function isMobile() {
if (defined('REQUEST_MOBILE_UA')) {
$regex = '/' . REQUEST_MOBILE_UA . '/i';
} else {
$regex = '/' . implode('|', $this->mobileUA) . '/i';
}
if (preg_match($regex, env('HTTP_USER_AGENT')) || $this->accepts('wap')) {
return true;
}
return false;
}
/**
* Returns true if the client accepts WAP content
*
* @return bool
* @access public
*/
function isWap() {
return $this->prefers('wap');
}
/**
* Returns true if the current call a POST request
*
* @return boolean True if call is a POST
* @access public
*/
function isPost() {
return (strtolower(env('REQUEST_METHOD')) == 'post');
}
/**
* Returns true if the current call a PUT request
*
* @return boolean True if call is a PUT
* @access public
*/
function isPut() {
return (strtolower(env('REQUEST_METHOD')) == 'put');
}
/**
* Returns true if the current call a GET request
*
* @return boolean True if call is a GET
* @access public
*/
function isGet() {
return (strtolower(env('REQUEST_METHOD')) == 'get');
}
/**
* Returns true if the current call a DELETE request
*
* @return boolean True if call is a DELETE
* @access public
*/
function isDelete() {
return (strtolower(env('REQUEST_METHOD')) == 'delete');
}
/**
* Gets Prototype version if call is Ajax, otherwise empty string.
* The Prototype library sets a special "Prototype version" HTTP header.
*
* @return string Prototype version of component making Ajax call
* @access public
*/
function getAjaxVersion() {
if (env('HTTP_X_PROTOTYPE_VERSION') != null) {
return env('HTTP_X_PROTOTYPE_VERSION');
}
return false;
}
/**
* Adds/sets the Content-type(s) for the given name. This method allows
* content-types to be mapped to friendly aliases (or extensions), which allows
* RequestHandler to automatically respond to requests of that type in the
* startup method.
*
* @param string $name The name of the Content-type, i.e. "html", "xml", "css"
* @param mixed $type The Content-type or array of Content-types assigned to the name,
* i.e. "text/html", or "application/xml"
* @return void
* @access public
*/
function setContent($name, $type = null) {
if (is_array($name)) {
$this->__requestContent = array_merge($this->__requestContent, $name);
return;
}
$this->__requestContent[$name] = $type;
}
/**
* Gets the server name from which this request was referred
*
* @return string Server address
* @access public
*/
function getReferer() {
if (env('HTTP_HOST') != null) {
$sessHost = env('HTTP_HOST');
}
if (env('HTTP_X_FORWARDED_HOST') != null) {
$sessHost = env('HTTP_X_FORWARDED_HOST');
}
return trim(preg_replace('/(?:\:.*)/', '', $sessHost));
}
/**
* Gets remote client IP
*
* @return string Client IP address
* @access public
*/
function getClientIP($safe = true) {
if (!$safe && env('HTTP_X_FORWARDED_FOR') != null) {
$ipaddr = preg_replace('/(?:,.*)/', '', env('HTTP_X_FORWARDED_FOR'));
} else {
if (env('HTTP_CLIENT_IP') != null) {
$ipaddr = env('HTTP_CLIENT_IP');
} else {
$ipaddr = env('REMOTE_ADDR');
}
}
if (env('HTTP_CLIENTADDRESS') != null) {
$tmpipaddr = env('HTTP_CLIENTADDRESS');
if (!empty($tmpipaddr)) {
$ipaddr = preg_replace('/(?:,.*)/', '', $tmpipaddr);
}
}
return trim($ipaddr);
}
/**
* Determines which content types the client accepts. Acceptance is based on
* the file extension parsed by the Router (if present), and by the HTTP_ACCEPT
* header.
*
* @param mixed $type Can be null (or no parameter), a string type name, or an
* array of types
* @return mixed If null or no parameter is passed, returns an array of content
* types the client accepts. If a string is passed, returns true
* if the client accepts it. If an array is passed, returns true
* if the client accepts one or more elements in the array.
* @access public
* @see RequestHandlerComponent::setContent()
*/
function accepts($type = null) {
$this->__initializeTypes();
if ($type == null) {
return $this->mapType($this->__acceptTypes);
} elseif (is_array($type)) {
foreach ($type as $t) {
if ($this->accepts($t) == true) {
return true;
}
}
return false;
} elseif (is_string($type)) {
if (!isset($this->__requestContent[$type])) {
return false;
}
$content = $this->__requestContent[$type];
if (is_array($content)) {
foreach ($content as $c) {
if (in_array($c, $this->__acceptTypes)) {
return true;
}
}
} else {
if (in_array($content, $this->__acceptTypes)) {
return true;
}
}
}
}
/**
* Determines the content type of the data the client has sent (i.e. in a POST request)
*
* @param mixed $type Can be null (or no parameter), a string type name, or an array of types
* @return mixed
* @access public
*/
function requestedWith($type = null) {
if (!$this->isPost() && !$this->isPut()) {
return null;
}
list($contentType) = explode(';', env('CONTENT_TYPE'));
if ($type == null) {
return $this->mapType($contentType);
} elseif (is_array($type)) {
foreach ($type as $t) {
if ($this->requestedWith($t)) {
return $this->mapType($t);
}
}
return false;
} elseif (is_string($type)) {
return ($type == $this->mapType($contentType));
}
}
/**
* Determines which content-types the client prefers. If no parameters are given,
* the content-type that the client most likely prefers is returned. If $type is
* an array, the first item in the array that the client accepts is returned.
* Preference is determined primarily by the file extension parsed by the Router
* if provided, and secondarily by the list of content-types provided in
* HTTP_ACCEPT.
*
* @param mixed $type An optional array of 'friendly' content-type names, i.e.
* 'html', 'xml', 'js', etc.
* @return mixed If $type is null or not provided, the first content-type in the
* list, based on preference, is returned.
* @access public
* @see RequestHandlerComponent::setContent()
*/
function prefers($type = null) {
$this->__initializeTypes();
$accept = $this->accepts();
if ($type == null) {
if (empty($this->ext)) {
if (is_array($accept)) {
return $accept[0];
}
return $accept;
}
return $this->ext;
}
$types = $type;
if (is_string($type)) {
$types = array($type);
}
if (count($types) === 1) {
if (!empty($this->ext)) {
return ($types[0] == $this->ext);
}
return ($types[0] == $accept[0]);
}
$accepts = array();
foreach ($types as $type) {
if (in_array($type, $accept)) {
$accepts[] = $type;
}
}
if (count($accepts) === 0) {
return false;
} elseif (count($types) === 1) {
return ($types[0] === $accepts[0]);
} elseif (count($accepts) === 1) {
return $accepts[0];
}
$acceptedTypes = array();
foreach ($this->__acceptTypes as $type) {
$acceptedTypes[] = $this->mapType($type);
}
$accepts = array_intersect($acceptedTypes, $accepts);
return $accepts[0];
}
/**
* Sets the layout and template paths for the content type defined by $type.
*
* @param object $controller A reference to a controller object
* @param string $type Type of response to send (e.g: 'ajax')
* @return void
* @access public
* @see RequestHandlerComponent::setContent()
* @see RequestHandlerComponent::respondAs()
*/
function renderAs(&$controller, $type) {
$this->__initializeTypes();
$options = array('charset' => 'UTF-8');
if (Configure::read('App.encoding') !== null) {
$options = array('charset' => Configure::read('App.encoding'));
}
if ($type == 'ajax') {
$controller->layout = $this->ajaxLayout;
return $this->respondAs('html', $options);
}
$controller->ext = '.ctp';
if (empty($this->__renderType)) {
$controller->viewPath .= DS . $type;
} else {
$remove = preg_replace("/([\/\\\\]{$this->__renderType})$/", DS . $type, $controller->viewPath);
$controller->viewPath = $remove;
}
$this->__renderType = $type;
$controller->layoutPath = $type;
if (isset($this->__requestContent[$type])) {
$this->respondAs($type, $options);
}
$helper = ucfirst($type);
$isAdded = (
in_array($helper, $controller->helpers) ||
array_key_exists($helper, $controller->helpers)
);
if (!$isAdded) {
if (App::import('Helper', $helper)) {
$controller->helpers[] = $helper;
}
}
}
/**
* Sets the response header based on type map index name. If DEBUG is greater than 2, the header
* is not set.
*
* @param mixed $type Friendly type name, i.e. 'html' or 'xml', or a full content-type,
* like 'application/x-shockwave'.
* @param array $options If $type is a friendly type name that is associated with
* more than one type of content, $index is used to select which content-type to use.
* @return boolean Returns false if the friendly type name given in $type does
* not exist in the type map, or if the Content-type header has
* already been set by this method.
* @access public
* @see RequestHandlerComponent::setContent()
*/
function respondAs($type, $options = array()) {
$this->__initializeTypes();
if (!array_key_exists($type, $this->__requestContent) && strpos($type, '/') === false) {
return false;
}
$defaults = array('index' => 0, 'charset' => null, 'attachment' => false);
$options = array_merge($defaults, $options);
if (strpos($type, '/') === false && isset($this->__requestContent[$type])) {
$cType = null;
if (is_array($this->__requestContent[$type]) && isset($this->__requestContent[$type][$options['index']])) {
$cType = $this->__requestContent[$type][$options['index']];
} elseif (is_array($this->__requestContent[$type]) && isset($this->__requestContent[$type][0])) {
$cType = $this->__requestContent[$type][0];
} elseif (isset($this->__requestContent[$type])) {
$cType = $this->__requestContent[$type];
} else {
return false;
}
if (is_array($cType)) {
if ($this->prefers($cType)) {
$cType = $this->prefers($cType);
} else {
$cType = $cType[0];
}
}
} else {
$cType = $type;
}
if ($cType != null) {
$header = 'Content-type: ' . $cType;
if (!empty($options['charset'])) {
$header .= '; charset=' . $options['charset'];
}
if (!empty($options['attachment'])) {
$this->_header("Content-Disposition: attachment; filename=\"{$options['attachment']}\"");
}
if (Configure::read() < 2 && !defined('CAKEPHP_SHELL')) {
$this->_header($header);
}
$this->__responseTypeSet = $cType;
return true;
}
return false;
}
/**
* Wrapper for header() so calls can be easily tested.
*
* @param string $header The header to be sent.
* @return void
* @access protected
*/
function _header($header) {
header($header);
}
/**
* Returns the current response type (Content-type header), or null if none has been set
*
* @return mixed A string content type alias, or raw content type if no alias map exists,
* otherwise null
* @access public
*/
function responseType() {
if ($this->__responseTypeSet == null) {
return null;
}
return $this->mapType($this->__responseTypeSet);
}
/**
* Maps a content-type back to an alias
*
* @param mixed $type Content type
* @return mixed Alias
* @access public
*/
function mapType($ctype) {
if (is_array($ctype)) {
$out = array();
foreach ($ctype as $t) {
$out[] = $this->mapType($t);
}
return $out;
} else {
$keys = array_keys($this->__requestContent);
$count = count($keys);
for ($i = 0; $i < $count; $i++) {
$name = $keys[$i];
$type = $this->__requestContent[$name];
if (is_array($type) && in_array($ctype, $type)) {
return $name;
} elseif (!is_array($type) && $type == $ctype) {
return $name;
}
}
return $ctype;
}
}
/**
* Initializes MIME types
*
* @return void
* @access private
*/
function __initializeTypes() {
if ($this->__typesInitialized) {
return;
}
if (isset($this->__requestContent[$this->ext])) {
$content = $this->__requestContent[$this->ext];
if (is_array($content)) {
$content = $content[0];
}
array_unshift($this->__acceptTypes, $content);
}
$this->__typesInitialized = true;
}
}

View File

@@ -0,0 +1,751 @@
<?php
/**
* Security Component
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.controller.components
* @since CakePHP(tm) v 0.10.8.2156
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Core', array('String', 'Security'));
/**
* SecurityComponent
*
* @package cake
* @subpackage cake.cake.libs.controller.components
* @link http://book.cakephp.org/view/1296/Security-Component
*/
class SecurityComponent extends Object {
/**
* The controller method that will be called if this request is black-hole'd
*
* @var string
* @access public
*/
var $blackHoleCallback = null;
/**
* List of controller actions for which a POST request is required
*
* @var array
* @access public
* @see SecurityComponent::requirePost()
*/
var $requirePost = array();
/**
* List of controller actions for which a GET request is required
*
* @var array
* @access public
* @see SecurityComponent::requireGet()
*/
var $requireGet = array();
/**
* List of controller actions for which a PUT request is required
*
* @var array
* @access public
* @see SecurityComponent::requirePut()
*/
var $requirePut = array();
/**
* List of controller actions for which a DELETE request is required
*
* @var array
* @access public
* @see SecurityComponent::requireDelete()
*/
var $requireDelete = array();
/**
* List of actions that require an SSL-secured connection
*
* @var array
* @access public
* @see SecurityComponent::requireSecure()
*/
var $requireSecure = array();
/**
* List of actions that require a valid authentication key
*
* @var array
* @access public
* @see SecurityComponent::requireAuth()
*/
var $requireAuth = array();
/**
* List of actions that require an HTTP-authenticated login (basic or digest)
*
* @var array
* @access public
* @see SecurityComponent::requireLogin()
*/
var $requireLogin = array();
/**
* Login options for SecurityComponent::requireLogin()
*
* @var array
* @access public
* @see SecurityComponent::requireLogin()
*/
var $loginOptions = array('type' => '', 'prompt' => null);
/**
* An associative array of usernames/passwords used for HTTP-authenticated logins.
*
* @var array
* @access public
* @see SecurityComponent::requireLogin()
*/
var $loginUsers = array();
/**
* Controllers from which actions of the current controller are allowed to receive
* requests.
*
* @var array
* @access public
* @see SecurityComponent::requireAuth()
*/
var $allowedControllers = array();
/**
* Actions from which actions of the current controller are allowed to receive
* requests.
*
* @var array
* @access public
* @see SecurityComponent::requireAuth()
*/
var $allowedActions = array();
/**
* Form fields to disable
*
* @var array
* @access public
*/
var $disabledFields = array();
/**
* Whether to validate POST data. Set to false to disable for data coming from 3rd party
* services, etc.
*
* @var boolean
* @access public
*/
var $validatePost = true;
/**
* Other components used by the Security component
*
* @var array
* @access public
*/
var $components = array('RequestHandler', 'Session');
/**
* Holds the current action of the controller
*
* @var string
*/
var $_action = null;
/**
* Initialize the SecurityComponent
*
* @param object $controller Controller instance for the request
* @param array $settings Settings to set to the component
* @return void
* @access public
*/
function initialize(&$controller, $settings = array()) {
$this->_set($settings);
}
/**
* Component startup. All security checking happens here.
*
* @param object $controller Instantiating controller
* @return void
* @access public
*/
function startup(&$controller) {
$this->_action = strtolower($controller->action);
$this->_methodsRequired($controller);
$this->_secureRequired($controller);
$this->_authRequired($controller);
$this->_loginRequired($controller);
$isPost = ($this->RequestHandler->isPost() || $this->RequestHandler->isPut());
$isRequestAction = (
!isset($controller->params['requested']) ||
$controller->params['requested'] != 1
);
if ($isPost && $isRequestAction && $this->validatePost) {
if ($this->_validatePost($controller) === false) {
if (!$this->blackHole($controller, 'auth')) {
return null;
}
}
}
$this->_generateToken($controller);
}
/**
* Sets the actions that require a POST request, or empty for all actions
*
* @return void
* @access public
* @link http://book.cakephp.org/view/1299/requirePost
*/
function requirePost() {
$args = func_get_args();
$this->_requireMethod('Post', $args);
}
/**
* Sets the actions that require a GET request, or empty for all actions
*
* @return void
* @access public
*/
function requireGet() {
$args = func_get_args();
$this->_requireMethod('Get', $args);
}
/**
* Sets the actions that require a PUT request, or empty for all actions
*
* @return void
* @access public
*/
function requirePut() {
$args = func_get_args();
$this->_requireMethod('Put', $args);
}
/**
* Sets the actions that require a DELETE request, or empty for all actions
*
* @return void
* @access public
*/
function requireDelete() {
$args = func_get_args();
$this->_requireMethod('Delete', $args);
}
/**
* Sets the actions that require a request that is SSL-secured, or empty for all actions
*
* @return void
* @access public
* @link http://book.cakephp.org/view/1300/requireSecure
*/
function requireSecure() {
$args = func_get_args();
$this->_requireMethod('Secure', $args);
}
/**
* Sets the actions that require an authenticated request, or empty for all actions
*
* @return void
* @access public
* @link http://book.cakephp.org/view/1301/requireAuth
*/
function requireAuth() {
$args = func_get_args();
$this->_requireMethod('Auth', $args);
}
/**
* Sets the actions that require an HTTP-authenticated request, or empty for all actions
*
* @return void
* @access public
* @link http://book.cakephp.org/view/1302/requireLogin
*/
function requireLogin() {
$args = func_get_args();
$base = $this->loginOptions;
foreach ($args as $i => $arg) {
if (is_array($arg)) {
$this->loginOptions = $arg;
unset($args[$i]);
}
}
$this->loginOptions = array_merge($base, $this->loginOptions);
$this->_requireMethod('Login', $args);
if (isset($this->loginOptions['users'])) {
$this->loginUsers =& $this->loginOptions['users'];
}
}
/**
* Attempts to validate the login credentials for an HTTP-authenticated request
*
* @param string $type Either 'basic', 'digest', or null. If null/empty, will try both.
* @return mixed If successful, returns an array with login name and password, otherwise null.
* @access public
* @link http://book.cakephp.org/view/1303/loginCredentials-string-type
*/
function loginCredentials($type = null) {
switch (strtolower($type)) {
case 'basic':
$login = array('username' => env('PHP_AUTH_USER'), 'password' => env('PHP_AUTH_PW'));
if (!empty($login['username'])) {
return $login;
}
break;
case 'digest':
default:
$digest = null;
if (version_compare(PHP_VERSION, '5.1') != -1) {
$digest = env('PHP_AUTH_DIGEST');
} elseif (function_exists('apache_request_headers')) {
$headers = apache_request_headers();
if (isset($headers['Authorization']) && !empty($headers['Authorization']) && substr($headers['Authorization'], 0, 7) == 'Digest ') {
$digest = substr($headers['Authorization'], 7);
}
} else {
// Server doesn't support digest-auth headers
trigger_error(__('SecurityComponent::loginCredentials() - Server does not support digest authentication', true), E_USER_WARNING);
}
if (!empty($digest)) {
return $this->parseDigestAuthData($digest);
}
break;
}
return null;
}
/**
* Generates the text of an HTTP-authentication request header from an array of options.
*
* @param array $options Set of options for header
* @return string HTTP-authentication request header
* @access public
* @link http://book.cakephp.org/view/1304/loginRequest-array-options
*/
function loginRequest($options = array()) {
$options = array_merge($this->loginOptions, $options);
$this->_setLoginDefaults($options);
$auth = 'WWW-Authenticate: ' . ucfirst($options['type']);
$out = array('realm="' . $options['realm'] . '"');
if (strtolower($options['type']) == 'digest') {
$out[] = 'qop="auth"';
$out[] = 'nonce="' . uniqid("") . '"';
$out[] = 'opaque="' . md5($options['realm']) . '"';
}
return $auth . ' ' . implode(',', $out);
}
/**
* Parses an HTTP digest authentication response, and returns an array of the data, or null on failure.
*
* @param string $digest Digest authentication response
* @return array Digest authentication parameters
* @access public
* @link http://book.cakephp.org/view/1305/parseDigestAuthData-string-digest
*/
function parseDigestAuthData($digest) {
if (substr($digest, 0, 7) == 'Digest ') {
$digest = substr($digest, 7);
}
$keys = array();
$match = array();
$req = array('nonce' => 1, 'nc' => 1, 'cnonce' => 1, 'qop' => 1, 'username' => 1, 'uri' => 1, 'response' => 1);
preg_match_all('/(\w+)=([\'"]?)([a-zA-Z0-9@=.\/_-]+)\2/', $digest, $match, PREG_SET_ORDER);
foreach ($match as $i) {
$keys[$i[1]] = $i[3];
unset($req[$i[1]]);
}
if (empty($req)) {
return $keys;
}
return null;
}
/**
* Generates a hash to be compared with an HTTP digest-authenticated response
*
* @param array $data HTTP digest response data, as parsed by SecurityComponent::parseDigestAuthData()
* @return string Digest authentication hash
* @access public
* @see SecurityComponent::parseDigestAuthData()
* @link http://book.cakephp.org/view/1306/generateDigestResponseHash-array-data
*/
function generateDigestResponseHash($data) {
return md5(
md5($data['username'] . ':' . $this->loginOptions['realm'] . ':' . $this->loginUsers[$data['username']]) .
':' . $data['nonce'] . ':' . $data['nc'] . ':' . $data['cnonce'] . ':' . $data['qop'] . ':' .
md5(env('REQUEST_METHOD') . ':' . $data['uri'])
);
}
/**
* Black-hole an invalid request with a 404 error or custom callback. If SecurityComponent::$blackHoleCallback
* is specified, it will use this callback by executing the method indicated in $error
*
* @param object $controller Instantiating controller
* @param string $error Error method
* @return mixed If specified, controller blackHoleCallback's response, or no return otherwise
* @access public
* @see SecurityComponent::$blackHoleCallback
* @link http://book.cakephp.org/view/1307/blackHole-object-controller-string-error
*/
function blackHole(&$controller, $error = '') {
if ($this->blackHoleCallback == null) {
$code = 404;
if ($error == 'login') {
$code = 401;
$controller->header($this->loginRequest());
}
$controller->redirect(null, $code, true);
} else {
return $this->_callback($controller, $this->blackHoleCallback, array($error));
}
}
/**
* Sets the actions that require a $method HTTP request, or empty for all actions
*
* @param string $method The HTTP method to assign controller actions to
* @param array $actions Controller actions to set the required HTTP method to.
* @return void
* @access protected
*/
function _requireMethod($method, $actions = array()) {
if (isset($actions[0]) && is_array($actions[0])) {
$actions = $actions[0];
}
$this->{'require' . $method} = (empty($actions)) ? array('*'): $actions;
}
/**
* Check if HTTP methods are required
*
* @param object $controller Instantiating controller
* @return bool true if $method is required
* @access protected
*/
function _methodsRequired(&$controller) {
foreach (array('Post', 'Get', 'Put', 'Delete') as $method) {
$property = 'require' . $method;
if (is_array($this->$property) && !empty($this->$property)) {
$require = array_map('strtolower', $this->$property);
if (in_array($this->_action, $require) || $this->$property == array('*')) {
if (!$this->RequestHandler->{'is' . $method}()) {
if (!$this->blackHole($controller, strtolower($method))) {
return null;
}
}
}
}
}
return true;
}
/**
* Check if access requires secure connection
*
* @param object $controller Instantiating controller
* @return bool true if secure connection required
* @access protected
*/
function _secureRequired(&$controller) {
if (is_array($this->requireSecure) && !empty($this->requireSecure)) {
$requireSecure = array_map('strtolower', $this->requireSecure);
if (in_array($this->_action, $requireSecure) || $this->requireSecure == array('*')) {
if (!$this->RequestHandler->isSSL()) {
if (!$this->blackHole($controller, 'secure')) {
return null;
}
}
}
}
return true;
}
/**
* Check if authentication is required
*
* @param object $controller Instantiating controller
* @return bool true if authentication required
* @access protected
*/
function _authRequired(&$controller) {
if (is_array($this->requireAuth) && !empty($this->requireAuth) && !empty($controller->data)) {
$requireAuth = array_map('strtolower', $this->requireAuth);
if (in_array($this->_action, $requireAuth) || $this->requireAuth == array('*')) {
if (!isset($controller->data['_Token'] )) {
if (!$this->blackHole($controller, 'auth')) {
return null;
}
}
if ($this->Session->check('_Token')) {
$tData = unserialize($this->Session->read('_Token'));
if (!empty($tData['allowedControllers']) && !in_array($controller->params['controller'], $tData['allowedControllers']) || !empty($tData['allowedActions']) && !in_array($controller->params['action'], $tData['allowedActions'])) {
if (!$this->blackHole($controller, 'auth')) {
return null;
}
}
} else {
if (!$this->blackHole($controller, 'auth')) {
return null;
}
}
}
}
return true;
}
/**
* Check if login is required
*
* @param object $controller Instantiating controller
* @return bool true if login is required
* @access protected
*/
function _loginRequired(&$controller) {
if (is_array($this->requireLogin) && !empty($this->requireLogin)) {
$requireLogin = array_map('strtolower', $this->requireLogin);
if (in_array($this->_action, $requireLogin) || $this->requireLogin == array('*')) {
$login = $this->loginCredentials($this->loginOptions['type']);
if ($login == null) {
$controller->header($this->loginRequest());
if (!empty($this->loginOptions['prompt'])) {
$this->_callback($controller, $this->loginOptions['prompt']);
} else {
$this->blackHole($controller, 'login');
}
} else {
if (isset($this->loginOptions['login'])) {
$this->_callback($controller, $this->loginOptions['login'], array($login));
} else {
if (strtolower($this->loginOptions['type']) == 'digest') {
if ($login && isset($this->loginUsers[$login['username']])) {
if ($login['response'] == $this->generateDigestResponseHash($login)) {
return true;
}
}
$this->blackHole($controller, 'login');
} else {
if (
!(in_array($login['username'], array_keys($this->loginUsers)) &&
$this->loginUsers[$login['username']] == $login['password'])
) {
$this->blackHole($controller, 'login');
}
}
}
}
}
}
return true;
}
/**
* Validate submitted form
*
* @param object $controller Instantiating controller
* @return bool true if submitted form is valid
* @access protected
*/
function _validatePost(&$controller) {
if (empty($controller->data)) {
return true;
}
$data = $controller->data;
if (!isset($data['_Token']) || !isset($data['_Token']['fields']) || !isset($data['_Token']['key'])) {
return false;
}
$token = $data['_Token']['key'];
if ($this->Session->check('_Token')) {
$tokenData = unserialize($this->Session->read('_Token'));
if ($tokenData['expires'] < time() || $tokenData['key'] !== $token) {
return false;
}
}
$locked = null;
$check = $controller->data;
$token = urldecode($check['_Token']['fields']);
if (strpos($token, ':')) {
list($token, $locked) = explode(':', $token, 2);
}
unset($check['_Token']);
$locked = str_rot13($locked);
if (preg_match('/(\A|;|{|})O\:[0-9]+/', $locked)) {
return false;
}
$lockedFields = array();
$fields = Set::flatten($check);
$fieldList = array_keys($fields);
$locked = unserialize($locked);
$multi = array();
foreach ($fieldList as $i => $key) {
if (preg_match('/\.\d+$/', $key)) {
$multi[$i] = preg_replace('/\.\d+$/', '', $key);
unset($fieldList[$i]);
}
}
if (!empty($multi)) {
$fieldList += array_unique($multi);
}
foreach ($fieldList as $i => $key) {
$isDisabled = false;
$isLocked = (is_array($locked) && in_array($key, $locked));
if (!empty($this->disabledFields)) {
foreach ((array)$this->disabledFields as $disabled) {
$disabled = explode('.', $disabled);
$field = array_values(array_intersect(explode('.', $key), $disabled));
$isDisabled = ($field === $disabled);
if ($isDisabled) {
break;
}
}
}
if ($isDisabled || $isLocked) {
unset($fieldList[$i]);
if ($isLocked) {
$lockedFields[$key] = $fields[$key];
}
}
}
sort($fieldList, SORT_STRING);
ksort($lockedFields, SORT_STRING);
$fieldList += $lockedFields;
$check = Security::hash(serialize($fieldList) . Configure::read('Security.salt'));
return ($token === $check);
}
/**
* Add authentication key for new form posts
*
* @param object $controller Instantiating controller
* @return bool Success
* @access protected
*/
function _generateToken(&$controller) {
if (isset($controller->params['requested']) && $controller->params['requested'] === 1) {
if ($this->Session->check('_Token')) {
$tokenData = unserialize($this->Session->read('_Token'));
$controller->params['_Token'] = $tokenData;
}
return false;
}
$authKey = Security::generateAuthKey();
$expires = strtotime('+' . Security::inactiveMins() . ' minutes');
$token = array(
'key' => $authKey,
'expires' => $expires,
'allowedControllers' => $this->allowedControllers,
'allowedActions' => $this->allowedActions,
'disabledFields' => $this->disabledFields
);
if (!isset($controller->data)) {
$controller->data = array();
}
if ($this->Session->check('_Token')) {
$tokenData = unserialize($this->Session->read('_Token'));
$valid = (
isset($tokenData['expires']) &&
$tokenData['expires'] > time() &&
isset($tokenData['key'])
);
if ($valid) {
$token['key'] = $tokenData['key'];
}
}
$controller->params['_Token'] = $token;
$this->Session->write('_Token', serialize($token));
return true;
}
/**
* Sets the default login options for an HTTP-authenticated request
*
* @param array $options Default login options
* @return void
* @access protected
*/
function _setLoginDefaults(&$options) {
$options = array_merge(array(
'type' => 'basic',
'realm' => env('SERVER_NAME'),
'qop' => 'auth',
'nonce' => String::uuid()
), array_filter($options));
$options = array_merge(array('opaque' => md5($options['realm'])), $options);
}
/**
* Calls a controller callback method
*
* @param object $controller Controller to run callback on
* @param string $method Method to execute
* @param array $params Parameters to send to method
* @return mixed Controller callback method's response
* @access protected
*/
function _callback(&$controller, $method, $params = array()) {
if (is_callable(array($controller, $method))) {
return call_user_func_array(array(&$controller, $method), empty($params) ? null : $params);
} else {
return null;
}
}
}

View File

@@ -0,0 +1,295 @@
<?php
/**
* SessionComponent. Provides access to Sessions from the Controller layer
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.controller.components
* @since CakePHP(tm) v 0.10.0.1232
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
if (!class_exists('cakesession')) {
require LIBS . 'cake_session.php';
}
/**
* Session Component.
*
* Session handling from the controller.
*
* @package cake
* @subpackage cake.cake.libs.controller.components
* @link http://book.cakephp.org/view/1310/Sessions
*
*/
class SessionComponent extends CakeSession {
/**
* Used to determine if methods implementation is used, or bypassed
*
* @var boolean
* @access private
*/
var $__active = true;
/**
* Used to determine if request are from an Ajax request
*
* @var boolean
* @access private
*/
var $__bare = 0;
/**
* Class constructor
*
* @param string $base The base path for the Session
*/
function __construct($base = null) {
if (Configure::read('Session.start') === true) {
parent::__construct($base);
} else {
$this->__active = false;
}
}
/**
* Startup method.
*
* @param object $controller Instantiating controller
* @return void
* @access public
*/
function startup(&$controller) {
if ($this->started() === false && $this->__active === true) {
$this->__start();
}
}
/**
* Starts Session on if 'Session.start' is set to false in core.php
*
* @param string $base The base path for the Session
* @return void
* @access public
*/
function activate($base = null) {
if ($this->__active === true) {
return;
}
parent::__construct($base);
$this->__active = true;
}
/**
* Used to write a value to a session key.
*
* In your controller: $this->Session->write('Controller.sessKey', 'session value');
*
* @param string $name The name of the key your are setting in the session.
* This should be in a Controller.key format for better organizing
* @param string $value The value you want to store in a session.
* @return boolean Success
* @access public
* @link http://book.cakephp.org/view/1312/write
*/
function write($name, $value = null) {
if ($this->__active === true) {
$this->__start();
if (is_array($name)) {
foreach ($name as $key => $value) {
if (parent::write($key, $value) === false) {
return false;
}
}
return true;
}
if (parent::write($name, $value) === false) {
return false;
}
return true;
}
return false;
}
/**
* Used to read a session values for a key or return values for all keys.
*
* In your controller: $this->Session->read('Controller.sessKey');
* Calling the method without a param will return all session vars
*
* @param string $name the name of the session key you want to read
* @return mixed value from the session vars
* @access public
* @link http://book.cakephp.org/view/1314/read
*/
function read($name = null) {
if ($this->__active === true) {
$this->__start();
return parent::read($name);
}
return false;
}
/**
* Wrapper for SessionComponent::del();
*
* In your controller: $this->Session->delete('Controller.sessKey');
*
* @param string $name the name of the session key you want to delete
* @return boolean true is session variable is set and can be deleted, false is variable was not set.
* @access public
* @link http://book.cakephp.org/view/1316/delete
*/
function delete($name) {
if ($this->__active === true) {
$this->__start();
return parent::delete($name);
}
return false;
}
/**
* Used to check if a session variable is set
*
* In your controller: $this->Session->check('Controller.sessKey');
*
* @param string $name the name of the session key you want to check
* @return boolean true is session variable is set, false if not
* @access public
* @link http://book.cakephp.org/view/1315/check
*/
function check($name) {
if ($this->__active === true) {
$this->__start();
return parent::check($name);
}
return false;
}
/**
* Used to determine the last error in a session.
*
* In your controller: $this->Session->error();
*
* @return string Last session error
* @access public
* @link http://book.cakephp.org/view/1318/error
*/
function error() {
if ($this->__active === true) {
$this->__start();
return parent::error();
}
return false;
}
/**
* Used to set a session variable that can be used to output messages in the view.
*
* In your controller: $this->Session->setFlash('This has been saved');
*
* Additional params below can be passed to customize the output, or the Message.[key]
*
* @param string $message Message to be flashed
* @param string $element Element to wrap flash message in.
* @param array $params Parameters to be sent to layout as view variables
* @param string $key Message key, default is 'flash'
* @access public
* @link http://book.cakephp.org/view/1313/setFlash
*/
function setFlash($message, $element = 'default', $params = array(), $key = 'flash') {
if ($this->__active === true) {
$this->__start();
$this->write('Message.' . $key, compact('message', 'element', 'params'));
}
}
/**
* Used to renew a session id
*
* In your controller: $this->Session->renew();
*
* @return void
* @access public
*/
function renew() {
if ($this->__active === true) {
$this->__start();
parent::renew();
}
}
/**
* Used to check for a valid session.
*
* In your controller: $this->Session->valid();
*
* @return boolean true is session is valid, false is session is invalid
* @access public
*/
function valid() {
if ($this->__active === true) {
$this->__start();
return parent::valid();
}
return false;
}
/**
* Used to destroy sessions
*
* In your controller: $this->Session->destroy();
*
* @return void
* @access public
* @link http://book.cakephp.org/view/1317/destroy
*/
function destroy() {
if ($this->__active === true) {
$this->__start();
parent::destroy();
}
}
/**
* Returns Session id
*
* If $id is passed in a beforeFilter, the Session will be started
* with the specified id
*
* @param $id string
* @return string
* @access public
*/
function id($id = null) {
return parent::id($id);
}
/**
* Starts Session if SessionComponent is used in Controller::beforeFilter(),
* or is called from
*
* @return boolean
* @access private
*/
function __start() {
if ($this->started() === false) {
if (!$this->id() && parent::start()) {
parent::_checkValid();
} else {
parent::start();
}
}
return $this->started();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,85 @@
<?php
/**
* Static content controller.
*
* This file will render views from views/pages/
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.controller
* @since CakePHP(tm) v 0.2.9
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Static content controller
*
* Override this controller by placing a copy in controllers directory of an application
*
* @package cake
* @subpackage cake.cake.libs.controller
* @link http://book.cakephp.org/view/958/The-Pages-Controller
*/
class PagesController extends AppController {
/**
* Controller name
*
* @var string
* @access public
*/
var $name = 'Pages';
/**
* Default helper
*
* @var array
* @access public
*/
var $helpers = array('Html', 'Session');
/**
* This controller does not use a model
*
* @var array
* @access public
*/
var $uses = array();
/**
* Displays a view
*
* @param mixed What page to display
* @access public
*/
function display() {
$path = func_get_args();
$count = count($path);
if (!$count) {
$this->redirect('/');
}
$page = $subpage = $title_for_layout = null;
if (!empty($path[0])) {
$page = $path[0];
}
if (!empty($path[1])) {
$subpage = $path[1];
}
if (!empty($path[$count - 1])) {
$title_for_layout = Inflector::humanize($path[$count - 1]);
}
$this->set(compact('page', 'subpage', 'title_for_layout'));
$this->render(implode('/', $path));
}
}

View File

@@ -0,0 +1,613 @@
<?php
/**
* Scaffold.
*
* Automatic forms and actions generation for rapid web application development.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.controller
* @since Cake v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Scaffolding is a set of automatic actions for starting web development work faster.
*
* Scaffold inspects your database tables, and making educated guesses, sets up a
* number of pages for each of your Models. These pages have data forms that work,
* and afford the web developer an early look at the data, and the possibility to over-ride
* scaffolded actions with custom-made ones.
*
* @package cake
* @subpackage cake.cake.libs.controller
*/
class Scaffold extends Object {
/**
* Controller object
*
* @var Controller
* @access public
*/
var $controller = null;
/**
* Name of the controller to scaffold
*
* @var string
* @access public
*/
var $name = null;
/**
* Action to be performed.
*
* @var string
* @access public
*/
var $action = null;
/**
* Name of current model this view context is attached to
*
* @var string
* @access public
*/
var $model = null;
/**
* Path to View.
*
* @var string
* @access public
*/
var $viewPath;
/**
* Path parts for creating links in views.
*
* @var string Base URL
* @access public
*/
var $base = null;
/**
* Name of layout to use with this View.
*
* @var string
* @access public
*/
var $layout = 'default';
/**
* Array of parameter data
*
* @var array
* @access public
*/
var $params;
/**
* File extension. Defaults to Cake's template ".ctp".
*
* @var array
* @access public
*/
var $ext = '.ctp';
/**
* Sub-directory for this view file.
*
* @var string
* @access public
*/
var $subDir = null;
/**
* Plugin name.
*
* @var string
* @access public
*/
var $plugin = null;
/**
* valid session.
*
* @var boolean
* @access public
*/
var $_validSession = null;
/**
* List of variables to collect from the associated controller
*
* @var array
* @access private
*/
var $__passedVars = array(
'action', 'base', 'webroot', 'layout', 'name',
'viewPath', 'ext', 'params', 'data', 'plugin', 'cacheAction'
);
/**
* Title HTML element for current scaffolded view
*
* @var string
* @access public
*/
var $scaffoldTitle = null;
/**
* Construct and set up given controller with given parameters.
*
* @param string $controller_class Name of controller
* @param array $params Parameters for scaffolding
*/
function __construct(&$controller, $params) {
$this->controller =& $controller;
$count = count($this->__passedVars);
for ($j = 0; $j < $count; $j++) {
$var = $this->__passedVars[$j];
$this->{$var} = $controller->{$var};
}
$this->redirect = array('action' => 'index');
$this->modelClass = $controller->modelClass;
$this->modelKey = $controller->modelKey;
if (!is_object($this->controller->{$this->modelClass})) {
return $this->cakeError('missingModel', array(array(
'className' => $this->modelClass, 'webroot' => '', 'base' => $controller->base
)));
}
$this->ScaffoldModel =& $this->controller->{$this->modelClass};
$this->scaffoldTitle = Inflector::humanize($this->viewPath);
$this->scaffoldActions = $controller->scaffold;
$title_for_layout = __('Scaffold :: ', true) . Inflector::humanize($this->action) . ' :: ' . $this->scaffoldTitle;
$modelClass = $this->controller->modelClass;
$primaryKey = $this->ScaffoldModel->primaryKey;
$displayField = $this->ScaffoldModel->displayField;
$singularVar = Inflector::variable($modelClass);
$pluralVar = Inflector::variable($this->controller->name);
$singularHumanName = Inflector::humanize(Inflector::underscore($modelClass));
$pluralHumanName = Inflector::humanize(Inflector::underscore($this->controller->name));
$scaffoldFields = array_keys($this->ScaffoldModel->schema());
$associations = $this->__associations();
$this->controller->set(compact(
'title_for_layout', 'modelClass', 'primaryKey', 'displayField', 'singularVar', 'pluralVar',
'singularHumanName', 'pluralHumanName', 'scaffoldFields', 'associations'
));
if ($this->controller->view) {
$this->controller->view = 'Scaffold';
}
$this->_validSession = (
isset($this->controller->Session) && $this->controller->Session->valid() != false
);
$this->__scaffold($params);
}
/**
* Outputs the content of a scaffold method passing it through the Controller::afterFilter()
*
* @return void
* @access protected
*/
function _output() {
$this->controller->afterFilter();
echo($this->controller->output);
}
/**
* Renders a view action of scaffolded model.
*
* @param array $params Parameters for scaffolding
* @return mixed A rendered view of a row from Models database table
* @access private
*/
function __scaffoldView($params) {
if ($this->controller->_beforeScaffold('view')) {
$message = __(sprintf("No id set for %s::view()", Inflector::humanize($this->modelKey)), true);
if (isset($params['pass'][0])) {
$this->ScaffoldModel->id = $params['pass'][0];
} elseif ($this->_validSession) {
$this->controller->Session->setFlash($message);
$this->controller->redirect($this->redirect);
} else {
return $this->controller->flash($message, '/' . Inflector::underscore($this->controller->viewPath));
}
$this->ScaffoldModel->recursive = 1;
$this->controller->data = $this->ScaffoldModel->read();
$this->controller->set(
Inflector::variable($this->controller->modelClass), $this->controller->data
);
$this->controller->render($this->action, $this->layout);
$this->_output();
} elseif ($this->controller->_scaffoldError('view') === false) {
return $this->__scaffoldError();
}
}
/**
* Renders index action of scaffolded model.
*
* @param array $params Parameters for scaffolding
* @return mixed A rendered view listing rows from Models database table
* @access private
*/
function __scaffoldIndex($params) {
if ($this->controller->_beforeScaffold('index')) {
$this->ScaffoldModel->recursive = 0;
$this->controller->set(
Inflector::variable($this->controller->name), $this->controller->paginate()
);
$this->controller->render($this->action, $this->layout);
$this->_output();
} elseif ($this->controller->_scaffoldError('index') === false) {
return $this->__scaffoldError();
}
}
/**
* Renders an add or edit action for scaffolded model.
*
* @param string $action Action (add or edit)
* @return mixed A rendered view with a form to edit or add a record in the Models database table
* @access private
*/
function __scaffoldForm($action = 'edit') {
$this->controller->viewVars['scaffoldFields'] = array_merge(
$this->controller->viewVars['scaffoldFields'],
array_keys($this->ScaffoldModel->hasAndBelongsToMany)
);
$this->controller->render($action, $this->layout);
$this->_output();
}
/**
* Saves or updates the scaffolded model.
*
* @param array $params Parameters for scaffolding
* @param string $action add or edt
* @return mixed Success on save/update, add/edit form if data is empty or error if save or update fails
* @access private
*/
function __scaffoldSave($params = array(), $action = 'edit') {
$formAction = 'edit';
$success = __('updated', true);
if ($action === 'add') {
$formAction = 'add';
$success = __('saved', true);
}
if ($this->controller->_beforeScaffold($action)) {
if ($action == 'edit') {
if (isset($params['pass'][0])) {
$this->ScaffoldModel->id = $params['pass'][0];
}
if (!$this->ScaffoldModel->exists()) {
$message = __(sprintf("Invalid id for %s::edit()", Inflector::humanize($this->modelKey), true));
if ($this->_validSession) {
$this->controller->Session->setFlash($message);
$this->controller->redirect($this->redirect);
} else {
$this->controller->flash($message, $this->redirect);
$this->_output();
}
}
}
if (!empty($this->controller->data)) {
if ($action == 'create') {
$this->ScaffoldModel->create();
}
if ($this->ScaffoldModel->save($this->controller->data)) {
if ($this->controller->_afterScaffoldSave($action)) {
$message = __(
sprintf('The %1$s has been %2$s', Inflector::humanize($this->modelKey), $success),
true
);
if ($this->_validSession) {
$this->controller->Session->setFlash($message);
$this->controller->redirect($this->redirect);
} else {
$this->controller->flash($message, $this->redirect);
return $this->_output();
}
} else {
return $this->controller->_afterScaffoldSaveError($action);
}
} else {
if ($this->_validSession) {
$this->controller->Session->setFlash(__('Please correct errors below.', true));
}
}
}
if (empty($this->controller->data)) {
if ($this->ScaffoldModel->id) {
$this->controller->data = $this->ScaffoldModel->read();
} else {
$this->controller->data = $this->ScaffoldModel->create();
}
}
foreach ($this->ScaffoldModel->belongsTo as $assocName => $assocData) {
$varName = Inflector::variable(Inflector::pluralize(
preg_replace('/(?:_id)$/', '', $assocData['foreignKey'])
));
$this->controller->set($varName, $this->ScaffoldModel->{$assocName}->find('list'));
}
foreach ($this->ScaffoldModel->hasAndBelongsToMany as $assocName => $assocData) {
$varName = Inflector::variable(Inflector::pluralize($assocName));
$this->controller->set($varName, $this->ScaffoldModel->{$assocName}->find('list'));
}
return $this->__scaffoldForm($formAction);
} elseif ($this->controller->_scaffoldError($action) === false) {
return $this->__scaffoldError();
}
}
/**
* Performs a delete on given scaffolded Model.
*
* @param array $params Parameters for scaffolding
* @return mixed Success on delete, error if delete fails
* @access private
*/
function __scaffoldDelete($params = array()) {
if ($this->controller->_beforeScaffold('delete')) {
$message = __(
sprintf("No id set for %s::delete()", Inflector::humanize($this->modelKey)), true
);
if (isset($params['pass'][0])) {
$id = $params['pass'][0];
} elseif ($this->_validSession) {
$this->controller->Session->setFlash($message);
$this->controller->redirect($this->redirect);
} else {
$this->controller->flash($message, $this->redirect);
return $this->_output();
}
if ($this->ScaffoldModel->delete($id)) {
$message = __(
sprintf('The %1$s with id: %2$d has been deleted.', Inflector::humanize($this->modelClass), $id),
true
);
if ($this->_validSession) {
$this->controller->Session->setFlash($message);
$this->controller->redirect($this->redirect);
} else {
$this->controller->flash($message, $this->redirect);
return $this->_output();
}
} else {
$message = __(sprintf(
'There was an error deleting the %1$s with id: %2$d',
Inflector::humanize($this->modelClass), $id
), true);
if ($this->_validSession) {
$this->controller->Session->setFlash($message);
$this->controller->redirect($this->redirect);
} else {
$this->controller->flash($message, $this->redirect);
return $this->_output();
}
}
} elseif ($this->controller->_scaffoldError('delete') === false) {
return $this->__scaffoldError();
}
}
/**
* Show a scaffold error
*
* @return mixed A rendered view showing the error
* @access private
*/
function __scaffoldError() {
return $this->controller->render('error', $this->layout);
$this->_output();
}
/**
* When methods are now present in a controller
* scaffoldView is used to call default Scaffold methods if:
* `var $scaffold;` is placed in the controller's class definition.
*
* @param array $params Parameters for scaffolding
* @return mixed A rendered view of scaffold action, or showing the error
* @access private
*/
function __scaffold($params) {
$db = &ConnectionManager::getDataSource($this->ScaffoldModel->useDbConfig);
$prefixes = Configure::read('Routing.prefixes');
$scaffoldPrefix = $this->scaffoldActions;
if (isset($db)) {
if (empty($this->scaffoldActions)) {
$this->scaffoldActions = array(
'index', 'list', 'view', 'add', 'create', 'edit', 'update', 'delete'
);
} elseif (!empty($prefixes) && in_array($scaffoldPrefix, $prefixes)) {
$this->scaffoldActions = array(
$scaffoldPrefix . '_index',
$scaffoldPrefix . '_list',
$scaffoldPrefix . '_view',
$scaffoldPrefix . '_add',
$scaffoldPrefix . '_create',
$scaffoldPrefix . '_edit',
$scaffoldPrefix . '_update',
$scaffoldPrefix . '_delete'
);
}
if (in_array($params['action'], $this->scaffoldActions)) {
if (!empty($prefixes)) {
$params['action'] = str_replace($scaffoldPrefix . '_', '', $params['action']);
}
switch ($params['action']) {
case 'index':
case 'list':
$this->__scaffoldIndex($params);
break;
case 'view':
$this->__scaffoldView($params);
break;
case 'add':
case 'create':
$this->__scaffoldSave($params, 'add');
break;
case 'edit':
case 'update':
$this->__scaffoldSave($params, 'edit');
break;
case 'delete':
$this->__scaffoldDelete($params);
break;
}
} else {
return $this->cakeError('missingAction', array(array(
'className' => $this->controller->name . "Controller",
'base' => $this->controller->base,
'action' => $this->action,
'webroot' => $this->controller->webroot
)));
}
} else {
return $this->cakeError('missingDatabase', array(array(
'webroot' => $this->controller->webroot
)));
}
}
/**
* Returns associations for controllers models.
*
* @return array Associations for model
* @access private
*/
function __associations() {
$keys = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
$associations = array();
foreach ($keys as $key => $type) {
foreach ($this->ScaffoldModel->{$type} as $assocKey => $assocData) {
$associations[$type][$assocKey]['primaryKey'] =
$this->ScaffoldModel->{$assocKey}->primaryKey;
$associations[$type][$assocKey]['displayField'] =
$this->ScaffoldModel->{$assocKey}->displayField;
$associations[$type][$assocKey]['foreignKey'] =
$assocData['foreignKey'];
$associations[$type][$assocKey]['controller'] =
Inflector::pluralize(Inflector::underscore($assocData['className']));
if ($type == 'hasAndBelongsToMany') {
$associations[$type][$assocKey]['with'] = $assocData['with'];
}
}
}
return $associations;
}
}
/**
* Scaffold View.
*
* @package cake
* @subpackage cake.cake.libs.controller
*/
if (!class_exists('ThemeView')) {
App::import('View', 'Theme');
}
/**
* ScaffoldView provides specific view file loading features for scaffolded views.
*
* @package cake.libs.view
*/
class ScaffoldView extends ThemeView {
/**
* Override _getViewFileName Appends special scaffolding views in.
*
* @param string $name name of the view file to get.
* @return string action
* @access protected
*/
function _getViewFileName($name = null) {
if ($name === null) {
$name = $this->action;
}
$name = Inflector::underscore($name);
$prefixes = Configure::read('Routing.prefixes');
if (!empty($prefixes)) {
foreach ($prefixes as $prefix) {
if (strpos($name, $prefix . '_') !== false) {
$name = substr($name, strlen($prefix) + 1);
break;
}
}
}
if ($name === 'add') {
$name = 'edit';
}
$scaffoldAction = 'scaffold.' . $name;
if (!is_null($this->subDir)) {
$subDir = strtolower($this->subDir) . DS;
} else {
$subDir = null;
}
$names[] = $this->viewPath . DS . $subDir . $scaffoldAction;
$names[] = 'scaffolds' . DS . $subDir . $name;
$paths = $this->_paths($this->plugin);
$exts = array($this->ext);
if ($this->ext !== '.ctp') {
array_push($exts, '.ctp');
}
foreach ($exts as $ext) {
foreach ($paths as $path) {
foreach ($names as $name) {
if (file_exists($path . $name . $ext)) {
return $path . $name . $ext;
}
}
}
}
if ($name === 'scaffolds' . DS . $subDir . 'error') {
return LIBS . 'view' . DS . 'errors' . DS . 'scaffold_error.ctp';
}
return $this->_missingView($paths[0] . $name . $this->ext, 'missingView');
}
}

View File

@@ -0,0 +1,708 @@
<?php
/**
* Framework debugging and PHP error-handling class
*
* Provides enhanced logging, stack traces, and rendering debug views
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs
* @since CakePHP(tm) v 1.2.4560
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Included libraries.
*
*/
if (!class_exists('Object')) {
require_once LIBS . 'object.php';
}
if (!class_exists('CakeLog')) {
require_once LIBS . 'cake_log.php';
}
if (!class_exists('String')) {
require_once LIBS . 'string.php';
}
/**
* Provide custom logging and error handling.
*
* Debugger overrides PHP's default error handling to provide stack traces and enhanced logging
*
* @package cake
* @subpackage cake.cake.libs
* @link http://book.cakephp.org/view/1191/Using-the-Debugger-Class
*/
class Debugger extends Object {
/**
* A list of errors generated by the application.
*
* @var array
* @access public
*/
var $errors = array();
/**
* Contains the base URL for error code documentation.
*
* @var string
* @access public
*/
var $helpPath = null;
/**
* The current output format.
*
* @var string
* @access protected
*/
var $_outputFormat = 'js';
/**
* Templates used when generating trace or error strings. Can be global or indexed by the format
* value used in $_outputFormat.
*
* @var string
* @access protected
*/
var $_templates = array(
'log' => array(
'trace' => '{:reference} - {:path}, line {:line}',
'error' => "{:error} ({:code}): {:description} in [{:file}, line {:line}]"
),
'js' => array(
'error' => '',
'info' => '',
'trace' => '<pre class="stack-trace">{:trace}</pre>',
'code' => '',
'context' => '',
'links' => array()
),
'html' => array(
'trace' => '<pre class="cake-debug trace"><b>Trace</b> <p>{:trace}</p></pre>',
'context' => '<pre class="cake-debug context"><b>Context</b> <p>{:context}</p></pre>'
),
'txt' => array(
'error' => "{:error}: {:code} :: {:description} on line {:line} of {:path}\n{:info}",
'context' => "Context:\n{:context}\n",
'trace' => "Trace:\n{:trace}\n",
'code' => '',
'info' => ''
),
'base' => array(
'traceLine' => '{:reference} - {:path}, line {:line}'
)
);
/**
* Holds current output data when outputFormat is false.
*
* @var string
* @access private
*/
var $_data = array();
/**
* Constructor.
*
*/
function __construct() {
$docRef = ini_get('docref_root');
if (empty($docRef) && function_exists('ini_set')) {
ini_set('docref_root', 'http://php.net/');
}
if (!defined('E_RECOVERABLE_ERROR')) {
define('E_RECOVERABLE_ERROR', 4096);
}
if (!defined('E_DEPRECATED')) {
define('E_DEPRECATED', 8192);
}
$e = '<pre class="cake-debug">';
$e .= '<a href="javascript:void(0);" onclick="document.getElementById(\'{:id}-trace\')';
$e .= '.style.display = (document.getElementById(\'{:id}-trace\').style.display == ';
$e .= '\'none\' ? \'\' : \'none\');"><b>{:error}</b> ({:code})</a>: {:description} ';
$e .= '[<b>{:path}</b>, line <b>{:line}</b>]';
$e .= '<div id="{:id}-trace" class="cake-stack-trace" style="display: none;">';
$e .= '{:links}{:info}</div>';
$e .= '</pre>';
$this->_templates['js']['error'] = $e;
$t = '<div id="{:id}-trace" class="cake-stack-trace" style="display: none;">';
$t .= '{:context}{:code}{:trace}</div>';
$this->_templates['js']['info'] = $t;
$links = array();
$link = '<a href="javascript:void(0);" onclick="document.getElementById(\'{:id}-code\')';
$link .= '.style.display = (document.getElementById(\'{:id}-code\').style.display == ';
$link .= '\'none\' ? \'\' : \'none\')">Code</a>';
$links['code'] = $link;
$link = '<a href="javascript:void(0);" onclick="document.getElementById(\'{:id}-context\')';
$link .= '.style.display = (document.getElementById(\'{:id}-context\').style.display == ';
$link .= '\'none\' ? \'\' : \'none\')">Context</a>';
$links['context'] = $link;
$links['help'] = '<a href="{:helpPath}{:code}" target="_blank">Help</a>';
$this->_templates['js']['links'] = $links;
$this->_templates['js']['context'] = '<pre id="{:id}-context" class="cake-context" ';
$this->_templates['js']['context'] .= 'style="display: none;">{:context}</pre>';
$this->_templates['js']['code'] = '<div id="{:id}-code" class="cake-code-dump" ';
$this->_templates['js']['code'] .= 'style="display: none;"><pre>{:code}</pre></div>';
$e = '<pre class="cake-debug"><b>{:error}</b> ({:code}) : {:description} ';
$e .= '[<b>{:path}</b>, line <b>{:line}]</b></pre>';
$this->_templates['html']['error'] = $e;
$this->_templates['html']['context'] = '<pre class="cake-debug context"><b>Context</b> ';
$this->_templates['html']['context'] .= '<p>{:context}</p></pre>';
}
/**
* Returns a reference to the Debugger singleton object instance.
*
* @return object
* @access public
* @static
*/
function &getInstance($class = null) {
static $instance = array();
if (!empty($class)) {
if (!$instance || strtolower($class) != strtolower(get_class($instance[0]))) {
$instance[0] = & new $class();
if (Configure::read() > 0) {
Configure::version(); // Make sure the core config is loaded
$instance[0]->helpPath = Configure::read('Cake.Debugger.HelpPath');
}
}
}
if (!$instance) {
$instance[0] =& new Debugger();
if (Configure::read() > 0) {
Configure::version(); // Make sure the core config is loaded
$instance[0]->helpPath = Configure::read('Cake.Debugger.HelpPath');
}
}
return $instance[0];
}
/**
* Formats and outputs the contents of the supplied variable.
*
* @param $var mixed the variable to dump
* @return void
* @see Debugger::exportVar()
* @access public
* @static
* @link http://book.cakephp.org/view/1191/Using-the-Debugger-Class
*/
function dump($var) {
$_this =& Debugger::getInstance();
pr($_this->exportVar($var));
}
/**
* Creates an entry in the log file. The log entry will contain a stack trace from where it was called.
* as well as export the variable using exportVar. By default the log is written to the debug log.
*
* @param $var mixed Variable or content to log
* @param $level int type of log to use. Defaults to LOG_DEBUG
* @return void
* @static
* @link http://book.cakephp.org/view/1191/Using-the-Debugger-Class
*/
function log($var, $level = LOG_DEBUG) {
$_this =& Debugger::getInstance();
$source = $_this->trace(array('start' => 1)) . "\n";
CakeLog::write($level, "\n" . $source . $_this->exportVar($var));
}
/**
* Overrides PHP's default error handling.
*
* @param integer $code Code of error
* @param string $description Error description
* @param string $file File on which error occurred
* @param integer $line Line that triggered the error
* @param array $context Context
* @return boolean true if error was handled
* @access public
*/
function handleError($code, $description, $file = null, $line = null, $context = null) {
if (error_reporting() == 0 || $code === 2048 || $code === 8192) {
return;
}
$_this =& Debugger::getInstance();
if (empty($file)) {
$file = '[internal]';
}
if (empty($line)) {
$line = '??';
}
$path = $_this->trimPath($file);
$info = compact('code', 'description', 'file', 'line');
if (!in_array($info, $_this->errors)) {
$_this->errors[] = $info;
} else {
return;
}
switch ($code) {
case E_PARSE:
case E_ERROR:
case E_CORE_ERROR:
case E_COMPILE_ERROR:
case E_USER_ERROR:
$error = 'Fatal Error';
$level = LOG_ERROR;
break;
case E_WARNING:
case E_USER_WARNING:
case E_COMPILE_WARNING:
case E_RECOVERABLE_ERROR:
$error = 'Warning';
$level = LOG_WARNING;
break;
case E_NOTICE:
case E_USER_NOTICE:
$error = 'Notice';
$level = LOG_NOTICE;
break;
default:
return;
break;
}
$helpCode = null;
if (!empty($_this->helpPath) && preg_match('/.*\[([0-9]+)\]$/', $description, $codes)) {
if (isset($codes[1])) {
$helpID = $codes[1];
$description = trim(preg_replace('/\[[0-9]+\]$/', '', $description));
}
}
$data = compact(
'level', 'error', 'code', 'helpID', 'description', 'file', 'path', 'line', 'context'
);
echo $_this->_output($data);
if (Configure::read('log')) {
$tpl = $_this->_templates['log']['error'];
$options = array('before' => '{:', 'after' => '}');
CakeLog::write($level, String::insert($tpl, $data, $options));
}
if ($error == 'Fatal Error') {
exit();
}
return true;
}
/**
* Outputs a stack trace based on the supplied options.
*
* ### Options
*
* - `depth` - The number of stack frames to return. Defaults to 999
* - `format` - The format you want the return. Defaults to the currently selected format. If
* format is 'array' or 'points' the return will be an array.
* - `args` - Should arguments for functions be shown? If true, the arguments for each method call
* will be displayed.
* - `start` - The stack frame to start generating a trace from. Defaults to 0
*
* @param array $options Format for outputting stack trace
* @return mixed Formatted stack trace
* @access public
* @static
* @link http://book.cakephp.org/view/1191/Using-the-Debugger-Class
*/
function trace($options = array()) {
$_this =& Debugger::getInstance();
$defaults = array(
'depth' => 999,
'format' => $_this->_outputFormat,
'args' => false,
'start' => 0,
'scope' => null,
'exclude' => null
);
$options += $defaults;
$backtrace = debug_backtrace();
$count = count($backtrace);
$back = array();
$_trace = array(
'line' => '??',
'file' => '[internal]',
'class' => null,
'function' => '[main]'
);
for ($i = $options['start']; $i < $count && $i < $options['depth']; $i++) {
$trace = array_merge(array('file' => '[internal]', 'line' => '??'), $backtrace[$i]);
if (isset($backtrace[$i + 1])) {
$next = array_merge($_trace, $backtrace[$i + 1]);
$reference = $next['function'];
if (!empty($next['class'])) {
$reference = $next['class'] . '::' . $reference . '(';
if ($options['args'] && isset($next['args'])) {
$args = array();
foreach ($next['args'] as $arg) {
$args[] = Debugger::exportVar($arg);
}
$reference .= join(', ', $args);
}
$reference .= ')';
}
} else {
$reference = '[main]';
}
if (in_array($reference, array('call_user_func_array', 'trigger_error'))) {
continue;
}
if ($options['format'] == 'points' && $trace['file'] != '[internal]') {
$back[] = array('file' => $trace['file'], 'line' => $trace['line']);
} elseif ($options['format'] == 'array') {
$back[] = $trace;
} else {
if (isset($_this->_templates[$options['format']]['traceLine'])) {
$tpl = $_this->_templates[$options['format']]['traceLine'];
} else {
$tpl = $_this->_templates['base']['traceLine'];
}
$trace['path'] = Debugger::trimPath($trace['file']);
$trace['reference'] = $reference;
unset($trace['object'], $trace['args']);
$back[] = String::insert($tpl, $trace, array('before' => '{:', 'after' => '}'));
}
}
if ($options['format'] == 'array' || $options['format'] == 'points') {
return $back;
}
return implode("\n", $back);
}
/**
* Shortens file paths by replacing the application base path with 'APP', and the CakePHP core
* path with 'CORE'.
*
* @param string $path Path to shorten
* @return string Normalized path
* @access public
* @static
*/
function trimPath($path) {
if (!defined('CAKE_CORE_INCLUDE_PATH') || !defined('APP')) {
return $path;
}
if (strpos($path, APP) === 0) {
return str_replace(APP, 'APP' . DS, $path);
} elseif (strpos($path, CAKE_CORE_INCLUDE_PATH) === 0) {
return str_replace(CAKE_CORE_INCLUDE_PATH, 'CORE', $path);
} elseif (strpos($path, ROOT) === 0) {
return str_replace(ROOT, 'ROOT', $path);
}
$corePaths = App::core('cake');
foreach ($corePaths as $corePath) {
if (strpos($path, $corePath) === 0) {
return str_replace($corePath, 'CORE' .DS . 'cake' .DS, $path);
}
}
return $path;
}
/**
* Grabs an excerpt from a file and highlights a given line of code
*
* @param string $file Absolute path to a PHP file
* @param integer $line Line number to highlight
* @param integer $context Number of lines of context to extract above and below $line
* @return array Set of lines highlighted
* @access public
* @static
* @link http://book.cakephp.org/view/1191/Using-the-Debugger-Class
*/
function excerpt($file, $line, $context = 2) {
$data = $lines = array();
if (!file_exists($file)) {
return array();
}
$data = @explode("\n", file_get_contents($file));
if (empty($data) || !isset($data[$line])) {
return;
}
for ($i = $line - ($context + 1); $i < $line + $context; $i++) {
if (!isset($data[$i])) {
continue;
}
$string = str_replace(array("\r\n", "\n"), "", highlight_string($data[$i], true));
if ($i == $line) {
$lines[] = '<span class="code-highlight">' . $string . '</span>';
} else {
$lines[] = $string;
}
}
return $lines;
}
/**
* Converts a variable to a string for debug output.
*
* @param string $var Variable to convert
* @return string Variable as a formatted string
* @access public
* @static
* @link http://book.cakephp.org/view/1191/Using-the-Debugger-Class
*/
function exportVar($var, $recursion = 0) {
$_this =& Debugger::getInstance();
switch (strtolower(gettype($var))) {
case 'boolean':
return ($var) ? 'true' : 'false';
break;
case 'integer':
case 'double':
return $var;
break;
case 'string':
if (trim($var) == "") {
return '""';
}
return '"' . h($var) . '"';
break;
case 'object':
return get_class($var) . "\n" . $_this->__object($var);
case 'array':
$out = "array(";
$vars = array();
foreach ($var as $key => $val) {
if ($recursion >= 0) {
if (is_numeric($key)) {
$vars[] = "\n\t" . $_this->exportVar($val, $recursion - 1);
} else {
$vars[] = "\n\t" .$_this->exportVar($key, $recursion - 1)
. ' => ' . $_this->exportVar($val, $recursion - 1);
}
}
}
$n = null;
if (!empty($vars)) {
$n = "\n";
}
return $out . implode(",", $vars) . "{$n})";
break;
case 'resource':
return strtolower(gettype($var));
break;
case 'null':
return 'null';
break;
}
}
/**
* Handles object to string conversion.
*
* @param string $var Object to convert
* @return string
* @access private
* @see Debugger::exportVar()
*/
function __object($var) {
$out = array();
if (is_object($var)) {
$className = get_class($var);
$objectVars = get_object_vars($var);
foreach ($objectVars as $key => $value) {
if (is_object($value)) {
$value = get_class($value) . ' object';
} elseif (is_array($value)) {
$value = 'array';
} elseif ($value === null) {
$value = 'NULL';
} elseif (in_array(gettype($value), array('boolean', 'integer', 'double', 'string', 'array', 'resource'))) {
$value = Debugger::exportVar($value);
}
$out[] = "$className::$$key = " . $value;
}
}
return implode("\n", $out);
}
/**
* Switches output format, updates format strings
*
* @param string $format Format to use, including 'js' for JavaScript-enhanced HTML, 'html' for
* straight HTML output, or 'txt' for unformatted text.
* @param array $strings Template strings to be used for the output format.
* @access protected
*/
function output($format = null, $strings = array()) {
$_this =& Debugger::getInstance();
$data = null;
if (is_null($format)) {
return $_this->_outputFormat;
}
if (!empty($strings)) {
if (isset($_this->_templates[$format])) {
if (isset($strings['links'])) {
$_this->_templates[$format]['links'] = array_merge(
$_this->_templates[$format]['links'],
$strings['links']
);
unset($strings['links']);
}
$_this->_templates[$format] = array_merge($_this->_templates[$format], $strings);
} else {
$_this->_templates[$format] = $strings;
}
return $_this->_templates[$format];
}
if ($format === true && !empty($_this->_data)) {
$data = $_this->_data;
$_this->_data = array();
$format = false;
}
$_this->_outputFormat = $format;
return $data;
}
/**
* Renders error messages
*
* @param array $data Data about the current error
* @access private
*/
function _output($data = array()) {
$defaults = array(
'level' => 0,
'error' => 0,
'code' => 0,
'helpID' => null,
'description' => '',
'file' => '',
'line' => 0,
'context' => array()
);
$data += $defaults;
$files = $this->trace(array('start' => 2, 'format' => 'points'));
$code = $this->excerpt($files[0]['file'], $files[0]['line'] - 1, 1);
$trace = $this->trace(array('start' => 2, 'depth' => '20'));
$insertOpts = array('before' => '{:', 'after' => '}');
$context = array();
$links = array();
$info = '';
foreach ((array)$data['context'] as $var => $value) {
$context[] = "\${$var}\t=\t" . $this->exportVar($value, 1);
}
switch ($this->_outputFormat) {
case false:
$this->_data[] = compact('context', 'trace') + $data;
return;
case 'log':
$this->log(compact('context', 'trace') + $data);
return;
}
if (empty($this->_outputFormat) || !isset($this->_templates[$this->_outputFormat])) {
$this->_outputFormat = 'js';
}
$data['id'] = 'cakeErr' . count($this->errors);
$tpl = array_merge($this->_templates['base'], $this->_templates[$this->_outputFormat]);
$insert = array('context' => join("\n", $context), 'helpPath' => $this->helpPath) + $data;
$detect = array('help' => 'helpID', 'context' => 'context');
if (isset($tpl['links'])) {
foreach ($tpl['links'] as $key => $val) {
if (isset($detect[$key]) && empty($insert[$detect[$key]])) {
continue;
}
$links[$key] = String::insert($val, $insert, $insertOpts);
}
}
foreach (array('code', 'context', 'trace') as $key) {
if (empty($$key) || !isset($tpl[$key])) {
continue;
}
if (is_array($$key)) {
$$key = join("\n", $$key);
}
$info .= String::insert($tpl[$key], compact($key) + $insert, $insertOpts);
}
$links = join(' | ', $links);
unset($data['context']);
echo String::insert($tpl['error'], compact('links', 'info') + $data, $insertOpts);
}
/**
* Verifies that the application's salt and cipher seed value has been changed from the default value.
*
* @access public
* @static
*/
function checkSecurityKeys() {
if (Configure::read('Security.salt') == 'DYhG93b0qyJfIxfs2guVoUubWwvniR2G0FgaC9mi') {
trigger_error(__('Please change the value of \'Security.salt\' in app/config/core.php to a salt value specific to your application', true), E_USER_NOTICE);
}
if (Configure::read('Security.cipherSeed') === '76859309657453542496749683645') {
trigger_error(__('Please change the value of \'Security.cipherSeed\' in app/config/core.php to a numeric (digits only) seed value specific to your application', true), E_USER_NOTICE);
}
}
/**
* Invokes the given debugger object as the current error handler, taking over control from the
* previous handler in a stack-like hierarchy.
*
* @param object $debugger A reference to the Debugger object
* @access public
* @static
* @link http://book.cakephp.org/view/1191/Using-the-Debugger-Class
*/
function invoke(&$debugger) {
set_error_handler(array(&$debugger, 'handleError'));
}
}
if (!defined('DISABLE_DEFAULT_ERROR_HANDLING')) {
Debugger::invoke(Debugger::getInstance());
}

View File

@@ -0,0 +1,462 @@
<?php
/**
* Error handler
*
* Provides Error Capturing for Framework errors.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs
* @since CakePHP(tm) v 0.10.5.1732
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Controller', 'App');
/**
* Error Handling Controller
*
* Controller used by ErrorHandler to render error views.
*
* @package cake
* @subpackage cake.cake.libs
*/
class CakeErrorController extends AppController {
var $name = 'CakeError';
/**
* Uses Property
*
* @var array
*/
var $uses = array();
/**
* __construct
*
* @access public
* @return void
*/
function __construct() {
parent::__construct();
$this->_set(Router::getPaths());
$this->params = Router::getParams();
$this->constructClasses();
$this->Component->initialize($this);
$this->_set(array('cacheAction' => false, 'viewPath' => 'errors'));
}
}
/**
* Error Handler.
*
* Captures and handles all cakeError() calls.
* Displays helpful framework errors when debug > 1.
* When debug < 1 cakeError() will render 404 or 500 errors.
*
* @package cake
* @subpackage cake.cake.libs
*/
class ErrorHandler extends Object {
/**
* Controller instance.
*
* @var Controller
* @access public
*/
var $controller = null;
/**
* Class constructor.
*
* @param string $method Method producing the error
* @param array $messages Error messages
*/
function __construct($method, $messages) {
App::import('Core', 'Sanitize');
static $__previousError = null;
if ($__previousError != array($method, $messages)) {
$__previousError = array($method, $messages);
$this->controller =& new CakeErrorController();
} else {
$this->controller =& new Controller();
$this->controller->viewPath = 'errors';
}
$options = array('escape' => false);
$messages = Sanitize::clean($messages, $options);
if (!isset($messages[0])) {
$messages = array($messages);
}
if (method_exists($this->controller, 'apperror')) {
return $this->controller->appError($method, $messages);
}
if (!in_array(strtolower($method), array_map('strtolower', get_class_methods($this)))) {
$method = 'error';
}
if ($method !== 'error') {
if (Configure::read('debug') == 0) {
$parentClass = get_parent_class($this);
if (strtolower($parentClass) != 'errorhandler') {
$method = 'error404';
}
$parentMethods = array_map('strtolower', get_class_methods($parentClass));
if (in_array(strtolower($method), $parentMethods)) {
$method = 'error404';
}
if (isset($messages[0]['code']) && $messages[0]['code'] == 500) {
$method = 'error500';
}
}
}
$this->dispatchMethod($method, $messages);
$this->_stop();
}
/**
* Displays an error page (e.g. 404 Not found).
*
* @param array $params Parameters for controller
* @access public
*/
function error($params) {
extract($params, EXTR_OVERWRITE);
$this->controller->set(array(
'code' => $code,
'name' => $name,
'message' => $message,
'title' => $code . ' ' . $name
));
$this->_outputMessage('error404');
}
/**
* Convenience method to display a 404 page.
*
* @param array $params Parameters for controller
* @access public
*/
function error404($params) {
extract($params, EXTR_OVERWRITE);
if (!isset($url)) {
$url = $this->controller->here;
}
$url = Router::normalize($url);
$this->controller->header("HTTP/1.0 404 Not Found");
$this->controller->set(array(
'code' => '404',
'name' => __('Not Found', true),
'message' => h($url),
'base' => $this->controller->base
));
$this->_outputMessage('error404');
}
/**
* Convenience method to display a 500 page.
*
* @param array $params Parameters for controller
* @access public
*/
function error500($params) {
extract($params, EXTR_OVERWRITE);
if (!isset($url)) {
$url = $this->controller->here;
}
$url = Router::normalize($url);
$this->controller->header("HTTP/1.0 500 Internal Server Error");
$this->controller->set(array(
'code' => '500',
'name' => __('An Internal Error Has Occurred', true),
'message' => h($url),
'base' => $this->controller->base
));
$this->_outputMessage('error500');
}
/**
* Renders the Missing Controller web page.
*
* @param array $params Parameters for controller
* @access public
*/
function missingController($params) {
extract($params, EXTR_OVERWRITE);
$controllerName = str_replace('Controller', '', $className);
$this->controller->set(array(
'controller' => $className,
'controllerName' => $controllerName,
'title' => __('Missing Controller', true)
));
$this->_outputMessage('missingController');
}
/**
* Renders the Missing Action web page.
*
* @param array $params Parameters for controller
* @access public
*/
function missingAction($params) {
extract($params, EXTR_OVERWRITE);
$controllerName = str_replace('Controller', '', $className);
$this->controller->set(array(
'controller' => $className,
'controllerName' => $controllerName,
'action' => $action,
'title' => __('Missing Method in Controller', true)
));
$this->_outputMessage('missingAction');
}
/**
* Renders the Private Action web page.
*
* @param array $params Parameters for controller
* @access public
*/
function privateAction($params) {
extract($params, EXTR_OVERWRITE);
$this->controller->set(array(
'controller' => $className,
'action' => $action,
'title' => __('Trying to access private method in class', true)
));
$this->_outputMessage('privateAction');
}
/**
* Renders the Missing Table web page.
*
* @param array $params Parameters for controller
* @access public
*/
function missingTable($params) {
extract($params, EXTR_OVERWRITE);
$this->controller->header("HTTP/1.0 500 Internal Server Error");
$this->controller->set(array(
'code' => '500',
'model' => $className,
'table' => $table,
'title' => __('Missing Database Table', true)
));
$this->_outputMessage('missingTable');
}
/**
* Renders the Missing Database web page.
*
* @param array $params Parameters for controller
* @access public
*/
function missingDatabase($params = array()) {
$this->controller->header("HTTP/1.0 500 Internal Server Error");
$this->controller->set(array(
'code' => '500',
'title' => __('Scaffold Missing Database Connection', true)
));
$this->_outputMessage('missingScaffolddb');
}
/**
* Renders the Missing View web page.
*
* @param array $params Parameters for controller
* @access public
*/
function missingView($params) {
extract($params, EXTR_OVERWRITE);
$this->controller->set(array(
'controller' => $className,
'action' => $action,
'file' => $file,
'title' => __('Missing View', true)
));
$this->_outputMessage('missingView');
}
/**
* Renders the Missing Layout web page.
*
* @param array $params Parameters for controller
* @access public
*/
function missingLayout($params) {
extract($params, EXTR_OVERWRITE);
$this->controller->layout = 'default';
$this->controller->set(array(
'file' => $file,
'title' => __('Missing Layout', true)
));
$this->_outputMessage('missingLayout');
}
/**
* Renders the Database Connection web page.
*
* @param array $params Parameters for controller
* @access public
*/
function missingConnection($params) {
extract($params, EXTR_OVERWRITE);
$this->controller->header("HTTP/1.0 500 Internal Server Error");
$this->controller->set(array(
'code' => '500',
'model' => $className,
'title' => __('Missing Database Connection', true)
));
$this->_outputMessage('missingConnection');
}
/**
* Renders the Missing Helper file web page.
*
* @param array $params Parameters for controller
* @access public
*/
function missingHelperFile($params) {
extract($params, EXTR_OVERWRITE);
$this->controller->set(array(
'helperClass' => Inflector::camelize($helper) . "Helper",
'file' => $file,
'title' => __('Missing Helper File', true)
));
$this->_outputMessage('missingHelperFile');
}
/**
* Renders the Missing Helper class web page.
*
* @param array $params Parameters for controller
* @access public
*/
function missingHelperClass($params) {
extract($params, EXTR_OVERWRITE);
$this->controller->set(array(
'helperClass' => Inflector::camelize($helper) . "Helper",
'file' => $file,
'title' => __('Missing Helper Class', true)
));
$this->_outputMessage('missingHelperClass');
}
/**
* Renders the Missing Behavior file web page.
*
* @param array $params Parameters for controller
* @access public
*/
function missingBehaviorFile($params) {
extract($params, EXTR_OVERWRITE);
$this->controller->set(array(
'behaviorClass' => Inflector::camelize($behavior) . "Behavior",
'file' => $file,
'title' => __('Missing Behavior File', true)
));
$this->_outputMessage('missingBehaviorFile');
}
/**
* Renders the Missing Behavior class web page.
*
* @param array $params Parameters for controller
* @access public
*/
function missingBehaviorClass($params) {
extract($params, EXTR_OVERWRITE);
$this->controller->set(array(
'behaviorClass' => Inflector::camelize($behavior) . "Behavior",
'file' => $file,
'title' => __('Missing Behavior Class', true)
));
$this->_outputMessage('missingBehaviorClass');
}
/**
* Renders the Missing Component file web page.
*
* @param array $params Parameters for controller
* @access public
*/
function missingComponentFile($params) {
extract($params, EXTR_OVERWRITE);
$this->controller->set(array(
'controller' => $className,
'component' => $component,
'file' => $file,
'title' => __('Missing Component File', true)
));
$this->_outputMessage('missingComponentFile');
}
/**
* Renders the Missing Component class web page.
*
* @param array $params Parameters for controller
* @access public
*/
function missingComponentClass($params) {
extract($params, EXTR_OVERWRITE);
$this->controller->set(array(
'controller' => $className,
'component' => $component,
'file' => $file,
'title' => __('Missing Component Class', true)
));
$this->_outputMessage('missingComponentClass');
}
/**
* Renders the Missing Model class web page.
*
* @param unknown_type $params Parameters for controller
* @access public
*/
function missingModel($params) {
extract($params, EXTR_OVERWRITE);
$this->controller->set(array(
'model' => $className,
'title' => __('Missing Model', true)
));
$this->_outputMessage('missingModel');
}
/**
* Output message
*
* @access protected
*/
function _outputMessage($template) {
$this->controller->render($template);
$this->controller->afterFilter();
echo $this->controller->output;
}
}

View File

@@ -0,0 +1,544 @@
<?php
/**
* Convenience class for reading, writing and appending to files.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs
* @since CakePHP(tm) v 0.2.9
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Included libraries.
*
*/
if (!class_exists('Object')) {
require LIBS . 'object.php';
}
if (!class_exists('Folder')) {
require LIBS . 'folder.php';
}
/**
* Convenience class for reading, writing and appending to files.
*
* @package cake
* @subpackage cake.cake.libs
*/
class File extends Object {
/**
* Folder object of the File
*
* @var Folder
* @access public
*/
var $Folder = null;
/**
* Filename
*
* @var string
* @access public
*/
var $name = null;
/**
* file info
*
* @var string
* @access public
*/
var $info = array();
/**
* Holds the file handler resource if the file is opened
*
* @var resource
* @access public
*/
var $handle = null;
/**
* enable locking for file reading and writing
*
* @var boolean
* @access public
*/
var $lock = null;
/**
* path property
*
* Current file's absolute path
*
* @var mixed null
* @access public
*/
var $path = null;
/**
* Constructor
*
* @param string $path Path to file
* @param boolean $create Create file if it does not exist (if true)
* @param integer $mode Mode to apply to the folder holding the file
* @access private
*/
function __construct($path, $create = false, $mode = 0755) {
parent::__construct();
$this->Folder =& new Folder(dirname($path), $create, $mode);
if (!is_dir($path)) {
$this->name = basename($path);
}
$this->pwd();
$create && !$this->exists() && $this->safe($path) && $this->create();
}
/**
* Closes the current file if it is opened
*
* @access private
*/
function __destruct() {
$this->close();
}
/**
* Creates the File.
*
* @return boolean Success
* @access public
*/
function create() {
$dir = $this->Folder->pwd();
if (is_dir($dir) && is_writable($dir) && !$this->exists()) {
$old = umask(0);
if (touch($this->path)) {
umask($old);
return true;
}
}
return false;
}
/**
* Opens the current file with a given $mode
*
* @param string $mode A valid 'fopen' mode string (r|w|a ...)
* @param boolean $force If true then the file will be re-opened even if its already opened, otherwise it won't
* @return boolean True on success, false on failure
* @access public
*/
function open($mode = 'r', $force = false) {
if (!$force && is_resource($this->handle)) {
return true;
}
clearstatcache();
if ($this->exists() === false) {
if ($this->create() === false) {
return false;
}
}
$this->handle = fopen($this->path, $mode);
if (is_resource($this->handle)) {
return true;
}
return false;
}
/**
* Return the contents of this File as a string.
*
* @param string $bytes where to start
* @param string $mode A `fread` compatible mode.
* @param boolean $force If true then the file will be re-opened even if its already opened, otherwise it won't
* @return mixed string on success, false on failure
* @access public
*/
function read($bytes = false, $mode = 'rb', $force = false) {
if ($bytes === false && $this->lock === null) {
return file_get_contents($this->path);
}
if ($this->open($mode, $force) === false) {
return false;
}
if ($this->lock !== null && flock($this->handle, LOCK_SH) === false) {
return false;
}
if (is_int($bytes)) {
return fread($this->handle, $bytes);
}
$data = '';
while (!feof($this->handle)) {
$data .= fgets($this->handle, 4096);
}
if ($this->lock !== null) {
flock($this->handle, LOCK_UN);
}
if ($bytes === false) {
$this->close();
}
return trim($data);
}
/**
* Sets or gets the offset for the currently opened file.
*
* @param mixed $offset The $offset in bytes to seek. If set to false then the current offset is returned.
* @param integer $seek PHP Constant SEEK_SET | SEEK_CUR | SEEK_END determining what the $offset is relative to
* @return mixed True on success, false on failure (set mode), false on failure or integer offset on success (get mode)
* @access public
*/
function offset($offset = false, $seek = SEEK_SET) {
if ($offset === false) {
if (is_resource($this->handle)) {
return ftell($this->handle);
}
} elseif ($this->open() === true) {
return fseek($this->handle, $offset, $seek) === 0;
}
return false;
}
/**
* Prepares a ascii string for writing. Converts line endings to the
* correct terminator for the current platform. If windows "\r\n" will be used
* all other platforms will use "\n"
*
* @param string $data Data to prepare for writing.
* @return string The with converted line endings.
* @access public
*/
function prepare($data, $forceWindows = false) {
$lineBreak = "\n";
if (DIRECTORY_SEPARATOR == '\\' || $forceWindows === true) {
$lineBreak = "\r\n";
}
return strtr($data, array("\r\n" => $lineBreak, "\n" => $lineBreak, "\r" => $lineBreak));
}
/**
* Write given data to this File.
*
* @param string $data Data to write to this File.
* @param string $mode Mode of writing. {@link http://php.net/fwrite See fwrite()}.
* @param string $force force the file to open
* @return boolean Success
* @access public
*/
function write($data, $mode = 'w', $force = false) {
$success = false;
if ($this->open($mode, $force) === true) {
if ($this->lock !== null) {
if (flock($this->handle, LOCK_EX) === false) {
return false;
}
}
if (fwrite($this->handle, $data) !== false) {
$success = true;
}
if ($this->lock !== null) {
flock($this->handle, LOCK_UN);
}
}
return $success;
}
/**
* Append given data string to this File.
*
* @param string $data Data to write
* @param string $force force the file to open
* @return boolean Success
* @access public
*/
function append($data, $force = false) {
return $this->write($data, 'a', $force);
}
/**
* Closes the current file if it is opened.
*
* @return boolean True if closing was successful or file was already closed, otherwise false
* @access public
*/
function close() {
if (!is_resource($this->handle)) {
return true;
}
return fclose($this->handle);
}
/**
* Deletes the File.
*
* @return boolean Success
* @access public
*/
function delete() {
clearstatcache();
if ($this->exists()) {
return unlink($this->path);
}
return false;
}
/**
* Returns the File info.
*
* @return string The File extension
* @access public
*/
function info() {
if ($this->info == null) {
$this->info = pathinfo($this->path);
}
if (!isset($this->info['filename'])) {
$this->info['filename'] = $this->name();
}
return $this->info;
}
/**
* Returns the File extension.
*
* @return string The File extension
* @access public
*/
function ext() {
if ($this->info == null) {
$this->info();
}
if (isset($this->info['extension'])) {
return $this->info['extension'];
}
return false;
}
/**
* Returns the File name without extension.
*
* @return string The File name without extension.
* @access public
*/
function name() {
if ($this->info == null) {
$this->info();
}
if (isset($this->info['extension'])) {
return basename($this->name, '.'.$this->info['extension']);
} elseif ($this->name) {
return $this->name;
}
return false;
}
/**
* makes filename safe for saving
*
* @param string $name The name of the file to make safe if different from $this->name
* @param strin $ext The name of the extension to make safe if different from $this->ext
* @return string $ext the extension of the file
* @access public
*/
function safe($name = null, $ext = null) {
if (!$name) {
$name = $this->name;
}
if (!$ext) {
$ext = $this->ext();
}
return preg_replace( "/(?:[^\w\.-]+)/", "_", basename($name, $ext));
}
/**
* Get md5 Checksum of file with previous check of Filesize
*
* @param mixed $maxsize in MB or true to force
* @return string md5 Checksum {@link http://php.net/md5_file See md5_file()}
* @access public
*/
function md5($maxsize = 5) {
if ($maxsize === true) {
return md5_file($this->path);
}
$size = $this->size();
if ($size && $size < ($maxsize * 1024) * 1024) {
return md5_file($this->path);
}
return false;
}
/**
* Returns the full path of the File.
*
* @return string Full path to file
* @access public
*/
function pwd() {
if (is_null($this->path)) {
$this->path = $this->Folder->slashTerm($this->Folder->pwd()) . $this->name;
}
return $this->path;
}
/**
* Returns true if the File exists.
*
* @return boolean true if it exists, false otherwise
* @access public
*/
function exists() {
return (file_exists($this->path) && is_file($this->path));
}
/**
* Returns the "chmod" (permissions) of the File.
*
* @return string Permissions for the file
* @access public
*/
function perms() {
if ($this->exists()) {
return substr(sprintf('%o', fileperms($this->path)), -4);
}
return false;
}
/**
* Returns the Filesize
*
* @return integer size of the file in bytes, or false in case of an error
* @access public
*/
function size() {
if ($this->exists()) {
return filesize($this->path);
}
return false;
}
/**
* Returns true if the File is writable.
*
* @return boolean true if its writable, false otherwise
* @access public
*/
function writable() {
return is_writable($this->path);
}
/**
* Returns true if the File is executable.
*
* @return boolean true if its executable, false otherwise
* @access public
*/
function executable() {
return is_executable($this->path);
}
/**
* Returns true if the File is readable.
*
* @return boolean true if file is readable, false otherwise
* @access public
*/
function readable() {
return is_readable($this->path);
}
/**
* Returns the File's owner.
*
* @return integer the Fileowner
* @access public
*/
function owner() {
if ($this->exists()) {
return fileowner($this->path);
}
return false;
}
/**
* Returns the File's group.
*
* @return integer the Filegroup
* @access public
*/
function group() {
if ($this->exists()) {
return filegroup($this->path);
}
return false;
}
/**
* Returns last access time.
*
* @return integer timestamp Timestamp of last access time
* @access public
*/
function lastAccess() {
if ($this->exists()) {
return fileatime($this->path);
}
return false;
}
/**
* Returns last modified time.
*
* @return integer timestamp Timestamp of last modification
* @access public
*/
function lastChange() {
if ($this->exists()) {
return filemtime($this->path);
}
return false;
}
/**
* Returns the current folder.
*
* @return Folder Current folder
* @access public
*/
function &Folder() {
return $this->Folder;
}
/**
* Copy the File to $dest
*
* @param string $dest destination for the copy
* @param boolean $overwrite Overwrite $dest if exists
* @return boolean Succes
* @access public
*/
function copy($dest, $overwrite = true) {
if (!$this->exists() || is_file($dest) && !$overwrite) {
return false;
}
return copy($this->path, $dest);
}
}

View File

@@ -0,0 +1,787 @@
<?php
/**
* Convenience class for handling directories.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs
* @since CakePHP(tm) v 0.2.9
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Included libraries.
*
*/
if (!class_exists('Object')) {
require LIBS . 'object.php';
}
/**
* Folder structure browser, lists folders and files.
* Provides an Object interface for Common directory related tasks.
*
* @package cake
* @subpackage cake.cake.libs
*/
class Folder extends Object {
/**
* Path to Folder.
*
* @var string
* @access public
*/
var $path = null;
/**
* Sortedness. Whether or not list results
* should be sorted by name.
*
* @var boolean
* @access public
*/
var $sort = false;
/**
* Mode to be used on create. Does nothing on windows platforms.
*
* @var integer
* @access public
*/
var $mode = 0755;
/**
* Holds messages from last method.
*
* @var array
* @access private
*/
var $__messages = array();
/**
* Holds errors from last method.
*
* @var array
* @access private
*/
var $__errors = false;
/**
* Holds array of complete directory paths.
*
* @var array
* @access private
*/
var $__directories;
/**
* Holds array of complete file paths.
*
* @var array
* @access private
*/
var $__files;
/**
* Constructor.
*
* @param string $path Path to folder
* @param boolean $create Create folder if not found
* @param mixed $mode Mode (CHMOD) to apply to created folder, false to ignore
*/
function __construct($path = false, $create = false, $mode = false) {
parent::__construct();
if (empty($path)) {
$path = TMP;
}
if ($mode) {
$this->mode = $mode;
}
if (!file_exists($path) && $create === true) {
$this->create($path, $this->mode);
}
if (!Folder::isAbsolute($path)) {
$path = realpath($path);
}
if (!empty($path)) {
$this->cd($path);
}
}
/**
* Return current path.
*
* @return string Current path
* @access public
*/
function pwd() {
return $this->path;
}
/**
* Change directory to $path.
*
* @param string $path Path to the directory to change to
* @return string The new path. Returns false on failure
* @access public
*/
function cd($path) {
$path = $this->realpath($path);
if (is_dir($path)) {
return $this->path = $path;
}
return false;
}
/**
* Returns an array of the contents of the current directory.
* The returned array holds two arrays: One of directories and one of files.
*
* @param boolean $sort Whether you want the results sorted, set this and the sort property
* to false to get unsorted results.
* @param mixed $exceptions Either an array or boolean true will not grab dot files
* @param boolean $fullPath True returns the full path
* @return mixed Contents of current directory as an array, an empty array on failure
* @access public
*/
function read($sort = true, $exceptions = false, $fullPath = false) {
$dirs = $files = array();
if (!$this->pwd()) {
return array($dirs, $files);
}
if (is_array($exceptions)) {
$exceptions = array_flip($exceptions);
}
$skipHidden = isset($exceptions['.']) || $exceptions === true;
if (false === ($dir = @opendir($this->path))) {
return array($dirs, $files);
}
while (false !== ($item = readdir($dir))) {
if ($item === '.' || $item === '..' || ($skipHidden && $item[0] === '.') || isset($exceptions[$item])) {
continue;
}
$path = Folder::addPathElement($this->path, $item);
if (is_dir($path)) {
$dirs[] = $fullPath ? $path : $item;
} else {
$files[] = $fullPath ? $path : $item;
}
}
if ($sort || $this->sort) {
sort($dirs);
sort($files);
}
closedir($dir);
return array($dirs, $files);
}
/**
* Returns an array of all matching files in current directory.
*
* @param string $pattern Preg_match pattern (Defaults to: .*)
* @param boolean $sort Whether results should be sorted.
* @return array Files that match given pattern
* @access public
*/
function find($regexpPattern = '.*', $sort = false) {
list($dirs, $files) = $this->read($sort);
return array_values(preg_grep('/^' . $regexpPattern . '$/i', $files)); ;
}
/**
* Returns an array of all matching files in and below current directory.
*
* @param string $pattern Preg_match pattern (Defaults to: .*)
* @param boolean $sort Whether results should be sorted.
* @return array Files matching $pattern
* @access public
*/
function findRecursive($pattern = '.*', $sort = false) {
if (!$this->pwd()) {
return array();
}
$startsOn = $this->path;
$out = $this->_findRecursive($pattern, $sort);
$this->cd($startsOn);
return $out;
}
/**
* Private helper function for findRecursive.
*
* @param string $pattern Pattern to match against
* @param boolean $sort Whether results should be sorted.
* @return array Files matching pattern
* @access private
*/
function _findRecursive($pattern, $sort = false) {
list($dirs, $files) = $this->read($sort);
$found = array();
foreach ($files as $file) {
if (preg_match('/^' . $pattern . '$/i', $file)) {
$found[] = Folder::addPathElement($this->path, $file);
}
}
$start = $this->path;
foreach ($dirs as $dir) {
$this->cd(Folder::addPathElement($start, $dir));
$found = array_merge($found, $this->findRecursive($pattern, $sort));
}
return $found;
}
/**
* Returns true if given $path is a Windows path.
*
* @param string $path Path to check
* @return boolean true if windows path, false otherwise
* @access public
* @static
*/
function isWindowsPath($path) {
return (bool)preg_match('/^[A-Z]:\\\\/i', $path);
}
/**
* Returns true if given $path is an absolute path.
*
* @param string $path Path to check
* @return bool true if path is absolute.
* @access public
* @static
*/
function isAbsolute($path) {
return !empty($path) && ($path[0] === '/' || preg_match('/^[A-Z]:\\\\/i', $path));
}
/**
* Returns a correct set of slashes for given $path. (\\ for Windows paths and / for other paths.)
*
* @param string $path Path to check
* @return string Set of slashes ("\\" or "/")
* @access public
* @static
*/
function normalizePath($path) {
return Folder::correctSlashFor($path);
}
/**
* Returns a correct set of slashes for given $path. (\\ for Windows paths and / for other paths.)
*
* @param string $path Path to check
* @return string Set of slashes ("\\" or "/")
* @access public
* @static
*/
function correctSlashFor($path) {
return (Folder::isWindowsPath($path)) ? '\\' : '/';
}
/**
* Returns $path with added terminating slash (corrected for Windows or other OS).
*
* @param string $path Path to check
* @return string Path with ending slash
* @access public
* @static
*/
function slashTerm($path) {
if (Folder::isSlashTerm($path)) {
return $path;
}
return $path . Folder::correctSlashFor($path);
}
/**
* Returns $path with $element added, with correct slash in-between.
*
* @param string $path Path
* @param string $element Element to and at end of path
* @return string Combined path
* @access public
* @static
*/
function addPathElement($path, $element) {
return rtrim($path, DS) . DS . $element;
}
/**
* Returns true if the File is in a given CakePath.
*
* @param string $path The path to check.
* @return bool
* @access public
*/
function inCakePath($path = '') {
$dir = substr(Folder::slashTerm(ROOT), 0, -1);
$newdir = $dir . $path;
return $this->inPath($newdir);
}
/**
* Returns true if the File is in given path.
*
* @param string $path The path to check that the current pwd() resides with in.
* @param boolean $reverse
* @return bool
* @access public
*/
function inPath($path = '', $reverse = false) {
$dir = Folder::slashTerm($path);
$current = Folder::slashTerm($this->pwd());
if (!$reverse) {
$return = preg_match('/^(.*)' . preg_quote($dir, '/') . '(.*)/', $current);
} else {
$return = preg_match('/^(.*)' . preg_quote($current, '/') . '(.*)/', $dir);
}
return (bool)$return;
}
/**
* Change the mode on a directory structure recursively. This includes changing the mode on files as well.
*
* @param string $path The path to chmod
* @param integer $mode octal value 0755
* @param boolean $recursive chmod recursively, set to false to only change the current directory.
* @param array $exceptions array of files, directories to skip
* @return boolean Returns TRUE on success, FALSE on failure
* @access public
*/
function chmod($path, $mode = false, $recursive = true, $exceptions = array()) {
if (!$mode) {
$mode = $this->mode;
}
if ($recursive === false && is_dir($path)) {
if (@chmod($path, intval($mode, 8))) {
$this->__messages[] = sprintf(__('%s changed to %s', true), $path, $mode);
return true;
}
$this->__errors[] = sprintf(__('%s NOT changed to %s', true), $path, $mode);
return false;
}
if (is_dir($path)) {
$paths = $this->tree($path);
foreach ($paths as $type) {
foreach ($type as $key => $fullpath) {
$check = explode(DS, $fullpath);
$count = count($check);
if (in_array($check[$count - 1], $exceptions)) {
continue;
}
if (@chmod($fullpath, intval($mode, 8))) {
$this->__messages[] = sprintf(__('%s changed to %s', true), $fullpath, $mode);
} else {
$this->__errors[] = sprintf(__('%s NOT changed to %s', true), $fullpath, $mode);
}
}
}
if (empty($this->__errors)) {
return true;
}
}
return false;
}
/**
* Returns an array of nested directories and files in each directory
*
* @param string $path the directory path to build the tree from
* @param mixed $exceptions Array of files to exclude, defaults to excluding hidden files.
* @param string $type either file or dir. null returns both files and directories
* @return mixed array of nested directories and files in each directory
* @access public
*/
function tree($path, $exceptions = true, $type = null) {
$original = $this->path;
$path = rtrim($path, DS);
if (!$this->cd($path)) {
if ($type === null) {
return array(array(), array());
}
return array();
}
$this->__files = array();
$this->__directories = array($this->realpath($path));
$directories = array();
if ($exceptions === false) {
$exceptions = true;
}
while (!empty($this->__directories)) {
$dir = array_pop($this->__directories);
$this->__tree($dir, $exceptions);
$directories[] = $dir;
}
if ($type === null) {
return array($directories, $this->__files);
}
if ($type === 'dir') {
return $directories;
}
$this->cd($original);
return $this->__files;
}
/**
* Private method to list directories and files in each directory
*
* @param string $path The Path to read.
* @param mixed $exceptions Array of files to exclude from the read that will be performed.
* @access private
*/
function __tree($path, $exceptions) {
$this->path = $path;
list($dirs, $files) = $this->read(false, $exceptions, true);
$this->__directories = array_merge($this->__directories, $dirs);
$this->__files = array_merge($this->__files, $files);
}
/**
* Create a directory structure recursively. Can be used to create
* deep path structures like `/foo/bar/baz/shoe/horn`
*
* @param string $pathname The directory structure to create
* @param integer $mode octal value 0755
* @return boolean Returns TRUE on success, FALSE on failure
* @access public
*/
function create($pathname, $mode = false) {
if (is_dir($pathname) || empty($pathname)) {
return true;
}
if (!$mode) {
$mode = $this->mode;
}
if (is_file($pathname)) {
$this->__errors[] = sprintf(__('%s is a file', true), $pathname);
return false;
}
$pathname = rtrim($pathname, DS);
$nextPathname = substr($pathname, 0, strrpos($pathname, DS));
if ($this->create($nextPathname, $mode)) {
if (!file_exists($pathname)) {
$old = umask(0);
if (mkdir($pathname, $mode)) {
umask($old);
$this->__messages[] = sprintf(__('%s created', true), $pathname);
return true;
} else {
umask($old);
$this->__errors[] = sprintf(__('%s NOT created', true), $pathname);
return false;
}
}
}
return false;
}
/**
* Returns the size in bytes of this Folder and its contents.
*
* @param string $directory Path to directory
* @return int size in bytes of current folder
* @access public
*/
function dirsize() {
$size = 0;
$directory = Folder::slashTerm($this->path);
$stack = array($directory);
$count = count($stack);
for ($i = 0, $j = $count; $i < $j; ++$i) {
if (is_file($stack[$i])) {
$size += filesize($stack[$i]);
} elseif (is_dir($stack[$i])) {
$dir = dir($stack[$i]);
if ($dir) {
while (false !== ($entry = $dir->read())) {
if ($entry === '.' || $entry === '..') {
continue;
}
$add = $stack[$i] . $entry;
if (is_dir($stack[$i] . $entry)) {
$add = Folder::slashTerm($add);
}
$stack[] = $add;
}
$dir->close();
}
}
$j = count($stack);
}
return $size;
}
/**
* Recursively Remove directories if the system allows.
*
* @param string $path Path of directory to delete
* @return boolean Success
* @access public
*/
function delete($path = null) {
if (!$path) {
$path = $this->pwd();
}
if (!$path) {
return null;
}
$path = Folder::slashTerm($path);
if (is_dir($path) === true) {
$normalFiles = glob($path . '*');
$hiddenFiles = glob($path . '\.?*');
$normalFiles = $normalFiles ? $normalFiles : array();
$hiddenFiles = $hiddenFiles ? $hiddenFiles : array();
$files = array_merge($normalFiles, $hiddenFiles);
if (is_array($files)) {
foreach ($files as $file) {
if (preg_match('/(\.|\.\.)$/', $file)) {
continue;
}
if (is_file($file) === true) {
if (@unlink($file)) {
$this->__messages[] = sprintf(__('%s removed', true), $file);
} else {
$this->__errors[] = sprintf(__('%s NOT removed', true), $file);
}
} elseif (is_dir($file) === true && $this->delete($file) === false) {
return false;
}
}
}
$path = substr($path, 0, strlen($path) - 1);
if (rmdir($path) === false) {
$this->__errors[] = sprintf(__('%s NOT removed', true), $path);
return false;
} else {
$this->__messages[] = sprintf(__('%s removed', true), $path);
}
}
return true;
}
/**
* Recursive directory copy.
*
* ### Options
*
* - `to` The directory to copy to.
* - `from` The directory to copy from, this will cause a cd() to occur, changing the results of pwd().
* - `chmod` The mode to copy the files/directories with.
* - `skip` Files/directories to skip.
*
* @param mixed $options Either an array of options (see above) or a string of the destination directory.
* @return bool Success
* @access public
*/
function copy($options = array()) {
if (!$this->pwd()) {
return false;
}
$to = null;
if (is_string($options)) {
$to = $options;
$options = array();
}
$options = array_merge(array('to' => $to, 'from' => $this->path, 'mode' => $this->mode, 'skip' => array()), $options);
$fromDir = $options['from'];
$toDir = $options['to'];
$mode = $options['mode'];
if (!$this->cd($fromDir)) {
$this->__errors[] = sprintf(__('%s not found', true), $fromDir);
return false;
}
if (!is_dir($toDir)) {
$this->create($toDir, $mode);
}
if (!is_writable($toDir)) {
$this->__errors[] = sprintf(__('%s not writable', true), $toDir);
return false;
}
$exceptions = array_merge(array('.', '..', '.svn'), $options['skip']);
if ($handle = @opendir($fromDir)) {
while (false !== ($item = readdir($handle))) {
if (!in_array($item, $exceptions)) {
$from = Folder::addPathElement($fromDir, $item);
$to = Folder::addPathElement($toDir, $item);
if (is_file($from)) {
if (copy($from, $to)) {
chmod($to, intval($mode, 8));
touch($to, filemtime($from));
$this->__messages[] = sprintf(__('%s copied to %s', true), $from, $to);
} else {
$this->__errors[] = sprintf(__('%s NOT copied to %s', true), $from, $to);
}
}
if (is_dir($from) && !file_exists($to)) {
$old = umask(0);
if (mkdir($to, $mode)) {
umask($old);
$old = umask(0);
chmod($to, $mode);
umask($old);
$this->__messages[] = sprintf(__('%s created', true), $to);
$options = array_merge($options, array('to'=> $to, 'from'=> $from));
$this->copy($options);
} else {
$this->__errors[] = sprintf(__('%s not created', true), $to);
}
}
}
}
closedir($handle);
} else {
return false;
}
if (!empty($this->__errors)) {
return false;
}
return true;
}
/**
* Recursive directory move.
*
* ### Options
*
* - `to` The directory to copy to.
* - `from` The directory to copy from, this will cause a cd() to occur, changing the results of pwd().
* - `chmod` The mode to copy the files/directories with.
* - `skip` Files/directories to skip.
*
* @param array $options (to, from, chmod, skip)
* @return boolean Success
* @access public
*/
function move($options) {
$to = null;
if (is_string($options)) {
$to = $options;
$options = (array)$options;
}
$options = array_merge(array('to' => $to, 'from' => $this->path, 'mode' => $this->mode, 'skip' => array()), $options);
if ($this->copy($options)) {
if ($this->delete($options['from'])) {
return $this->cd($options['to']);
}
}
return false;
}
/**
* get messages from latest method
*
* @return array
* @access public
*/
function messages() {
return $this->__messages;
}
/**
* get error from latest method
*
* @return array
* @access public
*/
function errors() {
return $this->__errors;
}
/**
* Get the real path (taking ".." and such into account)
*
* @param string $path Path to resolve
* @return string The resolved path
*/
function realpath($path) {
$path = str_replace('/', DS, trim($path));
if (strpos($path, '..') === false) {
if (!Folder::isAbsolute($path)) {
$path = Folder::addPathElement($this->path, $path);
}
return $path;
}
$parts = explode(DS, $path);
$newparts = array();
$newpath = '';
if ($path[0] === DS) {
$newpath = DS;
}
while (($part = array_shift($parts)) !== NULL) {
if ($part === '.' || $part === '') {
continue;
}
if ($part === '..') {
if (!empty($newparts)) {
array_pop($newparts);
continue;
} else {
return false;
}
}
$newparts[] = $part;
}
$newpath .= implode(DS, $newparts);
return Folder::slashTerm($newpath);
}
/**
* Returns true if given $path ends in a slash (i.e. is slash-terminated).
*
* @param string $path Path to check
* @return boolean true if path ends with slash, false otherwise
* @access public
* @static
*/
function isSlashTerm($path) {
$lastChar = $path[strlen($path) - 1];
return $lastChar === '/' || $lastChar === '\\';
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,566 @@
<?php
/**
* Internationalization
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs
* @since CakePHP(tm) v 1.2.0.4116
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Included libraries.
*/
App::import('Core', array('l10n', 'Multibyte'));
/**
* I18n handles translation of Text and time format strings.
*
* @package cake
* @subpackage cake.cake.libs
*/
class I18n extends Object {
/**
* Instance of the I10n class for localization
*
* @var I10n
* @access public
*/
var $l10n = null;
/**
* Current domain of translation
*
* @var string
* @access public
*/
var $domain = null;
/**
* Current category of translation
*
* @var string
* @access public
*/
var $category = 'LC_MESSAGES';
/**
* Current language used for translations
*
* @var string
* @access private
*/
var $__lang = null;
/**
* Translation strings for a specific domain read from the .mo or .po files
*
* @var array
* @access private
*/
var $__domains = array();
/**
* Set to true when I18N::__bindTextDomain() is called for the first time.
* If a translation file is found it is set to false again
*
* @var boolean
* @access private
*/
var $__noLocale = false;
/**
* Set to true when I18N::__bindTextDomain() is called for the first time.
* If a translation file is found it is set to false again
*
* @var array
* @access private
*/
var $__categories = array(
'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME', 'LC_MESSAGES'
);
/**
* Return a static instance of the I18n class
*
* @return object I18n
* @access public
*/
function &getInstance() {
static $instance = array();
if (!$instance) {
$instance[0] =& new I18n();
$instance[0]->l10n =& new L10n();
}
return $instance[0];
}
/**
* Used by the translation functions in basics.php
* Can also be used like I18n::translate(); but only if the App::import('I18n'); has been used to load the class.
*
* @param string $singular String to translate
* @param string $plural Plural string (if any)
* @param string $domain Domain The domain of the translation. Domains are often used by plugin translations
* @param string $category Category The integer value of the category to use.
* @param integer $count Count Count is used with $plural to choose the correct plural form.
* @return string translated string.
* @access public
*/
function translate($singular, $plural = null, $domain = null, $category = 6, $count = null) {
$_this =& I18n::getInstance();
if (strpos($singular, "\r\n") !== false) {
$singular = str_replace("\r\n", "\n", $singular);
}
if ($plural !== null && strpos($plural, "\r\n") !== false) {
$plural = str_replace("\r\n", "\n", $plural);
}
if (is_numeric($category)) {
$_this->category = $_this->__categories[$category];
}
$language = Configure::read('Config.language');
if (!empty($_SESSION['Config']['language'])) {
$language = $_SESSION['Config']['language'];
}
if (($_this->__lang && $_this->__lang !== $language) || !$_this->__lang) {
$lang = $_this->l10n->get($language);
$_this->__lang = $lang;
}
if (is_null($domain)) {
$domain = 'default';
}
$_this->domain = $domain . '_' . $_this->l10n->lang;
if (empty($_this->__domains[$domain][$_this->__lang])) {
$_this->__domains[$domain][$_this->__lang] = Cache::read($_this->domain, '_cake_core_');
}
if (empty($_this->__domains[$domain][$_this->__lang][$_this->category])) {
$_this->__bindTextDomain($domain);
Cache::write($_this->domain, $_this->__domains[$domain][$_this->__lang], '_cake_core_');
}
if ($_this->category == 'LC_TIME') {
return $_this->__translateTime($singular,$domain);
}
if (!isset($count)) {
$plurals = 0;
} elseif (!empty($_this->__domains[$domain][$_this->__lang][$_this->category]["%plural-c"]) && $_this->__noLocale === false) {
$header = $_this->__domains[$domain][$_this->__lang][$_this->category]["%plural-c"];
$plurals = $_this->__pluralGuess($header, $count);
} else {
if ($count != 1) {
$plurals = 1;
} else {
$plurals = 0;
}
}
if (!empty($_this->__domains[$domain][$_this->__lang][$_this->category][$singular])) {
if (($trans = $_this->__domains[$domain][$_this->__lang][$_this->category][$singular]) || ($plurals) && ($trans = $_this->__domains[$domain][$_this->__lang][$_this->category][$plural])) {
if (is_array($trans)) {
if (isset($trans[$plurals])) {
$trans = $trans[$plurals];
}
}
if (strlen($trans)) {
return $trans;
}
}
}
if (!empty($plurals)) {
return $plural;
}
return $singular;
}
/**
* Clears the domains internal data array. Useful for testing i18n.
*
* @return void
*/
function clear() {
$self =& I18n::getInstance();
$self->__domains = array();
}
/**
* Attempts to find the plural form of a string.
*
* @param string $header Type
* @param integrer $n Number
* @return integer plural match
* @access private
*/
function __pluralGuess($header, $n) {
if (!is_string($header) || $header === "nplurals=1;plural=0;" || !isset($header[0])) {
return 0;
}
if ($header === "nplurals=2;plural=n!=1;") {
return $n != 1 ? 1 : 0;
} elseif ($header === "nplurals=2;plural=n>1;") {
return $n > 1 ? 1 : 0;
}
if (strpos($header, "plurals=3")) {
if (strpos($header, "100!=11")) {
if (strpos($header, "10<=4")) {
return $n % 10 == 1 && $n % 100 != 11 ? 0 : ($n % 10 >= 2 && $n % 10 <= 4 && ($n % 100 < 10 || $n % 100 >= 20) ? 1 : 2);
} elseif (strpos($header, "100<10")) {
return $n % 10 == 1 && $n % 100 != 11 ? 0 : ($n % 10 >= 2 && ($n % 100 < 10 || $n % 100 >= 20) ? 1 : 2);
}
return $n % 10 == 1 && $n % 100 != 11 ? 0 : ($n != 0 ? 1 : 2);
} elseif (strpos($header, "n==2")) {
return $n == 1 ? 0 : ($n == 2 ? 1 : 2);
} elseif (strpos($header, "n==0")) {
return $n == 1 ? 0 : ($n == 0 || ($n % 100 > 0 && $n % 100 < 20) ? 1 : 2);
} elseif (strpos($header, "n>=2")) {
return $n == 1 ? 0 : ($n >= 2 && $n <= 4 ? 1 : 2);
} elseif (strpos($header, "10>=2")) {
return $n == 1 ? 0 : ($n % 10 >= 2 && $n % 10 <= 4 && ($n % 100 < 10 || $n % 100 >= 20) ? 1 : 2);
}
return $n % 10 == 1 ? 0 : ($n % 10 == 2 ? 1 : 2);
} elseif (strpos($header, "plurals=4")) {
if (strpos($header, "100==2")) {
return $n % 100 == 1 ? 0 : ($n % 100 == 2 ? 1 : ($n % 100 == 3 || $n % 100 == 4 ? 2 : 3));
} elseif (strpos($header, "n>=3")) {
return $n == 1 ? 0 : ($n == 2 ? 1 : ($n == 0 || ($n >= 3 && $n <= 10) ? 2 : 3));
} elseif (strpos($header, "100>=1")) {
return $n == 1 ? 0 : ($n == 0 || ($n % 100 >= 1 && $n % 100 <= 10) ? 1 : ($n % 100 >= 11 && $n % 100 <= 20 ? 2 : 3));
}
} elseif (strpos($header, "plurals=5")) {
return $n == 1 ? 0 : ($n == 2 ? 1 : ($n >= 3 && $n <= 6 ? 2 : ($n >= 7 && $n <= 10 ? 3 : 4)));
}
}
/**
* Binds the given domain to a file in the specified directory.
*
* @param string $domain Domain to bind
* @return string Domain binded
* @access private
*/
function __bindTextDomain($domain) {
$this->__noLocale = true;
$core = true;
$merge = array();
$searchPaths = App::path('locales');
$plugins = App::objects('plugin');
if (!empty($plugins)) {
foreach ($plugins as $plugin) {
$plugin = Inflector::underscore($plugin);
if ($plugin === $domain) {
$searchPaths[] = App::pluginPath($plugin) . DS . 'locale' . DS;
$searchPaths = array_reverse($searchPaths);
break;
}
}
}
foreach ($searchPaths as $directory) {
foreach ($this->l10n->languagePath as $lang) {
$file = $directory . $lang . DS . $this->category . DS . $domain;
$localeDef = $directory . $lang . DS . $this->category;
if ($core) {
$app = $directory . $lang . DS . $this->category . DS . 'core';
if (file_exists($fn = "$app.mo")) {
$this->__loadMo($fn, $domain);
$this->__noLocale = false;
$merge[$domain][$this->__lang][$this->category] = $this->__domains[$domain][$this->__lang][$this->category];
$core = null;
} elseif (file_exists($fn = "$app.po") && ($f = fopen($fn, "r"))) {
$this->__loadPo($f, $domain);
$this->__noLocale = false;
$merge[$domain][$this->__lang][$this->category] = $this->__domains[$domain][$this->__lang][$this->category];
$core = null;
}
}
if (file_exists($fn = "$file.mo")) {
$this->__loadMo($fn, $domain);
$this->__noLocale = false;
break 2;
} elseif (file_exists($fn = "$file.po") && ($f = fopen($fn, "r"))) {
$this->__loadPo($f, $domain);
$this->__noLocale = false;
break 2;
} elseif (is_file($localeDef) && ($f = fopen($localeDef, "r"))) {
$this->__loadLocaleDefinition($f, $domain);
$this->__noLocale = false;
return $domain;
}
}
}
if (empty($this->__domains[$domain][$this->__lang][$this->category])) {
$this->__domains[$domain][$this->__lang][$this->category] = array();
return $domain;
}
if ($head = $this->__domains[$domain][$this->__lang][$this->category][""]) {
foreach (explode("\n", $head) as $line) {
$header = strtok($line,":");
$line = trim(strtok("\n"));
$this->__domains[$domain][$this->__lang][$this->category]["%po-header"][strtolower($header)] = $line;
}
if (isset($this->__domains[$domain][$this->__lang][$this->category]["%po-header"]["plural-forms"])) {
$switch = preg_replace("/(?:[() {}\\[\\]^\\s*\\]]+)/", "", $this->__domains[$domain][$this->__lang][$this->category]["%po-header"]["plural-forms"]);
$this->__domains[$domain][$this->__lang][$this->category]["%plural-c"] = $switch;
unset($this->__domains[$domain][$this->__lang][$this->category]["%po-header"]);
}
$this->__domains = Set::pushDiff($this->__domains, $merge);
if (isset($this->__domains[$domain][$this->__lang][$this->category][null])) {
unset($this->__domains[$domain][$this->__lang][$this->category][null]);
}
}
return $domain;
}
/**
* Loads the binary .mo file for translation and sets the values for this translation in the var I18n::__domains
*
* @param resource $file Binary .mo file to load
* @param string $domain Domain where to load file in
* @access private
*/
function __loadMo($file, $domain) {
$data = file_get_contents($file);
if ($data) {
$header = substr($data, 0, 20);
$header = unpack("L1magic/L1version/L1count/L1o_msg/L1o_trn", $header);
extract($header);
if ((dechex($magic) == '950412de' || dechex($magic) == 'ffffffff950412de') && $version == 0) {
for ($n = 0; $n < $count; $n++) {
$r = unpack("L1len/L1offs", substr($data, $o_msg + $n * 8, 8));
$msgid = substr($data, $r["offs"], $r["len"]);
unset($msgid_plural);
if (strpos($msgid, "\000")) {
list($msgid, $msgid_plural) = explode("\000", $msgid);
}
$r = unpack("L1len/L1offs", substr($data, $o_trn + $n * 8, 8));
$msgstr = substr($data, $r["offs"], $r["len"]);
if (strpos($msgstr, "\000")) {
$msgstr = explode("\000", $msgstr);
}
$this->__domains[$domain][$this->__lang][$this->category][$msgid] = $msgstr;
if (isset($msgid_plural)) {
$this->__domains[$domain][$this->__lang][$this->category][$msgid_plural] =& $this->__domains[$domain][$this->__lang][$this->category][$msgid];
}
}
}
}
}
/**
* Loads the text .po file for translation and sets the values for this translation in the var I18n::__domains
*
* @param resource $file Text .po file to load
* @param string $domain Domain to load file in
* @return array Binded domain elements
* @access private
*/
function __loadPo($file, $domain) {
$type = 0;
$translations = array();
$translationKey = "";
$plural = 0;
$header = "";
do {
$line = trim(fgets($file));
if ($line == "" || $line[0] == "#") {
continue;
}
if (preg_match("/msgid[[:space:]]+\"(.+)\"$/i", $line, $regs)) {
$type = 1;
$translationKey = stripcslashes($regs[1]);
} elseif (preg_match("/msgid[[:space:]]+\"\"$/i", $line, $regs)) {
$type = 2;
$translationKey = "";
} elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && ($type == 1 || $type == 2 || $type == 3)) {
$type = 3;
$translationKey .= stripcslashes($regs[1]);
} elseif (preg_match("/msgstr[[:space:]]+\"(.+)\"$/i", $line, $regs) && ($type == 1 || $type == 3) && $translationKey) {
$translations[$translationKey] = stripcslashes($regs[1]);
$type = 4;
} elseif (preg_match("/msgstr[[:space:]]+\"\"$/i", $line, $regs) && ($type == 1 || $type == 3) && $translationKey) {
$type = 4;
$translations[$translationKey] = "";
} elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 4 && $translationKey) {
$translations[$translationKey] .= stripcslashes($regs[1]);
} elseif (preg_match("/msgid_plural[[:space:]]+\".*\"$/i", $line, $regs)) {
$type = 6;
} elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 6 && $translationKey) {
$type = 6;
} elseif (preg_match("/msgstr\[(\d+)\][[:space:]]+\"(.+)\"$/i", $line, $regs) && ($type == 6 || $type == 7) && $translationKey) {
$plural = $regs[1];
$translations[$translationKey][$plural] = stripcslashes($regs[2]);
$type = 7;
} elseif (preg_match("/msgstr\[(\d+)\][[:space:]]+\"\"$/i", $line, $regs) && ($type == 6 || $type == 7) && $translationKey) {
$plural = $regs[1];
$translations[$translationKey][$plural] = "";
$type = 7;
} elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 7 && $translationKey) {
$translations[$translationKey][$plural] .= stripcslashes($regs[1]);
} elseif (preg_match("/msgstr[[:space:]]+\"(.+)\"$/i", $line, $regs) && $type == 2 && !$translationKey) {
$header .= stripcslashes($regs[1]);
$type = 5;
} elseif (preg_match("/msgstr[[:space:]]+\"\"$/i", $line, $regs) && !$translationKey) {
$header = "";
$type = 5;
} elseif (preg_match("/^\"(.*)\"$/i", $line, $regs) && $type == 5) {
$header .= stripcslashes($regs[1]);
} else {
unset($translations[$translationKey]);
$type = 0;
$translationKey = "";
$plural = 0;
}
} while (!feof($file));
fclose($file);
$merge[""] = $header;
return $this->__domains[$domain][$this->__lang][$this->category] = array_merge($merge ,$translations);
}
/**
* Parses a locale definition file following the POSIX standard
*
* @param resource $file file handler
* @param string $domain Domain where locale definitions will be stored
* @return void
* @access private
*/
function __loadLocaleDefinition($file, $domain = null) {
$comment = '#';
$escape = '\\';
$currentToken = false;
$value = '';
while ($line = fgets($file)) {
$line = trim($line);
if (empty($line) || $line[0] === $comment) {
continue;
}
$parts = preg_split("/[[:space:]]+/",$line);
if ($parts[0] === 'comment_char') {
$comment = $parts[1];
continue;
}
if ($parts[0] === 'escape_char') {
$escape = $parts[1];
continue;
}
$count = count($parts);
if ($count == 2) {
$currentToken = $parts[0];
$value = $parts[1];
} elseif ($count == 1) {
$value .= $parts[0];
} else {
continue;
}
$len = strlen($value) - 1;
if ($value[$len] === $escape) {
$value = substr($value, 0, $len);
continue;
}
$mustEscape = array($escape . ',' , $escape . ';', $escape . '<', $escape . '>', $escape . $escape);
$replacements = array_map('crc32', $mustEscape);
$value = str_replace($mustEscape, $replacements, $value);
$value = explode(';', $value);
$this->__escape = $escape;
foreach ($value as $i => $val) {
$val = trim($val, '"');
$val = preg_replace_callback('/(?:<)?(.[^>]*)(?:>)?/', array(&$this, '__parseLiteralValue'), $val);
$val = str_replace($replacements, $mustEscape, $val);
$value[$i] = $val;
}
if (count($value) == 1) {
$this->__domains[$domain][$this->__lang][$this->category][$currentToken] = array_pop($value);
} else {
$this->__domains[$domain][$this->__lang][$this->category][$currentToken] = $value;
}
}
}
/**
* Auxiliary function to parse a symbol from a locale definition file
*
* @param string $string Symbol to be parsed
* @return string parsed symbol
* @access private
*/
function __parseLiteralValue($string) {
$string = $string[1];
if (substr($string, 0, 2) === $this->__escape . 'x') {
$delimiter = $this->__escape . 'x';
return join('', array_map('chr', array_map('hexdec',array_filter(explode($delimiter, $string)))));
}
if (substr($string, 0, 2) === $this->__escape . 'd') {
$delimiter = $this->__escape . 'd';
return join('', array_map('chr', array_filter(explode($delimiter, $string))));
}
if ($string[0] === $this->__escape && isset($string[1]) && is_numeric($string[1])) {
$delimiter = $this->__escape;
return join('', array_map('chr', array_filter(explode($delimiter, $string))));
}
if (substr($string, 0, 3) === 'U00') {
$delimiter = 'U00';
return join('', array_map('chr', array_map('hexdec', array_filter(explode($delimiter, $string)))));
}
if (preg_match('/U([0-9a-fA-F]{4})/', $string, $match)) {
return Multibyte::ascii(array(hexdec($match[1])));
}
return $string;
}
/**
* Returns a Time format definition from corresponding domain
*
* @param string $format Format to be translated
* @param string $domain Domain where format is stored
* @return mixed translated format string if only value or array of translated strings for corresponding format.
* @access private
*/
function __translateTime($format, $domain) {
if (!empty($this->__domains[$domain][$this->__lang]['LC_TIME'][$format])) {
if (($trans = $this->__domains[$domain][$this->__lang][$this->category][$format])) {
return $trans;
}
}
return $format;
}
}

View File

@@ -0,0 +1,626 @@
<?php
/**
* Pluralize and singularize English words.
*
* Used by Cake's naming conventions throughout the framework.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs
* @since CakePHP(tm) v 0.2.9
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Pluralize and singularize English words.
*
* Inflector pluralizes and singularizes English nouns.
* Used by Cake's naming conventions throughout the framework.
*
* @package cake
* @subpackage cake.cake.libs
* @link http://book.cakephp.org/view/1478/Inflector
*/
class Inflector {
/**
* Plural inflector rules
*
* @var array
* @access protected
*/
var $_plural = array(
'rules' => array(
'/(s)tatus$/i' => '\1\2tatuses',
'/(quiz)$/i' => '\1zes',
'/^(ox)$/i' => '\1\2en',
'/([m|l])ouse$/i' => '\1ice',
'/(matr|vert|ind)(ix|ex)$/i' => '\1ices',
'/(x|ch|ss|sh)$/i' => '\1es',
'/([^aeiouy]|qu)y$/i' => '\1ies',
'/(hive)$/i' => '\1s',
'/(?:([^f])fe|([lr])f)$/i' => '\1\2ves',
'/sis$/i' => 'ses',
'/([ti])um$/i' => '\1a',
'/(p)erson$/i' => '\1eople',
'/(m)an$/i' => '\1en',
'/(c)hild$/i' => '\1hildren',
'/(buffal|tomat)o$/i' => '\1\2oes',
'/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i',
'/us$/i' => 'uses',
'/(alias)$/i' => '\1es',
'/(ax|cris|test)is$/i' => '\1es',
'/s$/' => 's',
'/^$/' => '',
'/$/' => 's',
),
'uninflected' => array(
'.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', 'people'
),
'irregular' => array(
'atlas' => 'atlases',
'beef' => 'beefs',
'brother' => 'brothers',
'child' => 'children',
'corpus' => 'corpuses',
'cow' => 'cows',
'ganglion' => 'ganglions',
'genie' => 'genies',
'genus' => 'genera',
'graffito' => 'graffiti',
'hoof' => 'hoofs',
'loaf' => 'loaves',
'man' => 'men',
'money' => 'monies',
'mongoose' => 'mongooses',
'move' => 'moves',
'mythos' => 'mythoi',
'niche' => 'niches',
'numen' => 'numina',
'occiput' => 'occiputs',
'octopus' => 'octopuses',
'opus' => 'opuses',
'ox' => 'oxen',
'penis' => 'penises',
'person' => 'people',
'sex' => 'sexes',
'soliloquy' => 'soliloquies',
'testis' => 'testes',
'trilby' => 'trilbys',
'turf' => 'turfs'
)
);
/**
* Singular inflector rules
*
* @var array
* @access protected
*/
var $_singular = array(
'rules' => array(
'/(s)tatuses$/i' => '\1\2tatus',
'/^(.*)(menu)s$/i' => '\1\2',
'/(quiz)zes$/i' => '\\1',
'/(matr)ices$/i' => '\1ix',
'/(vert|ind)ices$/i' => '\1ex',
'/^(ox)en/i' => '\1',
'/(alias)(es)*$/i' => '\1',
'/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us',
'/([ftw]ax)es/i' => '\1',
'/(cris|ax|test)es$/i' => '\1is',
'/(shoe|slave)s$/i' => '\1',
'/(o)es$/i' => '\1',
'/ouses$/' => 'ouse',
'/([^a])uses$/' => '\1us',
'/([m|l])ice$/i' => '\1ouse',
'/(x|ch|ss|sh)es$/i' => '\1',
'/(m)ovies$/i' => '\1\2ovie',
'/(s)eries$/i' => '\1\2eries',
'/([^aeiouy]|qu)ies$/i' => '\1y',
'/([lr])ves$/i' => '\1f',
'/(tive)s$/i' => '\1',
'/(hive)s$/i' => '\1',
'/(drive)s$/i' => '\1',
'/([^fo])ves$/i' => '\1fe',
'/(^analy)ses$/i' => '\1sis',
'/(analy|ba|diagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis',
'/([ti])a$/i' => '\1um',
'/(p)eople$/i' => '\1\2erson',
'/(m)en$/i' => '\1an',
'/(c)hildren$/i' => '\1\2hild',
'/(n)ews$/i' => '\1\2ews',
'/eaus$/' => 'eau',
'/^(.*us)$/' => '\\1',
'/s$/i' => ''
),
'uninflected' => array(
'.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', '.*ss'
),
'irregular' => array(
'waves' => 'wave'
)
);
/**
* Words that should not be inflected
*
* @var array
* @access protected
*/
var $_uninflected = array(
'Amoyese', 'bison', 'Borghese', 'bream', 'breeches', 'britches', 'buffalo', 'cantus',
'carp', 'chassis', 'clippers', 'cod', 'coitus', 'Congoese', 'contretemps', 'corps',
'debris', 'diabetes', 'djinn', 'eland', 'elk', 'equipment', 'Faroese', 'flounder',
'Foochowese', 'gallows', 'Genevese', 'Genoese', 'Gilbertese', 'graffiti',
'headquarters', 'herpes', 'hijinks', 'Hottentotese', 'information', 'innings',
'jackanapes', 'Kiplingese', 'Kongoese', 'Lucchese', 'mackerel', 'Maltese', 'media',
'mews', 'moose', 'mumps', 'Nankingese', 'news', 'nexus', 'Niasese',
'Pekingese', 'Piedmontese', 'pincers', 'Pistoiese', 'pliers', 'Portuguese',
'proceedings', 'rabies', 'rice', 'rhinoceros', 'salmon', 'Sarawakese', 'scissors',
'sea[- ]bass', 'series', 'Shavese', 'shears', 'siemens', 'species', 'swine', 'testes',
'trousers', 'trout','tuna', 'Vermontese', 'Wenchowese', 'whiting', 'wildebeest',
'Yengeese'
);
/**
* Default map of accented and special characters to ASCII characters
*
* @var array
* @access protected
*/
var $_transliteration = array(
'/ä|æ|ǽ/' => 'ae',
'/ö|œ/' => 'oe',
'/ü/' => 'ue',
'/Ä/' => 'Ae',
'/Ü/' => 'Ue',
'/Ö/' => 'Oe',
'/À|Á|Â|Ã|Ä|Å|Ǻ|Ā|Ă|Ą|Ǎ/' => 'A',
'/à|á|â|ã|å|ǻ|ā|ă|ą|ǎ|ª/' => 'a',
'/Ç|Ć|Ĉ|Ċ|Č/' => 'C',
'/ç|ć|ĉ|ċ|č/' => 'c',
'/Ð|Ď|Đ/' => 'D',
'/ð|ď|đ/' => 'd',
'/È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě/' => 'E',
'/è|é|ê|ë|ē|ĕ|ė|ę|ě/' => 'e',
'/Ĝ|Ğ|Ġ|Ģ/' => 'G',
'/ĝ|ğ|ġ|ģ/' => 'g',
'/Ĥ|Ħ/' => 'H',
'/ĥ|ħ/' => 'h',
'/Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ/' => 'I',
'/ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı/' => 'i',
'/Ĵ/' => 'J',
'/ĵ/' => 'j',
'/Ķ/' => 'K',
'/ķ/' => 'k',
'/Ĺ|Ļ|Ľ|Ŀ|Ł/' => 'L',
'/ĺ|ļ|ľ|ŀ|ł/' => 'l',
'/Ñ|Ń|Ņ|Ň/' => 'N',
'/ñ|ń|ņ|ň|ʼn/' => 'n',
'/Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ/' => 'O',
'/ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º/' => 'o',
'/Ŕ|Ŗ|Ř/' => 'R',
'/ŕ|ŗ|ř/' => 'r',
'/Ś|Ŝ|Ş|Š/' => 'S',
'/ś|ŝ|ş|š|ſ/' => 's',
'/Ţ|Ť|Ŧ/' => 'T',
'/ţ|ť|ŧ/' => 't',
'/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ/' => 'U',
'/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ/' => 'u',
'/Ý|Ÿ|Ŷ/' => 'Y',
'/ý|ÿ|ŷ/' => 'y',
'/Ŵ/' => 'W',
'/ŵ/' => 'w',
'/Ź|Ż|Ž/' => 'Z',
'/ź|ż|ž/' => 'z',
'/Æ|Ǽ/' => 'AE',
'/ß/'=> 'ss',
'/IJ/' => 'IJ',
'/ij/' => 'ij',
'/Œ/' => 'OE',
'/ƒ/' => 'f'
);
/**
* Cached array identity map of pluralized words.
*
* @var array
* @access protected
*/
var $_pluralized = array();
/**
* Cached array identity map of singularized words.
*
* @var array
* @access protected
*/
var $_singularized = array();
/**
* Cached Underscore Inflections
*
* @var array
* @access protected
*/
var $_underscore = array();
/**
* Cached Camelize Inflections
*
* @var array
* @access protected
*/
var $_camelize = array();
/**
* Classify cached inflecctions
*
* @var array
* @access protected
*/
var $_classify = array();
/**
* Tablize cached inflections
*
* @var array
* @access protected
*/
var $_tableize = array();
/**
* Humanize cached inflections
*
* @var array
* @access protected
*/
var $_humanize = array();
/**
* Gets a reference to the Inflector object instance
*
* @return object
* @access public
*/
function &getInstance() {
static $instance = array();
if (!$instance) {
$instance[0] =& new Inflector();
}
return $instance[0];
}
/**
* Cache inflected values, and return if already available
*
* @param string $type Inflection type
* @param string $key Original value
* @param string $value Inflected value
* @return string Inflected value, from cache
* @access protected
*/
function _cache($type, $key, $value = false) {
$key = '_' . $key;
$type = '_' . $type;
if ($value !== false) {
$this->{$type}[$key] = $value;
return $value;
}
if (!isset($this->{$type}[$key])) {
return false;
}
return $this->{$type}[$key];
}
/**
* Adds custom inflection $rules, of either 'plural', 'singular' or 'transliteration' $type.
*
* ### Usage:
*
* {{{
* Inflector::rules('plural', array('/^(inflect)or$/i' => '\1ables'));
* Inflector::rules('plural', array(
* 'rules' => array('/^(inflect)ors$/i' => '\1ables'),
* 'uninflected' => array('dontinflectme'),
* 'irregular' => array('red' => 'redlings')
* ));
* Inflector::rules('transliteration', array('/å/' => 'aa'));
* }}}
*
* @param string $type The type of inflection, either 'plural', 'singular' or 'transliteration'
* @param array $rules Array of rules to be added.
* @param boolean $reset If true, will unset default inflections for all
* new rules that are being defined in $rules.
* @access public
* @return void
* @static
*/
function rules($type, $rules, $reset = false) {
$_this =& Inflector::getInstance();
$var = '_'.$type;
switch ($type) {
case 'transliteration':
if ($reset) {
$_this->_transliteration = $rules;
} else {
$_this->_transliteration = $rules + $_this->_transliteration;
}
break;
default:
foreach ($rules as $rule => $pattern) {
if (is_array($pattern)) {
if ($reset) {
$_this->{$var}[$rule] = $pattern;
} else {
$_this->{$var}[$rule] = array_merge($pattern, $_this->{$var}[$rule]);
}
unset($rules[$rule], $_this->{$var}['cache' . ucfirst($rule)]);
if (isset($_this->{$var}['merged'][$rule])) {
unset($_this->{$var}['merged'][$rule]);
}
if ($type === 'plural') {
$_this->_pluralized = $_this->_tableize = array();
} elseif ($type === 'singular') {
$_this->_singularized = array();
}
}
}
$_this->{$var}['rules'] = array_merge($rules, $_this->{$var}['rules']);
break;
}
}
/**
* Return $word in plural form.
*
* @param string $word Word in singular
* @return string Word in plural
* @access public
* @static
* @link http://book.cakephp.org/view/1479/Class-methods
*/
function pluralize($word) {
$_this =& Inflector::getInstance();
if (isset($_this->_pluralized[$word])) {
return $_this->_pluralized[$word];
}
if (!isset($_this->_plural['merged']['irregular'])) {
$_this->_plural['merged']['irregular'] = $_this->_plural['irregular'];
}
if (!isset($_this->plural['merged']['uninflected'])) {
$_this->_plural['merged']['uninflected'] = array_merge($_this->_plural['uninflected'], $_this->_uninflected);
}
if (!isset($_this->_plural['cacheUninflected']) || !isset($_this->_plural['cacheIrregular'])) {
$_this->_plural['cacheUninflected'] = '(?:' . implode('|', $_this->_plural['merged']['uninflected']) . ')';
$_this->_plural['cacheIrregular'] = '(?:' . implode('|', array_keys($_this->_plural['merged']['irregular'])) . ')';
}
if (preg_match('/(.*)\\b(' . $_this->_plural['cacheIrregular'] . ')$/i', $word, $regs)) {
$_this->_pluralized[$word] = $regs[1] . substr($word, 0, 1) . substr($_this->_plural['merged']['irregular'][strtolower($regs[2])], 1);
return $_this->_pluralized[$word];
}
if (preg_match('/^(' . $_this->_plural['cacheUninflected'] . ')$/i', $word, $regs)) {
$_this->_pluralized[$word] = $word;
return $word;
}
foreach ($_this->_plural['rules'] as $rule => $replacement) {
if (preg_match($rule, $word)) {
$_this->_pluralized[$word] = preg_replace($rule, $replacement, $word);
return $_this->_pluralized[$word];
}
}
}
/**
* Return $word in singular form.
*
* @param string $word Word in plural
* @return string Word in singular
* @access public
* @static
* @link http://book.cakephp.org/view/1479/Class-methods
*/
function singularize($word) {
$_this =& Inflector::getInstance();
if (isset($_this->_singularized[$word])) {
return $_this->_singularized[$word];
}
if (!isset($_this->_singular['merged']['uninflected'])) {
$_this->_singular['merged']['uninflected'] = array_merge($_this->_singular['uninflected'], $_this->_uninflected);
}
if (!isset($_this->_singular['merged']['irregular'])) {
$_this->_singular['merged']['irregular'] = array_merge($_this->_singular['irregular'], array_flip($_this->_plural['irregular']));
}
if (!isset($_this->_singular['cacheUninflected']) || !isset($_this->_singular['cacheIrregular'])) {
$_this->_singular['cacheUninflected'] = '(?:' . join( '|', $_this->_singular['merged']['uninflected']) . ')';
$_this->_singular['cacheIrregular'] = '(?:' . join( '|', array_keys($_this->_singular['merged']['irregular'])) . ')';
}
if (preg_match('/(.*)\\b(' . $_this->_singular['cacheIrregular'] . ')$/i', $word, $regs)) {
$_this->_singularized[$word] = $regs[1] . substr($word, 0, 1) . substr($_this->_singular['merged']['irregular'][strtolower($regs[2])], 1);
return $_this->_singularized[$word];
}
if (preg_match('/^(' . $_this->_singular['cacheUninflected'] . ')$/i', $word, $regs)) {
$_this->_singularized[$word] = $word;
return $word;
}
foreach ($_this->_singular['rules'] as $rule => $replacement) {
if (preg_match($rule, $word)) {
$_this->_singularized[$word] = preg_replace($rule, $replacement, $word);
return $_this->_singularized[$word];
}
}
$_this->_singularized[$word] = $word;
return $word;
}
/**
* Returns the given lower_case_and_underscored_word as a CamelCased word.
*
* @param string $lower_case_and_underscored_word Word to camelize
* @return string Camelized word. LikeThis.
* @access public
* @static
* @link http://book.cakephp.org/view/1479/Class-methods
*/
function camelize($lowerCaseAndUnderscoredWord) {
$_this =& Inflector::getInstance();
if (!($result = $_this->_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord))) {
$result = str_replace(' ', '', Inflector::humanize($lowerCaseAndUnderscoredWord));
$_this->_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord, $result);
}
return $result;
}
/**
* Returns the given camelCasedWord as an underscored_word.
*
* @param string $camelCasedWord Camel-cased word to be "underscorized"
* @return string Underscore-syntaxed version of the $camelCasedWord
* @access public
* @static
* @link http://book.cakephp.org/view/1479/Class-methods
*/
function underscore($camelCasedWord) {
$_this =& Inflector::getInstance();
if (!($result = $_this->_cache(__FUNCTION__, $camelCasedWord))) {
$result = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $camelCasedWord));
$_this->_cache(__FUNCTION__, $camelCasedWord, $result);
}
return $result;
}
/**
* Returns the given underscored_word_group as a Human Readable Word Group.
* (Underscores are replaced by spaces and capitalized following words.)
*
* @param string $lower_case_and_underscored_word String to be made more readable
* @return string Human-readable string
* @access public
* @static
* @link http://book.cakephp.org/view/1479/Class-methods
*/
function humanize($lowerCaseAndUnderscoredWord) {
$_this =& Inflector::getInstance();
if (!($result = $_this->_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord))) {
$result = ucwords(str_replace('_', ' ', $lowerCaseAndUnderscoredWord));
$_this->_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord, $result);
}
return $result;
}
/**
* Returns corresponding table name for given model $className. ("people" for the model class "Person").
*
* @param string $className Name of class to get database table name for
* @return string Name of the database table for given class
* @access public
* @static
* @link http://book.cakephp.org/view/1479/Class-methods
*/
function tableize($className) {
$_this =& Inflector::getInstance();
if (!($result = $_this->_cache(__FUNCTION__, $className))) {
$result = Inflector::pluralize(Inflector::underscore($className));
$_this->_cache(__FUNCTION__, $className, $result);
}
return $result;
}
/**
* Returns Cake model class name ("Person" for the database table "people".) for given database table.
*
* @param string $tableName Name of database table to get class name for
* @return string Class name
* @access public
* @static
* @link http://book.cakephp.org/view/1479/Class-methods
*/
function classify($tableName) {
$_this =& Inflector::getInstance();
if (!($result = $_this->_cache(__FUNCTION__, $tableName))) {
$result = Inflector::camelize(Inflector::singularize($tableName));
$_this->_cache(__FUNCTION__, $tableName, $result);
}
return $result;
}
/**
* Returns camelBacked version of an underscored string.
*
* @param string $string
* @return string in variable form
* @access public
* @static
* @link http://book.cakephp.org/view/1479/Class-methods
*/
function variable($string) {
$_this =& Inflector::getInstance();
if (!($result = $_this->_cache(__FUNCTION__, $string))) {
$string2 = Inflector::camelize(Inflector::underscore($string));
$replace = strtolower(substr($string2, 0, 1));
$result = preg_replace('/\\w/', $replace, $string2, 1);
$_this->_cache(__FUNCTION__, $string, $result);
}
return $result;
}
/**
* Returns a string with all spaces converted to underscores (by default), accented
* characters converted to non-accented characters, and non word characters removed.
*
* @param string $string the string you want to slug
* @param string $replacement will replace keys in map
* @param array $map extra elements to map to the replacement
* @deprecated $map param will be removed in future versions. Use Inflector::rules() instead
* @return string
* @access public
* @static
* @link http://book.cakephp.org/view/1479/Class-methods
*/
function slug($string, $replacement = '_', $map = array()) {
$_this =& Inflector::getInstance();
if (is_array($replacement)) {
$map = $replacement;
$replacement = '_';
}
$quotedReplacement = preg_quote($replacement, '/');
$merge = array(
'/[^\s\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{Nd}]/mu' => ' ',
'/\\s+/' => $replacement,
sprintf('/^[%s]+|[%s]+$/', $quotedReplacement, $quotedReplacement) => '',
);
$map = $map + $_this->_transliteration + $merge;
return preg_replace(array_keys($map), array_values($map), $string);
}
}

View File

@@ -0,0 +1,488 @@
<?php
/**
* Localization
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs
* @since CakePHP(tm) v 1.2.0.4116
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Localization
*
* @package cake
* @subpackage cake.cake.libs
*/
class L10n extends Object {
/**
* The language for current locale
*
* @var string
* @access public
*/
var $language = 'English (United States)';
/**
* Locale search paths
*
* @var array
* @access public
*/
var $languagePath = array('eng');
/**
* ISO 639-3 for current locale
*
* @var string
* @access public
*/
var $lang = 'eng';
/**
* Locale
*
* @var string
* @access public
*/
var $locale = 'en_us';
/**
* Default ISO 639-3 language.
*
* DEFAULT_LANGUAGE is defined in an application this will be set as a fall back
*
* @var string
* @access public
*/
var $default = null;
/**
* Encoding used for current locale
*
* @var string
* @access public
*/
var $charset = 'utf-8';
/**
* Text direction for current locale
*
* @var string
* @access public
*/
var $direction = 'ltr';
/**
* Set to true if a locale is found
*
* @var string
* @access public
*/
var $found = false;
/**
* Maps ISO 639-3 to I10n::__l10nCatalog
*
* @var array
* @access private
*/
var $__l10nMap = array(/* Afrikaans */ 'afr' => 'af',
/* Albanian */ 'alb' => 'sq',
/* Arabic */ 'ara' => 'ar',
/* Armenian - Armenia */ 'hye' => 'hy',
/* Basque */ 'baq' => 'eu',
/* Bosnian */ 'bos' => 'bs',
/* Bulgarian */ 'bul' => 'bg',
/* Byelorussian */ 'bel' => 'be',
/* Catalan */ 'cat' => 'ca',
/* Chinese */ 'chi' => 'zh',
/* Chinese */ 'zho' => 'zh',
/* Croatian */ 'hrv' => 'hr',
/* Czech */ 'cze' => 'cs',
/* Czech */ 'ces' => 'cs',
/* Danish */ 'dan' => 'da',
/* Dutch (Standard) */ 'dut' => 'nl',
/* Dutch (Standard) */ 'nld' => 'nl',
/* English */ 'eng' => 'en',
/* Estonian */ 'est' => 'et',
/* Faeroese */ 'fao' => 'fo',
/* Farsi */ 'fas' => 'fa',
/* Farsi */ 'per' => 'fa',
/* Finnish */ 'fin' => 'fi',
/* French (Standard) */ 'fre' => 'fr',
/* French (Standard) */ 'fra' => 'fr',
/* Gaelic (Scots) */ 'gla' => 'gd',
/* Galician */ 'glg' => 'gl',
/* German (Standard) */ 'deu' => 'de',
/* German (Standard) */ 'ger' => 'de',
/* Greek */ 'gre' => 'el',
/* Greek */ 'ell' => 'el',
/* Hebrew */ 'heb' => 'he',
/* Hindi */ 'hin' => 'hi',
/* Hungarian */ 'hun' => 'hu',
/* Icelandic */ 'ice' => 'is',
/* Icelandic */ 'isl' => 'is',
/* Indonesian */ 'ind' => 'id',
/* Irish */ 'gle' => 'ga',
/* Italian */ 'ita' => 'it',
/* Japanese */ 'jpn' => 'ja',
/* Korean */ 'kor' => 'ko',
/* Latvian */ 'lav' => 'lv',
/* Lithuanian */ 'lit' => 'lt',
/* Macedonian */ 'mac' => 'mk',
/* Macedonian */ 'mkd' => 'mk',
/* Malaysian */ 'may' => 'ms',
/* Malaysian */ 'msa' => 'ms',
/* Maltese */ 'mlt' => 'mt',
/* Norwegian */ 'nor' => 'no',
/* Norwegian Bokmal */ 'nob' => 'nb',
/* Norwegian Nynorsk */ 'nno' => 'nn',
/* Polish */ 'pol' => 'pl',
/* Portuguese (Portugal) */ 'por' => 'pt',
/* Rhaeto-Romanic */ 'roh' => 'rm',
/* Romanian */ 'rum' => 'ro',
/* Romanian */ 'ron' => 'ro',
/* Russian */ 'rus' => 'ru',
/* Sami (Lappish) */ 'smi' => 'sz',
/* Serbian */ 'scc' => 'sr',
/* Serbian */ 'srp' => 'sr',
/* Slovak */ 'slo' => 'sk',
/* Slovak */ 'slk' => 'sk',
/* Slovenian */ 'slv' => 'sl',
/* Sorbian */ 'wen' => 'sb',
/* Spanish (Spain - Traditional) */ 'spa' => 'es',
/* Swedish */ 'swe' => 'sv',
/* Thai */ 'tha' => 'th',
/* Tsonga */ 'tso' => 'ts',
/* Tswana */ 'tsn' => 'tn',
/* Turkish */ 'tur' => 'tr',
/* Ukrainian */ 'ukr' => 'uk',
/* Urdu */ 'urd' => 'ur',
/* Venda */ 'ven' => 've',
/* Vietnamese */ 'vie' => 'vi',
/* Welsh */ 'cym' => 'cy',
/* Xhosa */ 'xho' => 'xh',
/* Yiddish */ 'yid' => 'yi',
/* Zulu */ 'zul' => 'zu');
/**
* HTTP_ACCEPT_LANGUAGE catalog
*
* holds all information related to a language
*
* @var array
* @access private
*/
var $__l10nCatalog = array('af' => array('language' => 'Afrikaans', 'locale' => 'afr', 'localeFallback' => 'afr', 'charset' => 'utf-8', 'direction' => 'ltr'),
'ar' => array('language' => 'Arabic', 'locale' => 'ara', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
'ar-ae' => array('language' => 'Arabic (U.A.E.)', 'locale' => 'ar_ae', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
'ar-bh' => array('language' => 'Arabic (Bahrain)', 'locale' => 'ar_bh', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
'ar-dz' => array('language' => 'Arabic (Algeria)', 'locale' => 'ar_dz', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
'ar-eg' => array('language' => 'Arabic (Egypt)', 'locale' => 'ar_eg', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
'ar-iq' => array('language' => 'Arabic (Iraq)', 'locale' => 'ar_iq', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
'ar-jo' => array('language' => 'Arabic (Jordan)', 'locale' => 'ar_jo', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
'ar-kw' => array('language' => 'Arabic (Kuwait)', 'locale' => 'ar_kw', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
'ar-lb' => array('language' => 'Arabic (Lebanon)', 'locale' => 'ar_lb', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
'ar-ly' => array('language' => 'Arabic (Libya)', 'locale' => 'ar_ly', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
'ar-ma' => array('language' => 'Arabic (Morocco)', 'locale' => 'ar_ma', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
'ar-om' => array('language' => 'Arabic (Oman)', 'locale' => 'ar_om', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
'ar-qa' => array('language' => 'Arabic (Qatar)', 'locale' => 'ar_qa', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
'ar-sa' => array('language' => 'Arabic (Saudi Arabia)', 'locale' => 'ar_sa', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
'ar-sy' => array('language' => 'Arabic (Syria)', 'locale' => 'ar_sy', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
'ar-tn' => array('language' => 'Arabic (Tunisia)', 'locale' => 'ar_tn', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
'ar-ye' => array('language' => 'Arabic (Yemen)', 'locale' => 'ar_ye', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
'be' => array('language' => 'Byelorussian', 'locale' => 'bel', 'localeFallback' => 'bel', 'charset' => 'utf-8', 'direction' => 'ltr'),
'bg' => array('language' => 'Bulgarian', 'locale' => 'bul', 'localeFallback' => 'bul', 'charset' => 'utf-8', 'direction' => 'ltr'),
'bs' => array('language' => 'Bosnian', 'locale' => 'bos', 'localeFallback' => 'bos', 'charset' => 'utf-8', 'direction' => 'ltr'),
'ca' => array('language' => 'Catalan', 'locale' => 'cat', 'localeFallback' => 'cat', 'charset' => 'utf-8', 'direction' => 'ltr'),
'cs' => array('language' => 'Czech', 'locale' => 'cze', 'localeFallback' => 'cze', 'charset' => 'utf-8', 'direction' => 'ltr'),
'da' => array('language' => 'Danish', 'locale' => 'dan', 'localeFallback' => 'dan', 'charset' => 'utf-8', 'direction' => 'ltr'),
'de' => array('language' => 'German (Standard)', 'locale' => 'deu', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'),
'de-at' => array('language' => 'German (Austria)', 'locale' => 'de_at', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'),
'de-ch' => array('language' => 'German (Swiss)', 'locale' => 'de_ch', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'),
'de-de' => array('language' => 'German (Germany)', 'locale' => 'de_de', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'),
'de-li' => array('language' => 'German (Liechtenstein)', 'locale' => 'de_li', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'),
'de-lu' => array('language' => 'German (Luxembourg)', 'locale' => 'de_lu', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'),
'e' => array('language' => 'Greek', 'locale' => 'gre', 'localeFallback' => 'gre', 'charset' => 'utf-8', 'direction' => 'ltr'),
'el' => array('language' => 'Greek', 'locale' => 'gre', 'localeFallback' => 'gre', 'charset' => 'utf-8', 'direction' => 'ltr'),
'en' => array('language' => 'English', 'locale' => 'eng', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
'en-au' => array('language' => 'English (Australian)', 'locale' => 'en_au', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
'en-bz' => array('language' => 'English (Belize)', 'locale' => 'en_bz', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
'en-ca' => array('language' => 'English (Canadian)', 'locale' => 'en_ca', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
'en-gb' => array('language' => 'English (British)', 'locale' => 'en_gb', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
'en-ie' => array('language' => 'English (Ireland)', 'locale' => 'en_ie', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
'en-jm' => array('language' => 'English (Jamaica)', 'locale' => 'en_jm', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
'en-nz' => array('language' => 'English (New Zealand)', 'locale' => 'en_nz', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
'en-tt' => array('language' => 'English (Trinidad)', 'locale' => 'en_tt', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
'en-us' => array('language' => 'English (United States)', 'locale' => 'en_us', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
'en-za' => array('language' => 'English (South Africa)', 'locale' => 'en_za', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
'es' => array('language' => 'Spanish (Spain - Traditional)', 'locale' => 'spa', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
'es-ar' => array('language' => 'Spanish (Argentina)', 'locale' => 'es_ar', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
'es-bo' => array('language' => 'Spanish (Bolivia)', 'locale' => 'es_bo', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
'es-cl' => array('language' => 'Spanish (Chile)', 'locale' => 'es_cl', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
'es-co' => array('language' => 'Spanish (Colombia)', 'locale' => 'es_co', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
'es-cr' => array('language' => 'Spanish (Costa Rica)', 'locale' => 'es_cr', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
'es-do' => array('language' => 'Spanish (Dominican Republic)', 'locale' => 'es_do', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
'es-ec' => array('language' => 'Spanish (Ecuador)', 'locale' => 'es_ec', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
'es-es' => array('language' => 'Spanish (Spain)', 'locale' => 'es_es', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
'es-gt' => array('language' => 'Spanish (Guatemala)', 'locale' => 'es_gt', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
'es-hn' => array('language' => 'Spanish (Honduras)', 'locale' => 'es_hn', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
'es-mx' => array('language' => 'Spanish (Mexican)', 'locale' => 'es_mx', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
'es-ni' => array('language' => 'Spanish (Nicaragua)', 'locale' => 'es_ni', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
'es-pa' => array('language' => 'Spanish (Panama)', 'locale' => 'es_pa', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
'es-pe' => array('language' => 'Spanish (Peru)', 'locale' => 'es_pe', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
'es-pr' => array('language' => 'Spanish (Puerto Rico)', 'locale' => 'es_pr', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
'es-py' => array('language' => 'Spanish (Paraguay)', 'locale' => 'es_py', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
'es-sv' => array('language' => 'Spanish (El Salvador)', 'locale' => 'es_sv', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
'es-uy' => array('language' => 'Spanish (Uruguay)', 'locale' => 'es_uy', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
'es-ve' => array('language' => 'Spanish (Venezuela)', 'locale' => 'es_ve', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
'et' => array('language' => 'Estonian', 'locale' => 'est', 'localeFallback' => 'est', 'charset' => 'utf-8', 'direction' => 'ltr'),
'eu' => array('language' => 'Basque', 'locale' => 'baq', 'localeFallback' => 'baq', 'charset' => 'utf-8', 'direction' => 'ltr'),
'fa' => array('language' => 'Farsi', 'locale' => 'per', 'localeFallback' => 'per', 'charset' => 'utf-8', 'direction' => 'rtl'),
'fi' => array('language' => 'Finnish', 'locale' => 'fin', 'localeFallback' => 'fin', 'charset' => 'utf-8', 'direction' => 'ltr'),
'fo' => array('language' => 'Faeroese', 'locale' => 'fao', 'localeFallback' => 'fao', 'charset' => 'utf-8', 'direction' => 'ltr'),
'fr' => array('language' => 'French (Standard)', 'locale' => 'fre', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'),
'fr-be' => array('language' => 'French (Belgium)', 'locale' => 'fr_be', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'),
'fr-ca' => array('language' => 'French (Canadian)', 'locale' => 'fr_ca', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'),
'fr-ch' => array('language' => 'French (Swiss)', 'locale' => 'fr_ch', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'),
'fr-fr' => array('language' => 'French (France)', 'locale' => 'fr_fr', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'),
'fr-lu' => array('language' => 'French (Luxembourg)', 'locale' => 'fr_lu', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'),
'ga' => array('language' => 'Irish', 'locale' => 'gle', 'localeFallback' => 'gle', 'charset' => 'utf-8', 'direction' => 'ltr'),
'gd' => array('language' => 'Gaelic (Scots)', 'locale' => 'gla', 'localeFallback' => 'gla', 'charset' => 'utf-8', 'direction' => 'ltr'),
'gd-ie' => array('language' => 'Gaelic (Irish)', 'locale' => 'gd_ie', 'localeFallback' => 'gla', 'charset' => 'utf-8', 'direction' => 'ltr'),
'gl' => array('language' => 'Galician', 'locale' => 'glg', 'localeFallback' => 'glg', 'charset' => 'utf-8', 'direction' => 'ltr'),
'he' => array('language' => 'Hebrew', 'locale' => 'heb', 'localeFallback' => 'heb', 'charset' => 'utf-8', 'direction' => 'rtl'),
'hi' => array('language' => 'Hindi', 'locale' => 'hin', 'localeFallback' => 'hin', 'charset' => 'utf-8', 'direction' => 'ltr'),
'hr' => array('language' => 'Croatian', 'locale' => 'hrv', 'localeFallback' => 'hrv', 'charset' => 'utf-8', 'direction' => 'ltr'),
'hu' => array('language' => 'Hungarian', 'locale' => 'hun', 'localeFallback' => 'hun', 'charset' => 'utf-8', 'direction' => 'ltr'),
'hy' => array('language' => 'Armenian - Armenia', 'locale' => 'hye', 'localeFallback' => 'hye', 'charset' => 'utf-8', 'direction' => 'ltr'),
'id' => array('language' => 'Indonesian', 'locale' => 'ind', 'localeFallback' => 'ind', 'charset' => 'utf-8', 'direction' => 'ltr'),
'in' => array('language' => 'Indonesian', 'locale' => 'ind', 'localeFallback' => 'ind', 'charset' => 'utf-8', 'direction' => 'ltr'),
'is' => array('language' => 'Icelandic', 'locale' => 'ice', 'localeFallback' => 'ice', 'charset' => 'utf-8', 'direction' => 'ltr'),
'it' => array('language' => 'Italian', 'locale' => 'ita', 'localeFallback' => 'ita', 'charset' => 'utf-8', 'direction' => 'ltr'),
'it-ch' => array('language' => 'Italian (Swiss) ', 'locale' => 'it_ch', 'localeFallback' => 'ita', 'charset' => 'utf-8', 'direction' => 'ltr'),
'ja' => array('language' => 'Japanese', 'locale' => 'jpn', 'localeFallback' => 'jpn', 'charset' => 'utf-8', 'direction' => 'ltr'),
'ko' => array('language' => 'Korean', 'locale' => 'kor', 'localeFallback' => 'kor', 'charset' => 'kr', 'direction' => 'ltr'),
'ko-kp' => array('language' => 'Korea (North)', 'locale' => 'ko_kp', 'localeFallback' => 'kor', 'charset' => 'kr', 'direction' => 'ltr'),
'ko-kr' => array('language' => 'Korea (South)', 'locale' => 'ko_kr', 'localeFallback' => 'kor', 'charset' => 'kr', 'direction' => 'ltr'),
'koi8-r' => array('language' => 'Russian', 'locale' => 'koi8_r', 'localeFallback' => 'rus', 'charset' => 'koi8-r', 'direction' => 'ltr'),
'lt' => array('language' => 'Lithuanian', 'locale' => 'lit', 'localeFallback' => 'lit', 'charset' => 'utf-8', 'direction' => 'ltr'),
'lv' => array('language' => 'Latvian', 'locale' => 'lav', 'localeFallback' => 'lav', 'charset' => 'utf-8', 'direction' => 'ltr'),
'mk' => array('language' => 'FYRO Macedonian', 'locale' => 'mk', 'localeFallback' => 'mac', 'charset' => 'utf-8', 'direction' => 'ltr'),
'mk-mk' => array('language' => 'Macedonian', 'locale' => 'mk_mk', 'localeFallback' => 'mac', 'charset' => 'utf-8', 'direction' => 'ltr'),
'ms' => array('language' => 'Malaysian', 'locale' => 'may', 'localeFallback' => 'may', 'charset' => 'utf-8', 'direction' => 'ltr'),
'mt' => array('language' => 'Maltese', 'locale' => 'mlt', 'localeFallback' => 'mlt', 'charset' => 'utf-8', 'direction' => 'ltr'),
'n' => array('language' => 'Dutch (Standard)', 'locale' => 'dut', 'localeFallback' => 'dut', 'charset' => 'utf-8', 'direction' => 'ltr'),
'nb' => array('language' => 'Norwegian Bokmal', 'locale' => 'nob', 'localeFallback' => 'nor', 'charset' => 'utf-8', 'direction' => 'ltr'),
'nl' => array('language' => 'Dutch (Standard)', 'locale' => 'dut', 'localeFallback' => 'dut', 'charset' => 'utf-8', 'direction' => 'ltr'),
'nl-be' => array('language' => 'Dutch (Belgium)', 'locale' => 'nl_be', 'localeFallback' => 'dut', 'charset' => 'utf-8', 'direction' => 'ltr'),
'nn' => array('language' => 'Norwegian Nynorsk', 'locale' => 'nno', 'localeFallback' => 'nor', 'charset' => 'utf-8', 'direction' => 'ltr'),
'no' => array('language' => 'Norwegian', 'locale' => 'nor', 'localeFallback' => 'nor', 'charset' => 'utf-8', 'direction' => 'ltr'),
'p' => array('language' => 'Polish', 'locale' => 'pol', 'localeFallback' => 'pol', 'charset' => 'utf-8', 'direction' => 'ltr'),
'pl' => array('language' => 'Polish', 'locale' => 'pol', 'localeFallback' => 'pol', 'charset' => 'utf-8', 'direction' => 'ltr'),
'pt' => array('language' => 'Portuguese (Portugal)', 'locale' => 'por', 'localeFallback' => 'por', 'charset' => 'utf-8', 'direction' => 'ltr'),
'pt-br' => array('language' => 'Portuguese (Brazil)', 'locale' => 'pt_br', 'localeFallback' => 'por', 'charset' => 'utf-8', 'direction' => 'ltr'),
'rm' => array('language' => 'Rhaeto-Romanic', 'locale' => 'roh', 'localeFallback' => 'roh', 'charset' => 'utf-8', 'direction' => 'ltr'),
'ro' => array('language' => 'Romanian', 'locale' => 'rum', 'localeFallback' => 'rum', 'charset' => 'utf-8', 'direction' => 'ltr'),
'ro-mo' => array('language' => 'Romanian (Moldavia)', 'locale' => 'ro_mo', 'localeFallback' => 'rum', 'charset' => 'utf-8', 'direction' => 'ltr'),
'ru' => array('language' => 'Russian', 'locale' => 'rus', 'localeFallback' => 'rus', 'charset' => 'utf-8', 'direction' => 'ltr'),
'ru-mo' => array('language' => 'Russian (Moldavia)', 'locale' => 'ru_mo', 'localeFallback' => 'rus', 'charset' => 'utf-8', 'direction' => 'ltr'),
'sb' => array('language' => 'Sorbian', 'locale' => 'wen', 'localeFallback' => 'wen', 'charset' => 'utf-8', 'direction' => 'ltr'),
'sk' => array('language' => 'Slovak', 'locale' => 'slo', 'localeFallback' => 'slo', 'charset' => 'utf-8', 'direction' => 'ltr'),
'sl' => array('language' => 'Slovenian', 'locale' => 'slv', 'localeFallback' => 'slv', 'charset' => 'utf-8', 'direction' => 'ltr'),
'sq' => array('language' => 'Albanian', 'locale' => 'alb', 'localeFallback' => 'alb', 'charset' => 'utf-8', 'direction' => 'ltr'),
'sr' => array('language' => 'Serbian', 'locale' => 'scc', 'localeFallback' => 'scc', 'charset' => 'utf-8', 'direction' => 'ltr'),
'sv' => array('language' => 'Swedish', 'locale' => 'swe', 'localeFallback' => 'swe', 'charset' => 'utf-8', 'direction' => 'ltr'),
'sv-fi' => array('language' => 'Swedish (Finland)', 'locale' => 'sv_fi', 'localeFallback' => 'swe', 'charset' => 'utf-8', 'direction' => 'ltr'),
'sx' => array('language' => 'Sutu', 'locale' => 'sx', 'localeFallback' => 'sx', 'charset' => 'utf-8', 'direction' => 'ltr'),
'sz' => array('language' => 'Sami (Lappish)', 'locale' => 'smi', 'localeFallback' => 'smi', 'charset' => 'utf-8', 'direction' => 'ltr'),
'th' => array('language' => 'Thai', 'locale' => 'tha', 'localeFallback' => 'tha', 'charset' => 'utf-8', 'direction' => 'ltr'),
'tn' => array('language' => 'Tswana', 'locale' => 'tsn', 'localeFallback' => 'tsn', 'charset' => 'utf-8', 'direction' => 'ltr'),
'tr' => array('language' => 'Turkish', 'locale' => 'tur', 'localeFallback' => 'tur', 'charset' => 'utf-8', 'direction' => 'ltr'),
'ts' => array('language' => 'Tsonga', 'locale' => 'tso', 'localeFallback' => 'tso', 'charset' => 'utf-8', 'direction' => 'ltr'),
'uk' => array('language' => 'Ukrainian', 'locale' => 'ukr', 'localeFallback' => 'ukr', 'charset' => 'utf-8', 'direction' => 'ltr'),
'ur' => array('language' => 'Urdu', 'locale' => 'urd', 'localeFallback' => 'urd', 'charset' => 'utf-8', 'direction' => 'rtl'),
've' => array('language' => 'Venda', 'locale' => 'ven', 'localeFallback' => 'ven', 'charset' => 'utf-8', 'direction' => 'ltr'),
'vi' => array('language' => 'Vietnamese', 'locale' => 'vie', 'localeFallback' => 'vie', 'charset' => 'utf-8', 'direction' => 'ltr'),
'cy' => array('language' => 'Welsh', 'locale' => 'cym', 'localeFallback' => 'cym', 'charset' => 'utf-8', 'direction' => 'ltr'),
'xh' => array('language' => 'Xhosa', 'locale' => 'xho', 'localeFallback' => 'xho', 'charset' => 'utf-8', 'direction' => 'ltr'),
'yi' => array('language' => 'Yiddish', 'locale' => 'yid', 'localeFallback' => 'yid', 'charset' => 'utf-8', 'direction' => 'ltr'),
'zh' => array('language' => 'Chinese', 'locale' => 'chi', 'localeFallback' => 'chi', 'charset' => 'utf-8', 'direction' => 'ltr'),
'zh-cn' => array('language' => 'Chinese (PRC)', 'locale' => 'zh_cn', 'localeFallback' => 'chi', 'charset' => 'GB2312', 'direction' => 'ltr'),
'zh-hk' => array('language' => 'Chinese (Hong Kong)', 'locale' => 'zh_hk', 'localeFallback' => 'chi', 'charset' => 'utf-8', 'direction' => 'ltr'),
'zh-sg' => array('language' => 'Chinese (Singapore)', 'locale' => 'zh_sg', 'localeFallback' => 'chi', 'charset' => 'utf-8', 'direction' => 'ltr'),
'zh-tw' => array('language' => 'Chinese (Taiwan)', 'locale' => 'zh_tw', 'localeFallback' => 'chi', 'charset' => 'utf-8', 'direction' => 'ltr'),
'zu' => array('language' => 'Zulu', 'locale' => 'zul', 'localeFallback' => 'zul', 'charset' => 'utf-8', 'direction' => 'ltr'));
/**
* Class constructor
*/
function __construct() {
if (defined('DEFAULT_LANGUAGE')) {
$this->default = DEFAULT_LANGUAGE;
}
parent::__construct();
}
/**
* Gets the settings for $language.
* If $language is null it attempt to get settings from L10n::__autoLanguage(); if this fails
* the method will get the settings from L10n::__setLanguage();
*
* @param string $language Language (if null will use DEFAULT_LANGUAGE if defined)
* @access public
*/
function get($language = null) {
if ($language !== null) {
return $this->__setLanguage($language);
} elseif ($this->__autoLanguage() === false) {
return $this->__setLanguage();
}
}
/**
* Sets the class vars to correct values for $language.
* If $language is null it will use the DEFAULT_LANGUAGE if defined
*
* @param string $language Language (if null will use DEFAULT_LANGUAGE if defined)
* @access private
*/
function __setLanguage($language = null) {
$langKey = null;
if ($language !== null && isset($this->__l10nMap[$language]) && isset($this->__l10nCatalog[$this->__l10nMap[$language]])) {
$langKey = $this->__l10nMap[$language];
} else if ($language !== null && isset($this->__l10nCatalog[$language])) {
$langKey = $language;
} else if (defined('DEFAULT_LANGUAGE')) {
$langKey = $language = DEFAULT_LANGUAGE;
}
if ($langKey !== null && isset($this->__l10nCatalog[$langKey])) {
$this->language = $this->__l10nCatalog[$langKey]['language'];
$this->languagePath = array(
$this->__l10nCatalog[$langKey]['locale'],
$this->__l10nCatalog[$langKey]['localeFallback']
);
$this->lang = $language;
$this->locale = $this->__l10nCatalog[$langKey]['locale'];
$this->charset = $this->__l10nCatalog[$langKey]['charset'];
$this->direction = $this->__l10nCatalog[$langKey]['direction'];
} else {
$this->lang = $language;
$this->languagePath = array($language);
}
if ($this->default) {
if (isset($this->__l10nMap[$this->default]) && isset($this->__l10nCatalog[$this->__l10nMap[$this->default]])) {
$this->languagePath[] = $this->__l10nCatalog[$this->__l10nMap[$this->default]]['localeFallback'];
} else if (isset($this->__l10nCatalog[$this->default])) {
$this->languagePath[] = $this->__l10nCatalog[$this->default]['localeFallback'];
}
}
$this->found = true;
if (Configure::read('Config.language') === null) {
Configure::write('Config.language', $this->lang);
}
if ($language) {
return $language;
}
}
/**
* Attempts to find the locale settings based on the HTTP_ACCEPT_LANGUAGE variable
*
* @return boolean Success
* @access private
*/
function __autoLanguage() {
$_detectableLanguages = preg_split('/[,;]/', env('HTTP_ACCEPT_LANGUAGE'));
foreach ($_detectableLanguages as $key => $langKey) {
$langKey = strtolower($langKey);
if (strpos($langKey, '_') !== false) {
$langKey = str_replace('_', '-', $langKey);
}
if (isset($this->__l10nCatalog[$langKey])) {
$this->__setLanguage($langKey);
return true;
} else if (strpos($langKey, '-') !== false) {
$langKey = substr($langKey, 0, 2);
if (isset($this->__l10nCatalog[$langKey])) {
$this->__setLanguage($langKey);
return true;
}
}
}
return false;
}
/**
* Attempts to find locale for language, or language for locale
*
* @param mixed $mixed 2/3 char string (language/locale), array of those strings, or null
* @return mixed string language/locale, array of those values, whole map as an array,
* or false when language/locale doesn't exist
* @access public
*/
function map($mixed = null) {
if (is_array($mixed)) {
$result = array();
foreach ($mixed as $_mixed) {
if ($_result = $this->map($_mixed)) {
$result[$_mixed] = $_result;
}
}
return $result;
} else if (is_string($mixed)) {
if (strlen($mixed) === 2 && in_array($mixed, $this->__l10nMap)) {
return array_search($mixed, $this->__l10nMap);
} else if (isset($this->__l10nMap[$mixed])) {
return $this->__l10nMap[$mixed];
}
return false;
}
return $this->__l10nMap;
}
/**
* Attempts to find catalog record for requested language
*
* @param mixed $language string requested language, array of requested languages, or null for whole catalog
* @return mixed array catalog record for requested language, array of catalog records, whole catalog,
* or false when language doesn't exist
* @access public
*/
function catalog($language = null) {
if (is_array($language)) {
$result = array();
foreach ($language as $_language) {
if ($_result = $this->catalog($_language)) {
$result[$_language] = $_result;
}
}
return $result;
} else if (is_string($language)) {
if (isset($this->__l10nCatalog[$language])) {
return $this->__l10nCatalog[$language];
} else if (isset($this->__l10nMap[$language]) && isset($this->__l10nCatalog[$this->__l10nMap[$language]])) {
return $this->__l10nCatalog[$this->__l10nMap[$language]];
}
return false;
}
return $this->__l10nCatalog;
}
}

View File

@@ -0,0 +1,77 @@
<?php
/**
* File Storage stream for Logging
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.log
* @since CakePHP(tm) v 1.3
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
if (!class_exists('File')) {
require LIBS . 'file.php';
}
/**
* File Storage stream for Logging. Writes logs to different files
* based on the type of log it is.
*
* @package cake
* @subpackage cake.cake.libs.log
*/
class FileLog {
/**
* Path to save log files on.
*
* @var string
*/
var $_path = null;
/**
* Constructs a new File Logger.
*
* Options
*
* - `path` the path to save logs on.
*
* @param array $options Options for the FileLog, see above.
* @return void
*/
function FileLog($options = array()) {
$options += array('path' => LOGS);
$this->_path = $options['path'];
}
/**
* Implements writing to log files.
*
* @param string $type The type of log you are making.
* @param string $message The message you want to log.
* @return boolean success of write.
*/
function write($type, $message) {
$debugTypes = array('notice', 'info', 'debug');
if ($type == 'error' || $type == 'warning') {
$filename = $this->_path . 'error.log';
} elseif (in_array($type, $debugTypes)) {
$filename = $this->_path . 'debug.log';
} else {
$filename = $this->_path . $type . '.log';
}
$output = date('Y-m-d H:i:s') . ' ' . ucfirst($type) . ': ' . $message . "\n";
$log = new File($filename, true);
if ($log->writable()) {
return $log->append($output);
}
}
}

View File

@@ -0,0 +1,302 @@
<?php
/**
* MagicDb parser and file analyzer
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs
* @since CakePHP(tm) v 1.2.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
if (!class_exists('Object')) {
require LIBS . 'object.php';
}
if (!class_exists('File')) {
require LIBS . 'file.php';
}
/**
* A class to parse and use the MagicDb for file type analysis
*
* @package cake.tests
* @subpackage cake.tests.cases.libs
*/
class MagicDb extends Object {
/**
* Holds the parsed MagicDb for this class instance
*
* @var array
*/
var $db = array();
/**
* Reads a MagicDb from various formats
*
* @var $magicDb mixed Can be an array containing the db, a magic db as a string, or a filename pointing to a magic db in .db or magic.db.php format
* @return boolean Returns false if reading / validation failed or true on success.
* @author Felix
*/
function read($magicDb = null) {
if (!is_string($magicDb) && !is_array($magicDb)) {
return false;
}
if (is_array($magicDb) || strpos($magicDb, '# FILE_ID DB') === 0) {
$data = $magicDb;
} else {
$File =& new File($magicDb);
if (!$File->exists()) {
return false;
}
if ($File->ext() == 'php') {
include($File->pwd());
$data = $magicDb;
} else {
// @TODO: Needs test coverage
$data = $File->read();
}
}
$magicDb = $this->toArray($data);
if (!$this->validates($magicDb)) {
return false;
}
return !!($this->db = $magicDb);
}
/**
* Parses a MagicDb $data string into an array or returns the current MagicDb instance as an array
*
* @param string $data A MagicDb string to turn into an array
* @return array A parsed MagicDb array or an empty array if the $data param was invalid. Returns the db property if $data is not set.
* @access public
*/
function toArray($data = null) {
if (is_array($data)) {
return $data;
}
if ($data === null) {
return $this->db;
}
if (strpos($data, '# FILE_ID DB') !== 0) {
return array();
}
$lines = explode("\r\n", $data);
$db = array();
$validHeader = count($lines) > 3
&& preg_match('/^# Date:([0-9]{4}-[0-9]{2}-[0-9]{2})$/', $lines[1], $date)
&& preg_match('/^# Source:(.+)$/', $lines[2], $source)
&& strlen($lines[3]) == 0;
if (!$validHeader) {
return $db;
}
$db = array('header' => array('Date' => $date[1], 'Source' => $source[1]), 'database' => array());
$lines = array_splice($lines, 3);
$format = array();
while (!empty($lines)) {
$line = array_shift($lines);
if (isset($line[0]) && $line[0] == '#' || empty($line)) {
continue;
}
$columns = explode("\t", $line);
if (in_array($columns[0]{0}, array('>', '&'))) {
$format[] = $columns;
} elseif (!empty($format)) {
$db['database'][] = $format;
$format = array($columns);
} else {
$format = array($columns);
}
}
return $db;
}
/**
* Returns true if the MagicDb instance or the passed $magicDb is valid
*
* @param mixed $magicDb A $magicDb string / array to validate (optional)
* @return boolean True if the $magicDb / instance db validates, false if not
* @access public
*/
function validates($magicDb = null) {
if (is_null($magicDb)) {
$magicDb = $this->db;
} elseif (!is_array($magicDb)) {
$magicDb = $this->toArray($magicDb);
}
return isset($magicDb['header'], $magicDb['database']) && is_array($magicDb['header']) && is_array($magicDb['database']);
}
/**
* Analyzes a given $file using the currently loaded MagicDb information based on the desired $options
*
* @param string $file Absolute path to the file to analyze
* @param array $options TBT
* @return mixed
* @access public
*/
function analyze($file, $options = array()) {
if (!is_string($file)) {
return false;
}
$matches = array();
$MagicFileResource =& new MagicFileResource($file);
foreach ($this->db['database'] as $format) {
$magic = $format[0];
$match = $MagicFileResource->test($magic);
if ($match === false) {
continue;
}
$matches[] = $magic;
}
return $matches;
}
}
/**
* undocumented class
*
* @package cake.tests
* @subpackage cake.tests.cases.libs
*/
class MagicFileResource extends Object{
/**
* undocumented variable
*
* @var unknown
* @access public
*/
var $resource = null;
/**
* undocumented variable
*
* @var unknown
* @access public
*/
var $offset = 0;
/**
* undocumented function
*
* @param unknown $file
* @return void
* @access public
*/
function __construct($file) {
if (file_exists($file)) {
$this->resource =& new File($file);
} else {
$this->resource = $file;
}
}
/**
* undocumented function
*
* @param unknown $magic
* @return void
* @access public
*/
function test($magic) {
$offset = null;
$type = null;
$expected = null;
$comment = null;
if (isset($magic[0])) {
$offset = $magic[0];
}
if (isset($magic[1])) {
$type = $magic[1];
}
if (isset($magic[2])) {
$expected = $magic[2];
}
if (isset($magic[3])) {
$comment = $magic[3];
}
$val = $this->extract($offset, $type, $expected);
return $val == $expected;
}
/**
* undocumented function
*
* @param unknown $type
* @param unknown $length
* @return void
* @access public
*/
function read($length = null) {
if (!is_object($this->resource)) {
return substr($this->resource, $this->offset, $length);
}
return $this->resource->read($length);
}
/**
* undocumented function
*
* @param unknown $type
* @param unknown $expected
* @return void
* @access public
*/
function extract($offset, $type, $expected) {
switch ($type) {
case 'string':
$this->offset($offset);
$val = $this->read(strlen($expected));
if ($val === $expected) {
return true;
}
break;
}
}
/**
* undocumented function
*
* @param unknown $offset
* @param unknown $whence
* @return void
* @access public
*/
function offset($offset = null) {
if (is_null($offset)) {
if (!is_object($this->resource)) {
return $this->offset;
}
return $this->offset;
}
if (!ctype_digit($offset)) {
return false;
}
if (is_object($this->resource)) {
$this->resource->offset($offset);
} else {
$this->offset = $offset;
}
}
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* Application model for Cake.
*
* This file is application-wide model file. You can put all
* application-wide model-related methods here.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.model
* @since CakePHP(tm) v 0.2.9
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Application model for Cake.
*
* This is a placeholder class.
* Create the same file in app/app_model.php
* Add your application-wide methods to the class, your models will inherit them.
*
* @package cake
* @subpackage cake.cake.libs.model
*/
class AppModel extends Model {
}

View File

@@ -0,0 +1,123 @@
<?php
/**
* ACL behavior class.
*
* Enables objects to easily tie into an ACL system
*
* PHP versions 4 and 5
*
* CakePHP : Rapid Development Framework (http://cakephp.org)
* Copyright 2006-2010, Cake Software Foundation, Inc.
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2006-2010, Cake Software Foundation, Inc.
* @link http://cakephp.org CakePHP Project
* @package cake
* @subpackage cake.cake.libs.model.behaviors
* @since CakePHP v 1.2.0.4487
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* ACL behavior
*
* @package cake
* @subpackage cake.cake.libs.model.behaviors
* @link http://book.cakephp.org/view/1320/ACL
*/
class AclBehavior extends ModelBehavior {
/**
* Maps ACL type options to ACL models
*
* @var array
* @access protected
*/
var $__typeMaps = array('requester' => 'Aro', 'controlled' => 'Aco');
/**
* Sets up the configuation for the model, and loads ACL models if they haven't been already
*
* @param mixed $config
* @return void
* @access public
*/
function setup(&$model, $config = array()) {
if (is_string($config)) {
$config = array('type' => $config);
}
$this->settings[$model->name] = array_merge(array('type' => 'requester'), (array)$config);
$this->settings[$model->name]['type'] = strtolower($this->settings[$model->name]['type']);
$type = $this->__typeMaps[$this->settings[$model->name]['type']];
if (!class_exists('AclNode')) {
require LIBS . 'model' . DS . 'db_acl.php';
}
if (PHP5) {
$model->{$type} = ClassRegistry::init($type);
} else {
$model->{$type} =& ClassRegistry::init($type);
}
if (!method_exists($model, 'parentNode')) {
trigger_error(sprintf(__('Callback parentNode() not defined in %s', true), $model->alias), E_USER_WARNING);
}
}
/**
* Retrieves the Aro/Aco node for this model
*
* @param mixed $ref
* @return array
* @access public
* @link http://book.cakephp.org/view/1322/node
*/
function node(&$model, $ref = null) {
$type = $this->__typeMaps[$this->settings[$model->name]['type']];
if (empty($ref)) {
$ref = array('model' => $model->name, 'foreign_key' => $model->id);
}
return $model->{$type}->node($ref);
}
/**
* Creates a new ARO/ACO node bound to this record
*
* @param boolean $created True if this is a new record
* @return void
* @access public
*/
function afterSave(&$model, $created) {
$type = $this->__typeMaps[$this->settings[$model->name]['type']];
$parent = $model->parentNode();
if (!empty($parent)) {
$parent = $this->node($model, $parent);
}
$data = array(
'parent_id' => isset($parent[0][$type]['id']) ? $parent[0][$type]['id'] : null,
'model' => $model->alias,
'foreign_key' => $model->id
);
if (!$created) {
$node = $this->node($model);
$data['id'] = isset($node[0][$type]['id']) ? $node[0][$type]['id'] : null;
}
$model->{$type}->create();
$model->{$type}->save($data);
}
/**
* Destroys the ARO/ACO node bound to the deleted record
*
* @return void
* @access public
*/
function afterDelete(&$model) {
$type = $this->__typeMaps[$this->settings[$model->name]['type']];
$node = Set::extract($this->node($model), "0.{$type}.id");
if (!empty($node)) {
$model->{$type}->delete($node);
}
}
}

View File

@@ -0,0 +1,453 @@
<?php
/**
* Behavior for binding management.
*
* Behavior to simplify manipulating a model's bindings when doing a find operation
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.console.libs
* @since CakePHP(tm) v 1.2.0.5669
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Behavior to allow for dynamic and atomic manipulation of a Model's associations used for a find call. Most useful for limiting
* the amount of associations and data returned.
*
* @package cake
* @subpackage cake.cake.console.libs
* @link http://book.cakephp.org/view/1323/Containable
*/
class ContainableBehavior extends ModelBehavior {
/**
* Types of relationships available for models
*
* @var array
* @access private
*/
var $types = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
/**
* Runtime configuration for this behavior
*
* @var array
* @access private
*/
var $runtime = array();
/**
* Initiate behavior for the model using specified settings.
*
* Available settings:
*
* - recursive: (boolean, optional) set to true to allow containable to automatically
* determine the recursiveness level needed to fetch specified models,
* and set the model recursiveness to this level. setting it to false
* disables this feature. DEFAULTS TO: true
* - notices: (boolean, optional) issues E_NOTICES for bindings referenced in a
* containable call that are not valid. DEFAULTS TO: true
* - autoFields: (boolean, optional) auto-add needed fields to fetch requested
* bindings. DEFAULTS TO: true
*
* @param object $Model Model using the behavior
* @param array $settings Settings to override for model.
* @access public
*/
function setup(&$Model, $settings = array()) {
if (!isset($this->settings[$Model->alias])) {
$this->settings[$Model->alias] = array('recursive' => true, 'notices' => true, 'autoFields' => true);
}
if (!is_array($settings)) {
$settings = array();
}
$this->settings[$Model->alias] = array_merge($this->settings[$Model->alias], $settings);
}
/**
* Runs before a find() operation. Used to allow 'contain' setting
* as part of the find call, like this:
*
* `Model->find('all', array('contain' => array('Model1', 'Model2')));`
*
* {{{
* Model->find('all', array('contain' => array(
* 'Model1' => array('Model11', 'Model12'),
* 'Model2',
* 'Model3' => array(
* 'Model31' => 'Model311',
* 'Model32',
* 'Model33' => array('Model331', 'Model332')
* )));
* }}}
*
* @param object $Model Model using the behavior
* @param array $query Query parameters as set by cake
* @return array
* @access public
*/
function beforeFind(&$Model, $query) {
$reset = (isset($query['reset']) ? $query['reset'] : true);
$noContain = ((isset($this->runtime[$Model->alias]['contain']) && empty($this->runtime[$Model->alias]['contain'])) || (isset($query['contain']) && empty($query['contain'])));
$contain = array();
if (isset($this->runtime[$Model->alias]['contain'])) {
$contain = $this->runtime[$Model->alias]['contain'];
unset($this->runtime[$Model->alias]['contain']);
}
if (isset($query['contain'])) {
$contain = array_merge($contain, (array)$query['contain']);
}
if ($noContain || !$contain || in_array($contain, array(null, false), true) || (isset($contain[0]) && $contain[0] === null)) {
if ($noContain) {
$query['recursive'] = -1;
}
return $query;
}
if ((isset($contain[0]) && is_bool($contain[0])) || is_bool(end($contain))) {
$reset = is_bool(end($contain))
? array_pop($contain)
: array_shift($contain);
}
$containments = $this->containments($Model, $contain);
$map = $this->containmentsMap($containments);
$mandatory = array();
foreach ($containments['models'] as $name => $model) {
$instance =& $model['instance'];
$needed = $this->fieldDependencies($instance, $map, false);
if (!empty($needed)) {
$mandatory = array_merge($mandatory, $needed);
}
if ($contain) {
$backupBindings = array();
foreach ($this->types as $relation) {
if (!empty($instance->__backAssociation[$relation])) {
$backupBindings[$relation] = $instance->__backAssociation[$relation];
} else {
$backupBindings[$relation] = $instance->{$relation};
}
}
foreach ($this->types as $type) {
$unbind = array();
foreach ($instance->{$type} as $assoc => $options) {
if (!isset($model['keep'][$assoc])) {
$unbind[] = $assoc;
}
}
if (!empty($unbind)) {
if (!$reset && empty($instance->__backOriginalAssociation)) {
$instance->__backOriginalAssociation = $backupBindings;
} else if ($reset && empty($instance->__backContainableAssociation)) {
$instance->__backContainableAssociation = $backupBindings;
}
$instance->unbindModel(array($type => $unbind), $reset);
}
foreach ($instance->{$type} as $assoc => $options) {
if (isset($model['keep'][$assoc]) && !empty($model['keep'][$assoc])) {
if (isset($model['keep'][$assoc]['fields'])) {
$model['keep'][$assoc]['fields'] = $this->fieldDependencies($containments['models'][$assoc]['instance'], $map, $model['keep'][$assoc]['fields']);
}
if (!$reset && empty($instance->__backOriginalAssociation)) {
$instance->__backOriginalAssociation = $backupBindings;
} else if ($reset) {
$instance->__backAssociation[$type] = $backupBindings[$type];
}
$instance->{$type}[$assoc] = array_merge($instance->{$type}[$assoc], $model['keep'][$assoc]);
}
if (!$reset) {
$instance->__backInnerAssociation[] = $assoc;
}
}
}
}
}
if ($this->settings[$Model->alias]['recursive']) {
$query['recursive'] = (isset($query['recursive'])) ? $query['recursive'] : $containments['depth'];
}
$autoFields = ($this->settings[$Model->alias]['autoFields']
&& !in_array($Model->findQueryType, array('list', 'count'))
&& !empty($query['fields']));
if (!$autoFields) {
return $query;
}
$query['fields'] = (array)$query['fields'];
foreach (array('hasOne', 'belongsTo') as $type) {
if (!empty($Model->{$type})) {
foreach ($Model->{$type} as $assoc => $data) {
if ($Model->useDbConfig == $Model->{$assoc}->useDbConfig && !empty($data['fields'])) {
foreach ((array) $data['fields'] as $field) {
$query['fields'][] = (strpos($field, '.') === false ? $assoc . '.' : '') . $field;
}
}
}
}
}
if (!empty($mandatory[$Model->alias])) {
foreach ($mandatory[$Model->alias] as $field) {
if ($field == '--primaryKey--') {
$field = $Model->primaryKey;
} else if (preg_match('/^.+\.\-\-[^-]+\-\-$/', $field)) {
list($modelName, $field) = explode('.', $field);
if ($Model->useDbConfig == $Model->{$modelName}->useDbConfig) {
$field = $modelName . '.' . (
($field === '--primaryKey--') ? $Model->$modelName->primaryKey : $field
);
} else {
$field = null;
}
}
if ($field !== null) {
$query['fields'][] = $field;
}
}
}
$query['fields'] = array_unique($query['fields']);
return $query;
}
/**
* Resets original associations on models that may have receive multiple,
* subsequent unbindings.
*
* @param object $Model Model on which we are resetting
* @param array $results Results of the find operation
* @param bool $primary true if this is the primary model that issued the find operation, false otherwise
* @access public
*/
function afterFind(&$Model, $results, $primary) {
if (!empty($Model->__backContainableAssociation)) {
foreach ($Model->__backContainableAssociation as $relation => $bindings) {
$Model->{$relation} = $bindings;
unset($Model->__backContainableAssociation);
}
}
}
/**
* Unbinds all relations from a model except the specified ones. Calling this function without
* parameters unbinds all related models.
*
* @param object $Model Model on which binding restriction is being applied
* @return void
* @access public
* @link http://book.cakephp.org/view/1323/Containable#Using-Containable-1324
*/
function contain(&$Model) {
$args = func_get_args();
$contain = call_user_func_array('am', array_slice($args, 1));
$this->runtime[$Model->alias]['contain'] = $contain;
}
/**
* Permanently restore the original binding settings of given model, useful
* for restoring the bindings after using 'reset' => false as part of the
* contain call.
*
* @param object $Model Model on which to reset bindings
* @return void
* @access public
*/
function resetBindings(&$Model) {
if (!empty($Model->__backOriginalAssociation)) {
$Model->__backAssociation = $Model->__backOriginalAssociation;
unset($Model->__backOriginalAssociation);
}
$Model->resetAssociations();
if (!empty($Model->__backInnerAssociation)) {
$assocs = $Model->__backInnerAssociation;
unset($Model->__backInnerAssociation);
foreach ($assocs as $currentModel) {
$this->resetBindings($Model->$currentModel);
}
}
}
/**
* Process containments for model.
*
* @param object $Model Model on which binding restriction is being applied
* @param array $contain Parameters to use for restricting this model
* @param array $containments Current set of containments
* @param bool $throwErrors Wether unexisting bindings show throw errors
* @return array Containments
* @access public
*/
function containments(&$Model, $contain, $containments = array(), $throwErrors = null) {
$options = array('className', 'joinTable', 'with', 'foreignKey', 'associationForeignKey', 'conditions', 'fields', 'order', 'limit', 'offset', 'unique', 'finderQuery', 'deleteQuery', 'insertQuery');
$keep = array();
$depth = array();
if ($throwErrors === null) {
$throwErrors = (empty($this->settings[$Model->alias]) ? true : $this->settings[$Model->alias]['notices']);
}
foreach ((array)$contain as $name => $children) {
if (is_numeric($name)) {
$name = $children;
$children = array();
}
if (preg_match('/(?<!\.)\(/', $name)) {
$name = str_replace('(', '.(', $name);
}
if (strpos($name, '.') !== false) {
$chain = explode('.', $name);
$name = array_shift($chain);
$children = array(implode('.', $chain) => $children);
}
$children = (array)$children;
foreach ($children as $key => $val) {
if (is_string($key) && is_string($val) && !in_array($key, $options, true)) {
$children[$key] = (array) $val;
}
}
$keys = array_keys($children);
if ($keys && isset($children[0])) {
$keys = array_merge(array_values($children), $keys);
}
foreach ($keys as $i => $key) {
if (is_array($key)) {
continue;
}
$optionKey = in_array($key, $options, true);
if (!$optionKey && is_string($key) && preg_match('/^[a-z(]/', $key) && (!isset($Model->{$key}) || !is_object($Model->{$key}))) {
$option = 'fields';
$val = array($key);
if ($key{0} == '(') {
$val = preg_split('/\s*,\s*/', substr(substr($key, 1), 0, -1));
} elseif (preg_match('/ASC|DESC$/', $key)) {
$option = 'order';
$val = $Model->{$name}->alias.'.'.$key;
} elseif (preg_match('/[ =!]/', $key)) {
$option = 'conditions';
$val = $Model->{$name}->alias.'.'.$key;
}
$children[$option] = is_array($val) ? $val : array($val);
$newChildren = null;
if (!empty($name) && !empty($children[$key])) {
$newChildren = $children[$key];
}
unset($children[$key], $children[$i]);
$key = $option;
$optionKey = true;
if (!empty($newChildren)) {
$children = Set::merge($children, $newChildren);
}
}
if ($optionKey && isset($children[$key])) {
if (!empty($keep[$name][$key]) && is_array($keep[$name][$key])) {
$keep[$name][$key] = array_merge((isset($keep[$name][$key]) ? $keep[$name][$key] : array()), (array) $children[$key]);
} else {
$keep[$name][$key] = $children[$key];
}
unset($children[$key]);
}
}
if (!isset($Model->{$name}) || !is_object($Model->{$name})) {
if ($throwErrors) {
trigger_error(sprintf(__('Model "%s" is not associated with model "%s"', true), $Model->alias, $name), E_USER_WARNING);
}
continue;
}
$containments = $this->containments($Model->{$name}, $children, $containments);
$depths[] = $containments['depth'] + 1;
if (!isset($keep[$name])) {
$keep[$name] = array();
}
}
if (!isset($containments['models'][$Model->alias])) {
$containments['models'][$Model->alias] = array('keep' => array(),'instance' => &$Model);
}
$containments['models'][$Model->alias]['keep'] = array_merge($containments['models'][$Model->alias]['keep'], $keep);
$containments['depth'] = empty($depths) ? 0 : max($depths);
return $containments;
}
/**
* Calculate needed fields to fetch the required bindings for the given model.
*
* @param object $Model Model
* @param array $map Map of relations for given model
* @param mixed $fields If array, fields to initially load, if false use $Model as primary model
* @return array Fields
* @access public
*/
function fieldDependencies(&$Model, $map, $fields = array()) {
if ($fields === false) {
foreach ($map as $parent => $children) {
foreach ($children as $type => $bindings) {
foreach ($bindings as $dependency) {
if ($type == 'hasAndBelongsToMany') {
$fields[$parent][] = '--primaryKey--';
} else if ($type == 'belongsTo') {
$fields[$parent][] = $dependency . '.--primaryKey--';
}
}
}
}
return $fields;
}
if (empty($map[$Model->alias])) {
return $fields;
}
foreach ($map[$Model->alias] as $type => $bindings) {
foreach ($bindings as $dependency) {
$innerFields = array();
switch ($type) {
case 'belongsTo':
$fields[] = $Model->{$type}[$dependency]['foreignKey'];
break;
case 'hasOne':
case 'hasMany':
$innerFields[] = $Model->$dependency->primaryKey;
$fields[] = $Model->primaryKey;
break;
}
if (!empty($innerFields) && !empty($Model->{$type}[$dependency]['fields'])) {
$Model->{$type}[$dependency]['fields'] = array_unique(array_merge($Model->{$type}[$dependency]['fields'], $innerFields));
}
}
}
return array_unique($fields);
}
/**
* Build the map of containments
*
* @param array $containments Containments
* @return array Built containments
* @access public
*/
function containmentsMap($containments) {
$map = array();
foreach ($containments['models'] as $name => $model) {
$instance =& $model['instance'];
foreach ($this->types as $type) {
foreach ($instance->{$type} as $assoc => $options) {
if (isset($model['keep'][$assoc])) {
$map[$name][$type] = isset($map[$name][$type]) ? array_merge($map[$name][$type], (array)$assoc) : (array)$assoc;
}
}
}
}
return $map;
}
}

View File

@@ -0,0 +1,540 @@
<?php
/**
* Translate behavior
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.model.behaviors
* @since CakePHP(tm) v 1.2.0.4525
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Translate behavior
*
* @package cake
* @subpackage cake.cake.libs.model.behaviors
* @link http://book.cakephp.org/view/1328/Translate
*/
class TranslateBehavior extends ModelBehavior {
/**
* Used for runtime configuration of model
*
* @var array
*/
var $runtime = array();
/**
* Callback
*
* $config for TranslateBehavior should be
* array( 'fields' => array('field_one',
* 'field_two' => 'FieldAssoc', 'field_three'))
*
* With above example only one permanent hasMany will be joined (for field_two
* as FieldAssoc)
*
* $config could be empty - and translations configured dynamically by
* bindTranslation() method
*
* @param Model $model Model the behavior is being attached to.
* @param array $config Array of configuration information.
* @return mixed
* @access public
*/
function setup(&$model, $config = array()) {
$db =& ConnectionManager::getDataSource($model->useDbConfig);
if (!$db->connected) {
trigger_error(
sprintf(__('Datasource %s for TranslateBehavior of model %s is not connected', true), $model->useDbConfig, $model->alias),
E_USER_ERROR
);
return false;
}
$this->settings[$model->alias] = array();
$this->runtime[$model->alias] = array('fields' => array());
$this->translateModel($model);
return $this->bindTranslation($model, $config, false);
}
/**
* Cleanup Callback unbinds bound translations and deletes setting information.
*
* @param Model $model Model being detached.
* @return void
* @access public
*/
function cleanup(&$model) {
$this->unbindTranslation($model);
unset($this->settings[$model->alias]);
unset($this->runtime[$model->alias]);
}
/**
* beforeFind Callback
*
* @param Model $model Model find is being run on.
* @param array $query Array of Query parameters.
* @return array Modified query
* @access public
*/
function beforeFind(&$model, $query) {
$locale = $this->_getLocale($model);
if (empty($locale)) {
return $query;
}
$db =& ConnectionManager::getDataSource($model->useDbConfig);
$RuntimeModel =& $this->translateModel($model);
if (!empty($RuntimeModel->tablePrefix)) {
$tablePrefix = $RuntimeModel->tablePrefix;
} else {
$tablePrefix = $db->config['prefix'];
}
if (is_string($query['fields']) && 'COUNT(*) AS '.$db->name('count') == $query['fields']) {
$query['fields'] = 'COUNT(DISTINCT('.$db->name($model->alias . '.' . $model->primaryKey) . ')) ' . $db->alias . 'count';
$query['joins'][] = array(
'type' => 'INNER',
'alias' => $RuntimeModel->alias,
'table' => $db->name($tablePrefix . $RuntimeModel->useTable),
'conditions' => array(
$model->alias . '.' . $model->primaryKey => $db->identifier($RuntimeModel->alias.'.foreign_key'),
$RuntimeModel->alias.'.model' => $model->name,
$RuntimeModel->alias.'.locale' => $locale
)
);
return $query;
}
$autoFields = false;
if (empty($query['fields'])) {
$query['fields'] = array($model->alias.'.*');
$recursive = $model->recursive;
if (isset($query['recursive'])) {
$recursive = $query['recursive'];
}
if ($recursive >= 0) {
foreach (array('hasOne', 'belongsTo') as $type) {
foreach ($model->{$type} as $key => $value) {
if (empty($value['fields'])) {
$query['fields'][] = $key.'.*';
} else {
foreach ($value['fields'] as $field) {
$query['fields'][] = $key.'.'.$field;
}
}
}
}
}
$autoFields = true;
}
$fields = array_merge($this->settings[$model->alias], $this->runtime[$model->alias]['fields']);
$addFields = array();
if (is_array($query['fields'])) {
foreach ($fields as $key => $value) {
$field = (is_numeric($key)) ? $value : $key;
if (in_array($model->alias.'.*', $query['fields']) || $autoFields || in_array($model->alias.'.'.$field, $query['fields']) || in_array($field, $query['fields'])) {
$addFields[] = $field;
}
}
}
if ($addFields) {
foreach ($addFields as $field) {
foreach (array($field, $model->alias.'.'.$field) as $_field) {
$key = array_search($_field, $query['fields']);
if ($key !== false) {
unset($query['fields'][$key]);
}
}
if (is_array($locale)) {
foreach ($locale as $_locale) {
$query['fields'][] = 'I18n__'.$field.'__'.$_locale.'.content';
$query['joins'][] = array(
'type' => 'LEFT',
'alias' => 'I18n__'.$field.'__'.$_locale,
'table' => $db->name($tablePrefix . $RuntimeModel->useTable),
'conditions' => array(
$model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}__{$_locale}.foreign_key"),
'I18n__'.$field.'__'.$_locale.'.model' => $model->name,
'I18n__'.$field.'__'.$_locale.'.'.$RuntimeModel->displayField => $field,
'I18n__'.$field.'__'.$_locale.'.locale' => $_locale
)
);
}
} else {
$query['fields'][] = 'I18n__'.$field.'.content';
$query['joins'][] = array(
'type' => 'LEFT',
'alias' => 'I18n__'.$field,
'table' => $db->name($tablePrefix . $RuntimeModel->useTable),
'conditions' => array(
$model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}.foreign_key"),
'I18n__'.$field.'.model' => $model->name,
'I18n__'.$field.'.'.$RuntimeModel->displayField => $field
)
);
if (is_string($query['conditions'])) {
$query['conditions'] = $db->conditions($query['conditions'], true, false, $model) . ' AND '.$db->name('I18n__'.$field.'.locale').' = \''.$locale.'\'';
} else {
$query['conditions'][$db->name("I18n__{$field}.locale")] = $locale;
}
}
}
}
if (is_array($query['fields'])) {
$query['fields'] = array_merge($query['fields']);
}
$this->runtime[$model->alias]['beforeFind'] = $addFields;
return $query;
}
/**
* afterFind Callback
*
* @param Model $model Model find was run on
* @param array $results Array of model results.
* @param boolean $primary Did the find originate on $model.
* @return array Modified results
* @access public
*/
function afterFind(&$model, $results, $primary) {
$this->runtime[$model->alias]['fields'] = array();
$locale = $this->_getLocale($model);
if (empty($locale) || empty($results) || empty($this->runtime[$model->alias]['beforeFind'])) {
return $results;
}
$beforeFind = $this->runtime[$model->alias]['beforeFind'];
foreach ($results as $key => $row) {
$results[$key][$model->alias]['locale'] = (is_array($locale)) ? @$locale[0] : $locale;
foreach ($beforeFind as $field) {
if (is_array($locale)) {
foreach ($locale as $_locale) {
if (!isset($results[$key][$model->alias][$field]) && !empty($results[$key]['I18n__'.$field.'__'.$_locale]['content'])) {
$results[$key][$model->alias][$field] = $results[$key]['I18n__'.$field.'__'.$_locale]['content'];
}
unset($results[$key]['I18n__'.$field.'__'.$_locale]);
}
if (!isset($results[$key][$model->alias][$field])) {
$results[$key][$model->alias][$field] = '';
}
} else {
$value = '';
if (!empty($results[$key]['I18n__'.$field]['content'])) {
$value = $results[$key]['I18n__'.$field]['content'];
}
$results[$key][$model->alias][$field] = $value;
unset($results[$key]['I18n__'.$field]);
}
}
}
return $results;
}
/**
* beforeValidate Callback
*
* @param Model $model Model invalidFields was called on.
* @return boolean
* @access public
*/
function beforeValidate(&$model) {
$locale = $this->_getLocale($model);
if (empty($locale)) {
return true;
}
$fields = array_merge($this->settings[$model->alias], $this->runtime[$model->alias]['fields']);
$tempData = array();
foreach ($fields as $key => $value) {
$field = (is_numeric($key)) ? $value : $key;
if (isset($model->data[$model->alias][$field])) {
$tempData[$field] = $model->data[$model->alias][$field];
if (is_array($model->data[$model->alias][$field])) {
if (is_string($locale) && !empty($model->data[$model->alias][$field][$locale])) {
$model->data[$model->alias][$field] = $model->data[$model->alias][$field][$locale];
} else {
$values = array_values($model->data[$model->alias][$field]);
$model->data[$model->alias][$field] = $values[0];
}
}
}
}
$this->runtime[$model->alias]['beforeSave'] = $tempData;
return true;
}
/**
* afterSave Callback
*
* @param Model $model Model the callback is called on
* @param boolean $created Whether or not the save created a record.
* @return void
* @access public
*/
function afterSave(&$model, $created) {
if (!isset($this->runtime[$model->alias]['beforeSave'])) {
return true;
}
$locale = $this->_getLocale($model);
$tempData = $this->runtime[$model->alias]['beforeSave'];
unset($this->runtime[$model->alias]['beforeSave']);
$conditions = array('model' => $model->alias, 'foreign_key' => $model->id);
$RuntimeModel =& $this->translateModel($model);
foreach ($tempData as $field => $value) {
unset($conditions['content']);
$conditions['field'] = $field;
if (is_array($value)) {
$conditions['locale'] = array_keys($value);
} else {
$conditions['locale'] = $locale;
if (is_array($locale)) {
$value = array($locale[0] => $value);
} else {
$value = array($locale => $value);
}
}
$translations = $RuntimeModel->find('list', array('conditions' => $conditions, 'fields' => array($RuntimeModel->alias . '.locale', $RuntimeModel->alias . '.id')));
foreach ($value as $_locale => $_value) {
$RuntimeModel->create();
$conditions['locale'] = $_locale;
$conditions['content'] = $_value;
if (array_key_exists($_locale, $translations)) {
$RuntimeModel->save(array($RuntimeModel->alias => array_merge($conditions, array('id' => $translations[$_locale]))));
} else {
$RuntimeModel->save(array($RuntimeModel->alias => $conditions));
}
}
}
}
/**
* afterDelete Callback
*
* @param Model $model Model the callback was run on.
* @return void
* @access public
*/
function afterDelete(&$model) {
$RuntimeModel =& $this->translateModel($model);
$conditions = array('model' => $model->alias, 'foreign_key' => $model->id);
$RuntimeModel->deleteAll($conditions);
}
/**
* Get selected locale for model
*
* @param Model $model Model the locale needs to be set/get on.
* @return mixed string or false
* @access protected
*/
function _getLocale(&$model) {
if (!isset($model->locale) || is_null($model->locale)) {
if (!class_exists('I18n')) {
App::import('Core', 'i18n');
}
$I18n =& I18n::getInstance();
$I18n->l10n->get(Configure::read('Config.language'));
$model->locale = $I18n->l10n->locale;
}
return $model->locale;
}
/**
* Get instance of model for translations.
*
* If the model has a translateModel property set, this will be used as the class
* name to find/use. If no translateModel property is found 'I18nModel' will be used.
*
* @param Model $model Model to get a translatemodel for.
* @return object
* @access public
*/
function &translateModel(&$model) {
if (!isset($this->runtime[$model->alias]['model'])) {
if (!isset($model->translateModel) || empty($model->translateModel)) {
$className = 'I18nModel';
} else {
$className = $model->translateModel;
}
if (PHP5) {
$this->runtime[$model->alias]['model'] = ClassRegistry::init($className, 'Model');
} else {
$this->runtime[$model->alias]['model'] =& ClassRegistry::init($className, 'Model');
}
}
if (!empty($model->translateTable) && $model->translateTable !== $this->runtime[$model->alias]['model']->useTable) {
$this->runtime[$model->alias]['model']->setSource($model->translateTable);
} elseif (empty($model->translateTable) && empty($model->translateModel)) {
$this->runtime[$model->alias]['model']->setSource('i18n');
}
$model =& $this->runtime[$model->alias]['model'];
return $model;
}
/**
* Bind translation for fields, optionally with hasMany association for
* fake field
*
* @param object instance of model
* @param mixed string with field or array(field1, field2=>AssocName, field3)
* @param boolean $reset
* @return bool
*/
function bindTranslation(&$model, $fields, $reset = true) {
if (is_string($fields)) {
$fields = array($fields);
}
$associations = array();
$RuntimeModel =& $this->translateModel($model);
$default = array('className' => $RuntimeModel->alias, 'foreignKey' => 'foreign_key');
foreach ($fields as $key => $value) {
if (is_numeric($key)) {
$field = $value;
$association = null;
} else {
$field = $key;
$association = $value;
}
if (array_key_exists($field, $this->settings[$model->alias])) {
unset($this->settings[$model->alias][$field]);
} elseif (in_array($field, $this->settings[$model->alias])) {
$this->settings[$model->alias] = array_merge(array_diff_assoc($this->settings[$model->alias], array($field)));
}
if (array_key_exists($field, $this->runtime[$model->alias]['fields'])) {
unset($this->runtime[$model->alias]['fields'][$field]);
} elseif (in_array($field, $this->runtime[$model->alias]['fields'])) {
$this->runtime[$model->alias]['fields'] = array_merge(array_diff_assoc($this->runtime[$model->alias]['fields'], array($field)));
}
if (is_null($association)) {
if ($reset) {
$this->runtime[$model->alias]['fields'][] = $field;
} else {
$this->settings[$model->alias][] = $field;
}
} else {
if ($reset) {
$this->runtime[$model->alias]['fields'][$field] = $association;
} else {
$this->settings[$model->alias][$field] = $association;
}
foreach (array('hasOne', 'hasMany', 'belongsTo', 'hasAndBelongsToMany') as $type) {
if (isset($model->{$type}[$association]) || isset($model->__backAssociation[$type][$association])) {
trigger_error(
sprintf(__('Association %s is already binded to model %s', true), $association, $model->alias),
E_USER_ERROR
);
return false;
}
}
$associations[$association] = array_merge($default, array('conditions' => array(
'model' => $model->alias,
$RuntimeModel->displayField => $field
)));
}
}
if (!empty($associations)) {
$model->bindModel(array('hasMany' => $associations), $reset);
}
return true;
}
/**
* Unbind translation for fields, optionally unbinds hasMany association for
* fake field
*
* @param object $model instance of model
* @param mixed $fields string with field, or array(field1, field2=>AssocName, field3), or null for
* unbind all original translations
* @return bool
*/
function unbindTranslation(&$model, $fields = null) {
if (empty($fields) && empty($this->settings[$model->alias])) {
return false;
}
if (empty($fields)) {
return $this->unbindTranslation($model, $this->settings[$model->alias]);
}
if (is_string($fields)) {
$fields = array($fields);
}
$RuntimeModel =& $this->translateModel($model);
$associations = array();
foreach ($fields as $key => $value) {
if (is_numeric($key)) {
$field = $value;
$association = null;
} else {
$field = $key;
$association = $value;
}
if (array_key_exists($field, $this->settings[$model->alias])) {
unset($this->settings[$model->alias][$field]);
} elseif (in_array($field, $this->settings[$model->alias])) {
$this->settings[$model->alias] = array_merge(array_diff_assoc($this->settings[$model->alias], array($field)));
}
if (array_key_exists($field, $this->runtime[$model->alias]['fields'])) {
unset($this->runtime[$model->alias]['fields'][$field]);
} elseif (in_array($field, $this->runtime[$model->alias]['fields'])) {
$this->runtime[$model->alias]['fields'] = array_merge(array_diff_assoc($this->runtime[$model->alias]['fields'], array($field)));
}
if (!is_null($association) && (isset($model->hasMany[$association]) || isset($model->__backAssociation['hasMany'][$association]))) {
$associations[] = $association;
}
}
if (!empty($associations)) {
$model->unbindModel(array('hasMany' => $associations), false);
}
return true;
}
}
if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) {
/**
* @package cake
* @subpackage cake.cake.libs.model.behaviors
*/
class I18nModel extends AppModel {
var $name = 'I18nModel';
var $useTable = 'i18n';
var $displayField = 'field';
}
}

View File

@@ -0,0 +1,977 @@
<?php
/**
* Tree behavior class.
*
* Enables a model object to act as a node-based tree.
*
* PHP versions 4 and 5
*
* CakePHP : Rapid Development Framework (http://cakephp.org)
* Copyright 2006-2010, Cake Software Foundation, Inc.
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2006-2010, Cake Software Foundation, Inc.
* @link http://cakephp.org CakePHP Project
* @package cake
* @subpackage cake.cake.libs.model.behaviors
* @since CakePHP v 1.2.0.4487
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Tree Behavior.
*
* Enables a model object to act as a node-based tree. Using Modified Preorder Tree Traversal
*
* @see http://en.wikipedia.org/wiki/Tree_traversal
* @package cake
* @subpackage cake.cake.libs.model.behaviors
* @link http://book.cakephp.org/view/1339/Tree
*/
class TreeBehavior extends ModelBehavior {
/**
* Errors
*
* @var array
*/
var $errors = array();
/**
* Defaults
*
* @var array
* @access protected
*/
var $_defaults = array(
'parent' => 'parent_id', 'left' => 'lft', 'right' => 'rght',
'scope' => '1 = 1', 'type' => 'nested', '__parentChange' => false, 'recursive' => -1
);
/**
* Initiate Tree behavior
*
* @param object $Model instance of model
* @param array $config array of configuration settings.
* @return void
* @access public
*/
function setup(&$Model, $config = array()) {
if (!is_array($config)) {
$config = array('type' => $config);
}
$settings = array_merge($this->_defaults, $config);
if (in_array($settings['scope'], $Model->getAssociated('belongsTo'))) {
$data = $Model->getAssociated($settings['scope']);
$parent =& $Model->{$settings['scope']};
$settings['scope'] = $Model->alias . '.' . $data['foreignKey'] . ' = ' . $parent->alias . '.' . $parent->primaryKey;
$settings['recursive'] = 0;
}
$this->settings[$Model->alias] = $settings;
}
/**
* After save method. Called after all saves
*
* Overriden to transparently manage setting the lft and rght fields if and only if the parent field is included in the
* parameters to be saved.
*
* @param AppModel $Model Model instance.
* @param boolean $created indicates whether the node just saved was created or updated
* @return boolean true on success, false on failure
* @access public
*/
function afterSave(&$Model, $created) {
extract($this->settings[$Model->alias]);
if ($created) {
if ((isset($Model->data[$Model->alias][$parent])) && $Model->data[$Model->alias][$parent]) {
return $this->_setParent($Model, $Model->data[$Model->alias][$parent], $created);
}
} elseif ($__parentChange) {
$this->settings[$Model->alias]['__parentChange'] = false;
return $this->_setParent($Model, $Model->data[$Model->alias][$parent]);
}
}
/**
* Before delete method. Called before all deletes
*
* Will delete the current node and all children using the deleteAll method and sync the table
*
* @param AppModel $Model Model instance
* @return boolean true to continue, false to abort the delete
* @access public
*/
function beforeDelete(&$Model) {
extract($this->settings[$Model->alias]);
list($name, $data) = array($Model->alias, $Model->read());
$data = $data[$name];
if (!$data[$right] || !$data[$left]) {
return true;
}
$diff = $data[$right] - $data[$left] + 1;
if ($diff > 2) {
if (is_string($scope)) {
$scope = array($scope);
}
$scope[]["{$Model->alias}.{$left} BETWEEN ? AND ?"] = array($data[$left] + 1, $data[$right] - 1);
$Model->deleteAll($scope);
}
$this->__sync($Model, $diff, '-', '> ' . $data[$right]);
return true;
}
/**
* Before save method. Called before all saves
*
* Overriden to transparently manage setting the lft and rght fields if and only if the parent field is included in the
* parameters to be saved. For newly created nodes with NO parent the left and right field values are set directly by
* this method bypassing the setParent logic.
*
* @since 1.2
* @param AppModel $Model Model instance
* @return boolean true to continue, false to abort the save
* @access public
*/
function beforeSave(&$Model) {
extract($this->settings[$Model->alias]);
$this->_addToWhitelist($Model, array($left, $right));
if (!$Model->id) {
if (array_key_exists($parent, $Model->data[$Model->alias]) && $Model->data[$Model->alias][$parent]) {
$parentNode = $Model->find('first', array(
'conditions' => array($scope, $Model->escapeField() => $Model->data[$Model->alias][$parent]),
'fields' => array($Model->primaryKey, $right), 'recursive' => $recursive
));
if (!$parentNode) {
return false;
}
list($parentNode) = array_values($parentNode);
$Model->data[$Model->alias][$left] = 0; //$parentNode[$right];
$Model->data[$Model->alias][$right] = 0; //$parentNode[$right] + 1;
} else {
$edge = $this->__getMax($Model, $scope, $right, $recursive);
$Model->data[$Model->alias][$left] = $edge + 1;
$Model->data[$Model->alias][$right] = $edge + 2;
}
} elseif (array_key_exists($parent, $Model->data[$Model->alias])) {
if ($Model->data[$Model->alias][$parent] != $Model->field($parent)) {
$this->settings[$Model->alias]['__parentChange'] = true;
}
if (!$Model->data[$Model->alias][$parent]) {
$Model->data[$Model->alias][$parent] = null;
$this->_addToWhitelist($Model, $parent);
} else {
$values = $Model->find('first', array(
'conditions' => array($scope,$Model->escapeField() => $Model->id),
'fields' => array($Model->primaryKey, $parent, $left, $right ), 'recursive' => $recursive)
);
if ($values === false) {
return false;
}
list($node) = array_values($values);
$parentNode = $Model->find('first', array(
'conditions' => array($scope, $Model->escapeField() => $Model->data[$Model->alias][$parent]),
'fields' => array($Model->primaryKey, $left, $right), 'recursive' => $recursive
));
if (!$parentNode) {
return false;
}
list($parentNode) = array_values($parentNode);
if (($node[$left] < $parentNode[$left]) && ($parentNode[$right] < $node[$right])) {
return false;
} elseif ($node[$Model->primaryKey] == $parentNode[$Model->primaryKey]) {
return false;
}
}
}
return true;
}
/**
* Get the number of child nodes
*
* If the direct parameter is set to true, only the direct children are counted (based upon the parent_id field)
* If false is passed for the id parameter, all top level nodes are counted, or all nodes are counted.
*
* @param AppModel $Model Model instance
* @param mixed $id The ID of the record to read or false to read all top level nodes
* @param boolean $direct whether to count direct, or all, children
* @return integer number of child nodes
* @access public
* @link http://book.cakephp.org/view/1347/Counting-children
*/
function childcount(&$Model, $id = null, $direct = false) {
if (is_array($id)) {
extract (array_merge(array('id' => null), $id));
}
if ($id === null && $Model->id) {
$id = $Model->id;
} elseif (!$id) {
$id = null;
}
extract($this->settings[$Model->alias]);
if ($direct) {
return $Model->find('count', array('conditions' => array($scope, $Model->escapeField($parent) => $id)));
}
if ($id === null) {
return $Model->find('count', array('conditions' => $scope));
} elseif (isset($Model->data[$Model->alias][$left]) && isset($Model->data[$Model->alias][$right])) {
$data = $Model->data[$Model->alias];
} else {
$data = $Model->find('first', array('conditions' => array($scope, $Model->escapeField() => $id), 'recursive' => $recursive));
if (!$data) {
return 0;
}
$data = $data[$Model->alias];
}
return ($data[$right] - $data[$left] - 1) / 2;
}
/**
* Get the child nodes of the current model
*
* If the direct parameter is set to true, only the direct children are returned (based upon the parent_id field)
* If false is passed for the id parameter, top level, or all (depending on direct parameter appropriate) are counted.
*
* @param AppModel $Model Model instance
* @param mixed $id The ID of the record to read
* @param boolean $direct whether to return only the direct, or all, children
* @param mixed $fields Either a single string of a field name, or an array of field names
* @param string $order SQL ORDER BY conditions (e.g. "price DESC" or "name ASC") defaults to the tree order
* @param integer $limit SQL LIMIT clause, for calculating items per page.
* @param integer $page Page number, for accessing paged data
* @param integer $recursive The number of levels deep to fetch associated records
* @return array Array of child nodes
* @access public
* @link http://book.cakephp.org/view/1346/Children
*/
function children(&$Model, $id = null, $direct = false, $fields = null, $order = null, $limit = null, $page = 1, $recursive = null) {
if (is_array($id)) {
extract (array_merge(array('id' => null), $id));
}
$overrideRecursive = $recursive;
if ($id === null && $Model->id) {
$id = $Model->id;
} elseif (!$id) {
$id = null;
}
$name = $Model->alias;
extract($this->settings[$Model->alias]);
if (!is_null($overrideRecursive)) {
$recursive = $overrideRecursive;
}
if (!$order) {
$order = $Model->alias . '.' . $left . ' asc';
}
if ($direct) {
$conditions = array($scope, $Model->escapeField($parent) => $id);
return $Model->find('all', compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive'));
}
if (!$id) {
$conditions = $scope;
} else {
$result = array_values((array)$Model->find('first', array(
'conditions' => array($scope, $Model->escapeField() => $id),
'fields' => array($left, $right),
'recursive' => $recursive
)));
if (empty($result) || !isset($result[0])) {
return array();
}
$conditions = array($scope,
$Model->escapeField($right) . ' <' => $result[0][$right],
$Model->escapeField($left) . ' >' => $result[0][$left]
);
}
return $Model->find('all', compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive'));
}
/**
* A convenience method for returning a hierarchical array used for HTML select boxes
*
* @param AppModel $Model Model instance
* @param mixed $conditions SQL conditions as a string or as an array('field' =>'value',...)
* @param string $keyPath A string path to the key, i.e. "{n}.Post.id"
* @param string $valuePath A string path to the value, i.e. "{n}.Post.title"
* @param string $spacer The character or characters which will be repeated
* @param integer $recursive The number of levels deep to fetch associated records
* @return array An associative array of records, where the id is the key, and the display field is the value
* @access public
* @link http://book.cakephp.org/view/1348/generatetreelist
*/
function generatetreelist(&$Model, $conditions = null, $keyPath = null, $valuePath = null, $spacer = '_', $recursive = null) {
$overrideRecursive = $recursive;
extract($this->settings[$Model->alias]);
if (!is_null($overrideRecursive)) {
$recursive = $overrideRecursive;
}
if ($keyPath == null && $valuePath == null && $Model->hasField($Model->displayField)) {
$fields = array($Model->primaryKey, $Model->displayField, $left, $right);
} else {
$fields = null;
}
if ($keyPath == null) {
$keyPath = '{n}.' . $Model->alias . '.' . $Model->primaryKey;
}
if ($valuePath == null) {
$valuePath = array('{0}{1}', '{n}.tree_prefix', '{n}.' . $Model->alias . '.' . $Model->displayField);
} elseif (is_string($valuePath)) {
$valuePath = array('{0}{1}', '{n}.tree_prefix', $valuePath);
} else {
$valuePath[0] = '{' . (count($valuePath) - 1) . '}' . $valuePath[0];
$valuePath[] = '{n}.tree_prefix';
}
$order = $Model->alias . '.' . $left . ' asc';
$results = $Model->find('all', compact('conditions', 'fields', 'order', 'recursive'));
$stack = array();
foreach ($results as $i => $result) {
while ($stack && ($stack[count($stack) - 1] < $result[$Model->alias][$right])) {
array_pop($stack);
}
$results[$i]['tree_prefix'] = str_repeat($spacer,count($stack));
$stack[] = $result[$Model->alias][$right];
}
if (empty($results)) {
return array();
}
return Set::combine($results, $keyPath, $valuePath);
}
/**
* Get the parent node
*
* reads the parent id and returns this node
*
* @param AppModel $Model Model instance
* @param mixed $id The ID of the record to read
* @param integer $recursive The number of levels deep to fetch associated records
* @return array Array of data for the parent node
* @access public
* @link http://book.cakephp.org/view/1349/getparentnode
*/
function getparentnode(&$Model, $id = null, $fields = null, $recursive = null) {
if (is_array($id)) {
extract (array_merge(array('id' => null), $id));
}
$overrideRecursive = $recursive;
if (empty ($id)) {
$id = $Model->id;
}
extract($this->settings[$Model->alias]);
if (!is_null($overrideRecursive)) {
$recursive = $overrideRecursive;
}
$parentId = $Model->find('first', array('conditions' => array($Model->primaryKey => $id), 'fields' => array($parent), 'recursive' => -1));
if ($parentId) {
$parentId = $parentId[$Model->alias][$parent];
$parent = $Model->find('first', array('conditions' => array($Model->escapeField() => $parentId), 'fields' => $fields, 'recursive' => $recursive));
return $parent;
}
return false;
}
/**
* Get the path to the given node
*
* @param AppModel $Model Model instance
* @param mixed $id The ID of the record to read
* @param mixed $fields Either a single string of a field name, or an array of field names
* @param integer $recursive The number of levels deep to fetch associated records
* @return array Array of nodes from top most parent to current node
* @access public
* @link http://book.cakephp.org/view/1350/getpath
*/
function getpath(&$Model, $id = null, $fields = null, $recursive = null) {
if (is_array($id)) {
extract (array_merge(array('id' => null), $id));
}
$overrideRecursive = $recursive;
if (empty ($id)) {
$id = $Model->id;
}
extract($this->settings[$Model->alias]);
if (!is_null($overrideRecursive)) {
$recursive = $overrideRecursive;
}
$result = $Model->find('first', array('conditions' => array($Model->escapeField() => $id), 'fields' => array($left, $right), 'recursive' => $recursive));
if ($result) {
$result = array_values($result);
} else {
return null;
}
$item = $result[0];
$results = $Model->find('all', array(
'conditions' => array($scope, $Model->escapeField($left) . ' <=' => $item[$left], $Model->escapeField($right) . ' >=' => $item[$right]),
'fields' => $fields, 'order' => array($Model->escapeField($left) => 'asc'), 'recursive' => $recursive
));
return $results;
}
/**
* Reorder the node without changing the parent.
*
* If the node is the last child, or is a top level node with no subsequent node this method will return false
*
* @param AppModel $Model Model instance
* @param mixed $id The ID of the record to move
* @param mixed $number how many places to move the node or true to move to last position
* @return boolean true on success, false on failure
* @access public
* @link http://book.cakephp.org/view/1352/moveDown
*/
function movedown(&$Model, $id = null, $number = 1) {
if (is_array($id)) {
extract (array_merge(array('id' => null), $id));
}
if (!$number) {
return false;
}
if (empty ($id)) {
$id = $Model->id;
}
extract($this->settings[$Model->alias]);
list($node) = array_values($Model->find('first', array(
'conditions' => array($scope, $Model->escapeField() => $id),
'fields' => array($Model->primaryKey, $left, $right, $parent), 'recursive' => $recursive
)));
if ($node[$parent]) {
list($parentNode) = array_values($Model->find('first', array(
'conditions' => array($scope, $Model->escapeField() => $node[$parent]),
'fields' => array($Model->primaryKey, $left, $right), 'recursive' => $recursive
)));
if (($node[$right] + 1) == $parentNode[$right]) {
return false;
}
}
$nextNode = $Model->find('first', array(
'conditions' => array($scope, $Model->escapeField($left) => ($node[$right] + 1)),
'fields' => array($Model->primaryKey, $left, $right), 'recursive' => $recursive)
);
if ($nextNode) {
list($nextNode) = array_values($nextNode);
} else {
return false;
}
$edge = $this->__getMax($Model, $scope, $right, $recursive);
$this->__sync($Model, $edge - $node[$left] + 1, '+', 'BETWEEN ' . $node[$left] . ' AND ' . $node[$right]);
$this->__sync($Model, $nextNode[$left] - $node[$left], '-', 'BETWEEN ' . $nextNode[$left] . ' AND ' . $nextNode[$right]);
$this->__sync($Model, $edge - $node[$left] - ($nextNode[$right] - $nextNode[$left]), '-', '> ' . $edge);
if (is_int($number)) {
$number--;
}
if ($number) {
$this->moveDown($Model, $id, $number);
}
return true;
}
/**
* Reorder the node without changing the parent.
*
* If the node is the first child, or is a top level node with no previous node this method will return false
*
* @param AppModel $Model Model instance
* @param mixed $id The ID of the record to move
* @param mixed $number how many places to move the node, or true to move to first position
* @return boolean true on success, false on failure
* @access public
* @link http://book.cakephp.org/view/1353/moveUp
*/
function moveup(&$Model, $id = null, $number = 1) {
if (is_array($id)) {
extract (array_merge(array('id' => null), $id));
}
if (!$number) {
return false;
}
if (empty ($id)) {
$id = $Model->id;
}
extract($this->settings[$Model->alias]);
list($node) = array_values($Model->find('first', array(
'conditions' => array($scope, $Model->escapeField() => $id),
'fields' => array($Model->primaryKey, $left, $right, $parent ), 'recursive' => $recursive
)));
if ($node[$parent]) {
list($parentNode) = array_values($Model->find('first', array(
'conditions' => array($scope, $Model->escapeField() => $node[$parent]),
'fields' => array($Model->primaryKey, $left, $right), 'recursive' => $recursive
)));
if (($node[$left] - 1) == $parentNode[$left]) {
return false;
}
}
$previousNode = $Model->find('first', array(
'conditions' => array($scope, $Model->escapeField($right) => ($node[$left] - 1)),
'fields' => array($Model->primaryKey, $left, $right),
'recursive' => $recursive
));
if ($previousNode) {
list($previousNode) = array_values($previousNode);
} else {
return false;
}
$edge = $this->__getMax($Model, $scope, $right, $recursive);
$this->__sync($Model, $edge - $previousNode[$left] +1, '+', 'BETWEEN ' . $previousNode[$left] . ' AND ' . $previousNode[$right]);
$this->__sync($Model, $node[$left] - $previousNode[$left], '-', 'BETWEEN ' .$node[$left] . ' AND ' . $node[$right]);
$this->__sync($Model, $edge - $previousNode[$left] - ($node[$right] - $node[$left]), '-', '> ' . $edge);
if (is_int($number)) {
$number--;
}
if ($number) {
$this->moveUp($Model, $id, $number);
}
return true;
}
/**
* Recover a corrupted tree
*
* The mode parameter is used to specify the source of info that is valid/correct. The opposite source of data
* will be populated based upon that source of info. E.g. if the MPTT fields are corrupt or empty, with the $mode
* 'parent' the values of the parent_id field will be used to populate the left and right fields. The missingParentAction
* parameter only applies to "parent" mode and determines what to do if the parent field contains an id that is not present.
*
* @todo Could be written to be faster, *maybe*. Ideally using a subquery and putting all the logic burden on the DB.
* @param AppModel $Model Model instance
* @param string $mode parent or tree
* @param mixed $missingParentAction 'return' to do nothing and return, 'delete' to
* delete, or the id of the parent to set as the parent_id
* @return boolean true on success, false on failure
* @access public
* @link http://book.cakephp.org/view/1628/Recover
*/
function recover(&$Model, $mode = 'parent', $missingParentAction = null) {
if (is_array($mode)) {
extract (array_merge(array('mode' => 'parent'), $mode));
}
extract($this->settings[$Model->alias]);
$Model->recursive = $recursive;
if ($mode == 'parent') {
$Model->bindModel(array('belongsTo' => array('VerifyParent' => array(
'className' => $Model->alias,
'foreignKey' => $parent,
'fields' => array($Model->primaryKey, $left, $right, $parent),
))));
$missingParents = $Model->find('list', array(
'recursive' => 0,
'conditions' => array($scope, array(
'NOT' => array($Model->escapeField($parent) => null), $Model->VerifyParent->escapeField() => null
))
));
$Model->unbindModel(array('belongsTo' => array('VerifyParent')));
if ($missingParents) {
if ($missingParentAction == 'return') {
foreach ($missingParents as $id => $display) {
$this->errors[] = 'cannot find the parent for ' . $Model->alias . ' with id ' . $id . '(' . $display . ')';
}
return false;
} elseif ($missingParentAction == 'delete') {
$Model->deleteAll(array($Model->primaryKey => array_flip($missingParents)));
} else {
$Model->updateAll(array($parent => $missingParentAction), array($Model->escapeField($Model->primaryKey) => array_flip($missingParents)));
}
}
$count = 1;
foreach ($Model->find('all', array('conditions' => $scope, 'fields' => array($Model->primaryKey), 'order' => $left)) as $array) {
$Model->id = $array[$Model->alias][$Model->primaryKey];
$lft = $count++;
$rght = $count++;
$Model->save(array($left => $lft, $right => $rght), array('callbacks' => false));
}
foreach ($Model->find('all', array('conditions' => $scope, 'fields' => array($Model->primaryKey, $parent), 'order' => $left)) as $array) {
$Model->create();
$Model->id = $array[$Model->alias][$Model->primaryKey];
$this->_setParent($Model, $array[$Model->alias][$parent]);
}
} else {
$db =& ConnectionManager::getDataSource($Model->useDbConfig);
foreach ($Model->find('all', array('conditions' => $scope, 'fields' => array($Model->primaryKey, $parent), 'order' => $left)) as $array) {
$path = $this->getpath($Model, $array[$Model->alias][$Model->primaryKey]);
if ($path == null || count($path) < 2) {
$parentId = null;
} else {
$parentId = $path[count($path) - 2][$Model->alias][$Model->primaryKey];
}
$Model->updateAll(array($parent => $db->value($parentId, $parent)), array($Model->escapeField() => $array[$Model->alias][$Model->primaryKey]));
}
}
return true;
}
/**
* Reorder method.
*
* Reorders the nodes (and child nodes) of the tree according to the field and direction specified in the parameters.
* This method does not change the parent of any node.
*
* Requires a valid tree, by default it verifies the tree before beginning.
*
* Options:
*
* - 'id' id of record to use as top node for reordering
* - 'field' Which field to use in reordeing defaults to displayField
* - 'order' Direction to order either DESC or ASC (defaults to ASC)
* - 'verify' Whether or not to verify the tree before reorder. defaults to true.
*
* @param AppModel $Model Model instance
* @param array $options array of options to use in reordering.
* @return boolean true on success, false on failure
* @link http://book.cakephp.org/view/1355/reorder
* @link http://book.cakephp.org/view/1629/Reorder
*/
function reorder(&$Model, $options = array()) {
$options = array_merge(array('id' => null, 'field' => $Model->displayField, 'order' => 'ASC', 'verify' => true), $options);
extract($options);
if ($verify && !$this->verify($Model)) {
return false;
}
$verify = false;
extract($this->settings[$Model->alias]);
$fields = array($Model->primaryKey, $field, $left, $right);
$sort = $field . ' ' . $order;
$nodes = $this->children($Model, $id, true, $fields, $sort, null, null, $recursive);
$cacheQueries = $Model->cacheQueries;
$Model->cacheQueries = false;
if ($nodes) {
foreach ($nodes as $node) {
$id = $node[$Model->alias][$Model->primaryKey];
$this->moveDown($Model, $id, true);
if ($node[$Model->alias][$left] != $node[$Model->alias][$right] - 1) {
$this->reorder($Model, compact('id', 'field', 'order', 'verify'));
}
}
}
$Model->cacheQueries = $cacheQueries;
return true;
}
/**
* Remove the current node from the tree, and reparent all children up one level.
*
* If the parameter delete is false, the node will become a new top level node. Otherwise the node will be deleted
* after the children are reparented.
*
* @param AppModel $Model Model instance
* @param mixed $id The ID of the record to remove
* @param boolean $delete whether to delete the node after reparenting children (if any)
* @return boolean true on success, false on failure
* @access public
* @link http://book.cakephp.org/view/1354/removeFromTree
*/
function removefromtree(&$Model, $id = null, $delete = false) {
if (is_array($id)) {
extract (array_merge(array('id' => null), $id));
}
extract($this->settings[$Model->alias]);
list($node) = array_values($Model->find('first', array(
'conditions' => array($scope, $Model->escapeField() => $id),
'fields' => array($Model->primaryKey, $left, $right, $parent),
'recursive' => $recursive
)));
if ($node[$right] == $node[$left] + 1) {
if ($delete) {
return $Model->delete($id);
} else {
$Model->id = $id;
return $Model->saveField($parent, null);
}
} elseif ($node[$parent]) {
list($parentNode) = array_values($Model->find('first', array(
'conditions' => array($scope, $Model->escapeField() => $node[$parent]),
'fields' => array($Model->primaryKey, $left, $right),
'recursive' => $recursive
)));
} else {
$parentNode[$right] = $node[$right] + 1;
}
$db =& ConnectionManager::getDataSource($Model->useDbConfig);
$Model->updateAll(
array($parent => $db->value($node[$parent], $parent)),
array($Model->escapeField($parent) => $node[$Model->primaryKey])
);
$this->__sync($Model, 1, '-', 'BETWEEN ' . ($node[$left] + 1) . ' AND ' . ($node[$right] - 1));
$this->__sync($Model, 2, '-', '> ' . ($node[$right]));
$Model->id = $id;
if ($delete) {
$Model->updateAll(
array(
$Model->escapeField($left) => 0,
$Model->escapeField($right) => 0,
$Model->escapeField($parent) => null
),
array($Model->escapeField() => $id)
);
return $Model->delete($id);
} else {
$edge = $this->__getMax($Model, $scope, $right, $recursive);
if ($node[$right] == $edge) {
$edge = $edge - 2;
}
$Model->id = $id;
return $Model->save(
array($left => $edge + 1, $right => $edge + 2, $parent => null),
array('callbacks' => false)
);
}
}
/**
* Check if the current tree is valid.
*
* Returns true if the tree is valid otherwise an array of (type, incorrect left/right index, message)
*
* @param AppModel $Model Model instance
* @return mixed true if the tree is valid or empty, otherwise an array of (error type [index, node],
* [incorrect left/right index,node id], message)
* @access public
* @link http://book.cakephp.org/view/1630/Verify
*/
function verify(&$Model) {
extract($this->settings[$Model->alias]);
if (!$Model->find('count', array('conditions' => $scope))) {
return true;
}
$min = $this->__getMin($Model, $scope, $left, $recursive);
$edge = $this->__getMax($Model, $scope, $right, $recursive);
$errors = array();
for ($i = $min; $i <= $edge; $i++) {
$count = $Model->find('count', array('conditions' => array(
$scope, 'OR' => array($Model->escapeField($left) => $i, $Model->escapeField($right) => $i)
)));
if ($count != 1) {
if ($count == 0) {
$errors[] = array('index', $i, 'missing');
} else {
$errors[] = array('index', $i, 'duplicate');
}
}
}
$node = $Model->find('first', array('conditions' => array($scope, $Model->escapeField($right) . '< ' . $Model->escapeField($left)), 'recursive' => 0));
if ($node) {
$errors[] = array('node', $node[$Model->alias][$Model->primaryKey], 'left greater than right.');
}
$Model->bindModel(array('belongsTo' => array('VerifyParent' => array(
'className' => $Model->alias,
'foreignKey' => $parent,
'fields' => array($Model->primaryKey, $left, $right, $parent)
))));
foreach ($Model->find('all', array('conditions' => $scope, 'recursive' => 0)) as $instance) {
if (is_null($instance[$Model->alias][$left]) || is_null($instance[$Model->alias][$right])) {
$errors[] = array('node', $instance[$Model->alias][$Model->primaryKey],
'has invalid left or right values');
} elseif ($instance[$Model->alias][$left] == $instance[$Model->alias][$right]) {
$errors[] = array('node', $instance[$Model->alias][$Model->primaryKey],
'left and right values identical');
} elseif ($instance[$Model->alias][$parent]) {
if (!$instance['VerifyParent'][$Model->primaryKey]) {
$errors[] = array('node', $instance[$Model->alias][$Model->primaryKey],
'The parent node ' . $instance[$Model->alias][$parent] . ' doesn\'t exist');
} elseif ($instance[$Model->alias][$left] < $instance['VerifyParent'][$left]) {
$errors[] = array('node', $instance[$Model->alias][$Model->primaryKey],
'left less than parent (node ' . $instance['VerifyParent'][$Model->primaryKey] . ').');
} elseif ($instance[$Model->alias][$right] > $instance['VerifyParent'][$right]) {
$errors[] = array('node', $instance[$Model->alias][$Model->primaryKey],
'right greater than parent (node ' . $instance['VerifyParent'][$Model->primaryKey] . ').');
}
} elseif ($Model->find('count', array('conditions' => array($scope, $Model->escapeField($left) . ' <' => $instance[$Model->alias][$left], $Model->escapeField($right) . ' >' => $instance[$Model->alias][$right]), 'recursive' => 0))) {
$errors[] = array('node', $instance[$Model->alias][$Model->primaryKey], 'The parent field is blank, but has a parent');
}
}
if ($errors) {
return $errors;
}
return true;
}
/**
* Sets the parent of the given node
*
* The force parameter is used to override the "don't change the parent to the current parent" logic in the event
* of recovering a corrupted table, or creating new nodes. Otherwise it should always be false. In reality this
* method could be private, since calling save with parent_id set also calls setParent
*
* @param AppModel $Model Model instance
* @param mixed $parentId
* @return boolean true on success, false on failure
* @access protected
*/
function _setParent(&$Model, $parentId = null, $created = false) {
extract($this->settings[$Model->alias]);
list($node) = array_values($Model->find('first', array(
'conditions' => array($scope, $Model->escapeField() => $Model->id),
'fields' => array($Model->primaryKey, $parent, $left, $right),
'recursive' => $recursive
)));
$edge = $this->__getMax($Model, $scope, $right, $recursive, $created);
if (empty ($parentId)) {
$this->__sync($Model, $edge - $node[$left] + 1, '+', 'BETWEEN ' . $node[$left] . ' AND ' . $node[$right], $created);
$this->__sync($Model, $node[$right] - $node[$left] + 1, '-', '> ' . $node[$left], $created);
} else {
$values = $Model->find('first', array(
'conditions' => array($scope, $Model->escapeField() => $parentId),
'fields' => array($Model->primaryKey, $left, $right),
'recursive' => $recursive
));
if ($values === false) {
return false;
}
$parentNode = array_values($values);
if (empty($parentNode) || empty($parentNode[0])) {
return false;
}
$parentNode = $parentNode[0];
if (($Model->id == $parentId)) {
return false;
} elseif (($node[$left] < $parentNode[$left]) && ($parentNode[$right] < $node[$right])) {
return false;
}
if (empty ($node[$left]) && empty ($node[$right])) {
$this->__sync($Model, 2, '+', '>= ' . $parentNode[$right], $created);
$result = $Model->save(
array($left => $parentNode[$right], $right => $parentNode[$right] + 1, $parent => $parentId),
array('validate' => false, 'callbacks' => false)
);
$Model->data = $result;
} else {
$this->__sync($Model, $edge - $node[$left] +1, '+', 'BETWEEN ' . $node[$left] . ' AND ' . $node[$right], $created);
$diff = $node[$right] - $node[$left] + 1;
if ($node[$left] > $parentNode[$left]) {
if ($node[$right] < $parentNode[$right]) {
$this->__sync($Model, $diff, '-', 'BETWEEN ' . $node[$right] . ' AND ' . ($parentNode[$right] - 1), $created);
$this->__sync($Model, $edge - $parentNode[$right] + $diff + 1, '-', '> ' . $edge, $created);
} else {
$this->__sync($Model, $diff, '+', 'BETWEEN ' . $parentNode[$right] . ' AND ' . $node[$right], $created);
$this->__sync($Model, $edge - $parentNode[$right] + 1, '-', '> ' . $edge, $created);
}
} else {
$this->__sync($Model, $diff, '-', 'BETWEEN ' . $node[$right] . ' AND ' . ($parentNode[$right] - 1), $created);
$this->__sync($Model, $edge - $parentNode[$right] + $diff + 1, '-', '> ' . $edge, $created);
}
}
}
return true;
}
/**
* get the maximum index value in the table.
*
* @param AppModel $Model
* @param string $scope
* @param string $right
* @return int
* @access private
*/
function __getMax($Model, $scope, $right, $recursive = -1, $created = false) {
$db =& ConnectionManager::getDataSource($Model->useDbConfig);
if ($created) {
if (is_string($scope)) {
$scope .= " AND {$Model->alias}.{$Model->primaryKey} <> ";
$scope .= $db->value($Model->id, $Model->getColumnType($Model->primaryKey));
} else {
$scope['NOT'][$Model->alias . '.' . $Model->primaryKey] = $Model->id;
}
}
$name = $Model->alias . '.' . $right;
list($edge) = array_values($Model->find('first', array(
'conditions' => $scope,
'fields' => $db->calculate($Model, 'max', array($name, $right)),
'recursive' => $recursive
)));
return (empty($edge[$right])) ? 0 : $edge[$right];
}
/**
* get the minimum index value in the table.
*
* @param AppModel $Model
* @param string $scope
* @param string $right
* @return int
* @access private
*/
function __getMin($Model, $scope, $left, $recursive = -1) {
$db =& ConnectionManager::getDataSource($Model->useDbConfig);
$name = $Model->alias . '.' . $left;
list($edge) = array_values($Model->find('first', array(
'conditions' => $scope,
'fields' => $db->calculate($Model, 'min', array($name, $left)),
'recursive' => $recursive
)));
return (empty($edge[$left])) ? 0 : $edge[$left];
}
/**
* Table sync method.
*
* Handles table sync operations, Taking account of the behavior scope.
*
* @param AppModel $Model
* @param integer $shift
* @param string $direction
* @param array $conditions
* @param string $field
* @access private
*/
function __sync(&$Model, $shift, $dir = '+', $conditions = array(), $created = false, $field = 'both') {
$ModelRecursive = $Model->recursive;
extract($this->settings[$Model->alias]);
$Model->recursive = $recursive;
if ($field == 'both') {
$this->__sync($Model, $shift, $dir, $conditions, $created, $left);
$field = $right;
}
if (is_string($conditions)) {
$conditions = array("{$Model->alias}.{$field} {$conditions}");
}
if (($scope != '1 = 1' && $scope !== true) && $scope) {
$conditions[] = $scope;
}
if ($created) {
$conditions['NOT'][$Model->alias . '.' . $Model->primaryKey] = $Model->id;
}
$Model->updateAll(array($Model->alias . '.' . $field => $Model->escapeField($field) . ' ' . $dir . ' ' . $shift), $conditions);
$Model->recursive = $ModelRecursive;
}
}

View File

@@ -0,0 +1,693 @@
<?php
/**
* Schema database management for CakePHP.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.model
* @since CakePHP(tm) v 1.2.0.5550
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Core', array('Model', 'ConnectionManager'));
/**
* Base Class for Schema management
*
* @package cake
* @subpackage cake.cake.libs.model
*/
class CakeSchema extends Object {
/**
* Name of the App Schema
*
* @var string
* @access public
*/
var $name = null;
/**
* Path to write location
*
* @var string
* @access public
*/
var $path = null;
/**
* File to write
*
* @var string
* @access public
*/
var $file = 'schema.php';
/**
* Connection used for read
*
* @var string
* @access public
*/
var $connection = 'default';
/**
* plugin name.
*
* @var string
*/
var $plugin = null;
/**
* Set of tables
*
* @var array
* @access public
*/
var $tables = array();
/**
* Constructor
*
* @param array $options optional load object properties
*/
function __construct($options = array()) {
parent::__construct();
if (empty($options['name'])) {
$this->name = preg_replace('/schema$/i', '', get_class($this));
}
if (!empty($options['plugin'])) {
$this->plugin = $options['plugin'];
}
if (strtolower($this->name) === 'cake') {
$this->name = Inflector::camelize(Inflector::slug(Configure::read('App.dir')));
}
if (empty($options['path'])) {
if (is_dir(CONFIGS . 'schema')) {
$this->path = CONFIGS . 'schema';
} else {
$this->path = CONFIGS . 'sql';
}
}
$options = array_merge(get_object_vars($this), $options);
$this->_build($options);
}
/**
* Builds schema object properties
*
* @param array $data loaded object properties
* @return void
* @access protected
*/
function _build($data) {
$file = null;
foreach ($data as $key => $val) {
if (!empty($val)) {
if (!in_array($key, array('plugin', 'name', 'path', 'file', 'connection', 'tables', '_log'))) {
$this->tables[$key] = $val;
unset($this->{$key});
} elseif ($key !== 'tables') {
if ($key === 'name' && $val !== $this->name && !isset($data['file'])) {
$file = Inflector::underscore($val) . '.php';
}
$this->{$key} = $val;
}
}
}
if (file_exists($this->path . DS . $file) && is_file($this->path . DS . $file)) {
$this->file = $file;
} elseif (!empty($this->plugin)) {
$this->path = App::pluginPath($this->plugin) . 'config' . DS . 'schema';
}
}
/**
* Before callback to be implemented in subclasses
*
* @param array $events schema object properties
* @return boolean Should process continue
* @access public
*/
function before($event = array()) {
return true;
}
/**
* After callback to be implemented in subclasses
*
* @param array $events schema object properties
* @access public
*/
function after($event = array()) {
}
/**
* Reads database and creates schema tables
*
* @param array $options schema object properties
* @return array Set of name and tables
* @access public
*/
function &load($options = array()) {
if (is_string($options)) {
$options = array('path' => $options);
}
$this->_build($options);
extract(get_object_vars($this));
$class = $name .'Schema';
if (!class_exists($class)) {
if (file_exists($path . DS . $file) && is_file($path . DS . $file)) {
require_once($path . DS . $file);
} elseif (file_exists($path . DS . 'schema.php') && is_file($path . DS . 'schema.php')) {
require_once($path . DS . 'schema.php');
}
}
if (class_exists($class)) {
$Schema =& new $class($options);
return $Schema;
}
$false = false;
return $false;
}
/**
* Reads database and creates schema tables
*
* Options
*
* - 'connection' - the db connection to use
* - 'name' - name of the schema
* - 'models' - a list of models to use, or false to ignore models
*
* @param array $options schema object properties
* @return array Array indexed by name and tables
* @access public
*/
function read($options = array()) {
extract(array_merge(
array(
'connection' => $this->connection,
'name' => $this->name,
'models' => true,
),
$options
));
$db =& ConnectionManager::getDataSource($connection);
App::import('Model', 'AppModel');
if (isset($this->plugin)) {
App::import('Model', Inflector::camelize($this->plugin) . 'AppModel');
}
$tables = array();
$currentTables = $db->listSources();
$prefix = null;
if (isset($db->config['prefix'])) {
$prefix = $db->config['prefix'];
}
if (!is_array($models) && $models !== false) {
if (isset($this->plugin)) {
$models = App::objects('model', App::pluginPath($this->plugin) . 'models' . DS, false);
} else {
$models = App::objects('model');
}
}
if (is_array($models)) {
foreach ($models as $model) {
$importModel = $model;
if (isset($this->plugin)) {
$importModel = $this->plugin . '.' . $model;
}
if (!App::import('Model', $importModel)) {
continue;
}
$vars = get_class_vars($model);
if (empty($vars['useDbConfig']) || $vars['useDbConfig'] != $connection) {
continue;
}
if (PHP5) {
$Object = ClassRegistry::init(array('class' => $model, 'ds' => $connection));
} else {
$Object =& ClassRegistry::init(array('class' => $model, 'ds' => $connection));
}
if (is_object($Object) && $Object->useTable !== false) {
$fulltable = $table = $db->fullTableName($Object, false);
if ($prefix && strpos($table, $prefix) !== 0) {
continue;
}
$table = str_replace($prefix, '', $table);
if (in_array($fulltable, $currentTables)) {
$key = array_search($fulltable, $currentTables);
if (empty($tables[$table])) {
$tables[$table] = $this->__columns($Object);
$tables[$table]['indexes'] = $db->index($Object);
$tables[$table]['tableParameters'] = $db->readTableParameters($fulltable);
unset($currentTables[$key]);
}
if (!empty($Object->hasAndBelongsToMany)) {
foreach ($Object->hasAndBelongsToMany as $Assoc => $assocData) {
if (isset($assocData['with'])) {
$class = $assocData['with'];
}
if (is_object($Object->$class)) {
$withTable = $db->fullTableName($Object->$class, false);
if (in_array($withTable, $currentTables)) {
$key = array_search($withTable, $currentTables);
$tables[$withTable] = $this->__columns($Object->$class);
$tables[$withTable]['indexes'] = $db->index($Object->$class);
$tables[$withTable]['tableParameters'] = $db->readTableParameters($withTable);
unset($currentTables[$key]);
}
}
}
}
}
}
}
}
if (!empty($currentTables)) {
foreach ($currentTables as $table) {
if ($prefix) {
if (strpos($table, $prefix) !== 0) {
continue;
}
$table = str_replace($prefix, '', $table);
}
$Object = new AppModel(array(
'name' => Inflector::classify($table), 'table' => $table, 'ds' => $connection
));
$systemTables = array(
'aros', 'acos', 'aros_acos', Configure::read('Session.table'), 'i18n'
);
if (in_array($table, $systemTables)) {
$tables[$Object->table] = $this->__columns($Object);
$tables[$Object->table]['indexes'] = $db->index($Object);
$tables[$Object->table]['tableParameters'] = $db->readTableParameters($table);
} elseif ($models === false) {
$tables[$table] = $this->__columns($Object);
$tables[$table]['indexes'] = $db->index($Object);
$tables[$table]['tableParameters'] = $db->readTableParameters($table);
} else {
$tables['missing'][$table] = $this->__columns($Object);
$tables['missing'][$table]['indexes'] = $db->index($Object);
$tables['missing'][$table]['tableParameters'] = $db->readTableParameters($table);
}
}
}
ksort($tables);
return compact('name', 'tables');
}
/**
* Writes schema file from object or options
*
* @param mixed $object schema object or options array
* @param array $options schema object properties to override object
* @return mixed false or string written to file
* @access public
*/
function write($object, $options = array()) {
if (is_object($object)) {
$object = get_object_vars($object);
$this->_build($object);
}
if (is_array($object)) {
$options = $object;
unset($object);
}
extract(array_merge(
get_object_vars($this), $options
));
$out = "class {$name}Schema extends CakeSchema {\n";
$out .= "\tvar \$name = '{$name}';\n\n";
if ($path !== $this->path) {
$out .= "\tvar \$path = '{$path}';\n\n";
}
if ($file !== $this->file) {
$out .= "\tvar \$file = '{$file}';\n\n";
}
if ($connection !== 'default') {
$out .= "\tvar \$connection = '{$connection}';\n\n";
}
$out .= "\tfunction before(\$event = array()) {\n\t\treturn true;\n\t}\n\n\tfunction after(\$event = array()) {\n\t}\n\n";
if (empty($tables)) {
$this->read();
}
foreach ($tables as $table => $fields) {
if (!is_numeric($table) && $table !== 'missing') {
$out .= $this->generateTable($table, $fields);
}
}
$out .= "}\n";
$File =& new File($path . DS . $file, true);
$header = '$Id';
$content = "<?php \n/* SVN FILE: {$header}$ */\n/* {$name} schema generated on: " . date('Y-m-d H:m:s') . " : ". time() . "*/\n{$out}?>";
$content = $File->prepare($content);
if ($File->write($content)) {
return $content;
}
return false;
}
/**
* Generate the code for a table. Takes a table name and $fields array
* Returns a completed variable declaration to be used in schema classes
*
* @param string $table Table name you want returned.
* @param array $fields Array of field information to generate the table with.
* @return string Variable declaration for a schema class
*/
function generateTable($table, $fields) {
$out = "\tvar \${$table} = array(\n";
if (is_array($fields)) {
$cols = array();
foreach ($fields as $field => $value) {
if ($field != 'indexes' && $field != 'tableParameters') {
if (is_string($value)) {
$type = $value;
$value = array('type'=> $type);
}
$col = "\t\t'{$field}' => array('type' => '" . $value['type'] . "', ";
unset($value['type']);
$col .= join(', ', $this->__values($value));
} elseif ($field == 'indexes') {
$col = "\t\t'indexes' => array(";
$props = array();
foreach ((array)$value as $key => $index) {
$props[] = "'{$key}' => array(" . join(', ', $this->__values($index)) . ")";
}
$col .= join(', ', $props);
} elseif ($field == 'tableParameters') {
//@todo add charset, collate and engine here
$col = "\t\t'tableParameters' => array(";
$props = array();
foreach ((array)$value as $key => $param) {
$props[] = "'{$key}' => '$param'";
}
$col .= join(', ', $props);
}
$col .= ")";
$cols[] = $col;
}
$out .= join(",\n", $cols);
}
$out .= "\n\t);\n";
return $out;
}
/**
* Compares two sets of schemas
*
* @param mixed $old Schema object or array
* @param mixed $new Schema object or array
* @return array Tables (that are added, dropped, or changed)
* @access public
*/
function compare($old, $new = null) {
if (empty($new)) {
$new =& $this;
}
if (is_array($new)) {
if (isset($new['tables'])) {
$new = $new['tables'];
}
} else {
$new = $new->tables;
}
if (is_array($old)) {
if (isset($old['tables'])) {
$old = $old['tables'];
}
} else {
$old = $old->tables;
}
$tables = array();
foreach ($new as $table => $fields) {
if ($table == 'missing') {
continue;
}
if (!array_key_exists($table, $old)) {
$tables[$table]['add'] = $fields;
} else {
$diff = $this->_arrayDiffAssoc($fields, $old[$table]);
if (!empty($diff)) {
$tables[$table]['add'] = $diff;
}
$diff = $this->_arrayDiffAssoc($old[$table], $fields);
if (!empty($diff)) {
$tables[$table]['drop'] = $diff;
}
}
foreach ($fields as $field => $value) {
if (isset($old[$table][$field])) {
$diff = $this->_arrayDiffAssoc($value, $old[$table][$field]);
if (!empty($diff) && $field !== 'indexes' && $field !== 'tableParameters') {
$tables[$table]['change'][$field] = array_merge($old[$table][$field], $diff);
}
}
if (isset($add[$table][$field])) {
$wrapper = array_keys($fields);
if ($column = array_search($field, $wrapper)) {
if (isset($wrapper[$column - 1])) {
$tables[$table]['add'][$field]['after'] = $wrapper[$column - 1];
}
}
}
}
if (isset($old[$table]['indexes']) && isset($new[$table]['indexes'])) {
$diff = $this->_compareIndexes($new[$table]['indexes'], $old[$table]['indexes']);
if ($diff) {
if (!isset($tables[$table])) {
$tables[$table] = array();
}
if (isset($diff['drop'])) {
$tables[$table]['drop']['indexes'] = $diff['drop'];
}
if ($diff && isset($diff['add'])) {
$tables[$table]['add']['indexes'] = $diff['add'];
}
}
}
if (isset($old[$table]['tableParameters']) && isset($new[$table]['tableParameters'])) {
$diff = $this->_compareTableParameters($new[$table]['tableParameters'], $old[$table]['tableParameters']);
if ($diff) {
$tables[$table]['change']['tableParameters'] = $diff;
}
}
}
return $tables;
}
/**
* Extended array_diff_assoc noticing change from/to NULL values
*
* It behaves almost the same way as array_diff_assoc except for NULL values: if
* one of the values is not NULL - change is detected. It is useful in situation
* where one value is strval('') ant other is strval(null) - in string comparing
* methods this results as EQUAL, while it is not.
*
* @param array $array1 Base array
* @param array $array2 Corresponding array checked for equality
* @return array Difference as array with array(keys => values) from input array
* where match was not found.
* @access protected
*/
function _arrayDiffAssoc($array1, $array2) {
$difference = array();
foreach ($array1 as $key => $value) {
if (!array_key_exists($key, $array2)) {
$difference[$key] = $value;
continue;
}
$correspondingValue = $array2[$key];
if (is_null($value) !== is_null($correspondingValue)) {
$difference[$key] = $value;
continue;
}
if (is_bool($value) !== is_bool($correspondingValue)) {
$difference[$key] = $value;
continue;
}
$compare = strval($value);
$correspondingValue = strval($correspondingValue);
if ($compare === $correspondingValue) {
continue;
}
$difference[$key] = $value;
}
return $difference;
}
/**
* Formats Schema columns from Model Object
*
* @param array $values options keys(type, null, default, key, length, extra)
* @return array Formatted values
* @access public
*/
function __values($values) {
$vals = array();
if (is_array($values)) {
foreach ($values as $key => $val) {
if (is_array($val)) {
$vals[] = "'{$key}' => array('" . implode("', '", $val) . "')";
} else if (!is_numeric($key)) {
$val = var_export($val, true);
$vals[] = "'{$key}' => {$val}";
}
}
}
return $vals;
}
/**
* Formats Schema columns from Model Object
*
* @param array $Obj model object
* @return array Formatted columns
* @access public
*/
function __columns(&$Obj) {
$db =& ConnectionManager::getDataSource($Obj->useDbConfig);
$fields = $Obj->schema(true);
$columns = $props = array();
foreach ($fields as $name => $value) {
if ($Obj->primaryKey == $name) {
$value['key'] = 'primary';
}
if (!isset($db->columns[$value['type']])) {
trigger_error(sprintf(__('Schema generation error: invalid column type %s does not exist in DBO', true), $value['type']), E_USER_NOTICE);
continue;
} else {
$defaultCol = $db->columns[$value['type']];
if (isset($defaultCol['limit']) && $defaultCol['limit'] == $value['length']) {
unset($value['length']);
} elseif (isset($defaultCol['length']) && $defaultCol['length'] == $value['length']) {
unset($value['length']);
}
unset($value['limit']);
}
if (isset($value['default']) && ($value['default'] === '' || $value['default'] === false)) {
unset($value['default']);
}
if (empty($value['length'])) {
unset($value['length']);
}
if (empty($value['key'])) {
unset($value['key']);
}
$columns[$name] = $value;
}
return $columns;
}
/**
* Compare two schema files table Parameters
*
* @param array $new New indexes
* @param array $old Old indexes
* @return mixed False on failure, or an array of parameters to add & drop.
*/
function _compareTableParameters($new, $old) {
if (!is_array($new) || !is_array($old)) {
return false;
}
$change = $this->_arrayDiffAssoc($new, $old);
return $change;
}
/**
* Compare two schema indexes
*
* @param array $new New indexes
* @param array $old Old indexes
* @return mixed false on failure or array of indexes to add and drop
*/
function _compareIndexes($new, $old) {
if (!is_array($new) || !is_array($old)) {
return false;
}
$add = $drop = array();
$diff = $this->_arrayDiffAssoc($new, $old);
if (!empty($diff)) {
$add = $diff;
}
$diff = $this->_arrayDiffAssoc($old, $new);
if (!empty($diff)) {
$drop = $diff;
}
foreach ($new as $name => $value) {
if (isset($old[$name])) {
$newUnique = isset($value['unique']) ? $value['unique'] : 0;
$oldUnique = isset($old[$name]['unique']) ? $old[$name]['unique'] : 0;
$newColumn = $value['column'];
$oldColumn = $old[$name]['column'];
$diff = false;
if ($newUnique != $oldUnique) {
$diff = true;
} elseif (is_array($newColumn) && is_array($oldColumn)) {
$diff = ($newColumn !== $oldColumn);
} elseif (is_string($newColumn) && is_string($oldColumn)) {
$diff = ($newColumn != $oldColumn);
} else {
$diff = true;
}
if ($diff) {
$drop[$name] = null;
$add[$name] = $value;
}
}
}
return array_filter(compact('add', 'drop'));
}
}

View File

@@ -0,0 +1,295 @@
<?php
/**
* Datasource connection manager
*
* Provides an interface for loading and enumerating connections defined in app/config/database.php
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.model
* @since CakePHP(tm) v 0.10.x.1402
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
require LIBS . 'model' . DS . 'datasources' . DS . 'datasource.php';
include_once CONFIGS . 'database.php';
/**
* Manages loaded instances of DataSource objects
*
* @package cake
* @subpackage cake.cake.libs.model
*/
class ConnectionManager extends Object {
/**
* Holds a loaded instance of the Connections object
*
* @var DATABASE_CONFIG
* @access public
*/
var $config = null;
/**
* Holds instances DataSource objects
*
* @var array
* @access protected
*/
var $_dataSources = array();
/**
* Contains a list of all file and class names used in Connection settings
*
* @var array
* @access protected
*/
var $_connectionsEnum = array();
/**
* Constructor.
*
*/
function __construct() {
if (class_exists('DATABASE_CONFIG')) {
$this->config =& new DATABASE_CONFIG();
$this->_getConnectionObjects();
}
}
/**
* Gets a reference to the ConnectionManger object instance
*
* @return object Instance
* @access public
* @static
*/
function &getInstance() {
static $instance = array();
if (!$instance) {
$instance[0] =& new ConnectionManager();
}
return $instance[0];
}
/**
* Gets a reference to a DataSource object
*
* @param string $name The name of the DataSource, as defined in app/config/database.php
* @return object Instance
* @access public
* @static
*/
function &getDataSource($name) {
$_this =& ConnectionManager::getInstance();
if (!empty($_this->_dataSources[$name])) {
$return =& $_this->_dataSources[$name];
return $return;
}
if (empty($_this->_connectionsEnum[$name])) {
trigger_error(sprintf(__("ConnectionManager::getDataSource - Non-existent data source %s", true), $name), E_USER_ERROR);
$null = null;
return $null;
}
$conn = $_this->_connectionsEnum[$name];
$class = $conn['classname'];
if ($_this->loadDataSource($name) === null) {
trigger_error(sprintf(__("ConnectionManager::getDataSource - Could not load class %s", true), $class), E_USER_ERROR);
$null = null;
return $null;
}
$_this->_dataSources[$name] =& new $class($_this->config->{$name});
$_this->_dataSources[$name]->configKeyName = $name;
$return =& $_this->_dataSources[$name];
return $return;
}
/**
* Gets the list of available DataSource connections
*
* @return array List of available connections
* @access public
* @static
*/
function sourceList() {
$_this =& ConnectionManager::getInstance();
return array_keys($_this->_dataSources);
}
/**
* Gets a DataSource name from an object reference.
*
* **Warning** this method may cause fatal errors in PHP4.
*
* @param object $source DataSource object
* @return string Datasource name, or null if source is not present
* in the ConnectionManager.
* @access public
* @static
*/
function getSourceName(&$source) {
$_this =& ConnectionManager::getInstance();
foreach ($_this->_dataSources as $name => $ds) {
if ($ds == $source) {
return $name;
}
}
return '';
}
/**
* Loads the DataSource class for the given connection name
*
* @param mixed $connName A string name of the connection, as defined in app/config/database.php,
* or an array containing the filename (without extension) and class name of the object,
* to be found in app/models/datasources/ or cake/libs/model/datasources/.
* @return boolean True on success, null on failure or false if the class is already loaded
* @access public
* @static
*/
function loadDataSource($connName) {
$_this =& ConnectionManager::getInstance();
if (is_array($connName)) {
$conn = $connName;
} else {
$conn = $_this->_connectionsEnum[$connName];
}
if (class_exists($conn['classname'])) {
return false;
}
if (!empty($conn['parent'])) {
$_this->loadDataSource($conn['parent']);
}
$conn = array_merge(array('plugin' => null, 'classname' => null, 'parent' => null), $conn);
$class = "{$conn['plugin']}.{$conn['classname']}";
if (!App::import('Datasource', $class, !is_null($conn['plugin']))) {
trigger_error(sprintf(__('ConnectionManager::loadDataSource - Unable to import DataSource class %s', true), $class), E_USER_ERROR);
return null;
}
return true;
}
/**
* Return a list of connections
*
* @return array An associative array of elements where the key is the connection name
* (as defined in Connections), and the value is an array with keys 'filename' and 'classname'.
* @access public
* @static
*/
function enumConnectionObjects() {
$_this =& ConnectionManager::getInstance();
return $_this->_connectionsEnum;
}
/**
* Dynamically creates a DataSource object at runtime, with the given name and settings
*
* @param string $name The DataSource name
* @param array $config The DataSource configuration settings
* @return object A reference to the DataSource object, or null if creation failed
* @access public
* @static
*/
function &create($name = '', $config = array()) {
$_this =& ConnectionManager::getInstance();
if (empty($name) || empty($config) || array_key_exists($name, $_this->_connectionsEnum)) {
$null = null;
return $null;
}
$_this->config->{$name} = $config;
$_this->_connectionsEnum[$name] = $_this->__connectionData($config);
$return =& $_this->getDataSource($name);
return $return;
}
/**
* Gets a list of class and file names associated with the user-defined DataSource connections
*
* @return void
* @access protected
* @static
*/
function _getConnectionObjects() {
$connections = get_object_vars($this->config);
if ($connections != null) {
foreach ($connections as $name => $config) {
$this->_connectionsEnum[$name] = $this->__connectionData($config);
}
} else {
$this->cakeError('missingConnection', array(array('code' => 500, 'className' => 'ConnectionManager')));
}
}
/**
* Returns the file, class name, and parent for the given driver.
*
* @return array An indexed array with: filename, classname, plugin and parent
* @access private
*/
function __connectionData($config) {
if (!isset($config['datasource'])) {
$config['datasource'] = 'dbo';
}
$filename = $classname = $parent = $plugin = null;
if (!empty($config['driver'])) {
$parent = $this->__connectionData(array('datasource' => $config['datasource']));
$parentSource = preg_replace('/_source$/', '', $parent['filename']);
list($plugin, $classname) = pluginSplit($config['driver']);
if ($plugin) {
$source = Inflector::underscore($classname);
} else {
$source = $parentSource . '_' . $config['driver'];
$classname = Inflector::camelize(strtolower($source));
}
$filename = $parentSource . DS . $source;
} else {
list($plugin, $classname) = pluginSplit($config['datasource']);
if ($plugin) {
$filename = Inflector::underscore($classname);
} else {
$filename = Inflector::underscore($config['datasource']);
}
if (substr($filename, -7) != '_source') {
$filename .= '_source';
}
$classname = Inflector::camelize(strtolower($filename));
}
return compact('filename', 'classname', 'parent', 'plugin');
}
/**
* Destructor.
*
* @access private
*/
function __destruct() {
if (Configure::read('Session.save') == 'database' && function_exists('session_write_close')) {
session_write_close();
}
}
}

View File

@@ -0,0 +1,596 @@
<?php
/**
* DataSource base class
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.model.datasources
* @since CakePHP(tm) v 0.10.5.1790
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* DataSource base class
*
* @package cake
* @subpackage cake.cake.libs.model.datasources
*/
class DataSource extends Object {
/**
* Are we connected to the DataSource?
*
* @var boolean
* @access public
*/
var $connected = false;
/**
* Print full query debug info?
*
* @var boolean
* @access public
*/
var $fullDebug = false;
/**
* Error description of last query
*
* @var unknown_type
* @access public
*/
var $error = null;
/**
* String to hold how many rows were affected by the last SQL operation.
*
* @var string
* @access public
*/
var $affected = null;
/**
* Number of rows in current resultset
*
* @var int
* @access public
*/
var $numRows = null;
/**
* Time the last query took
*
* @var int
* @access public
*/
var $took = null;
/**
* The starting character that this DataSource uses for quoted identifiers.
*
* @var string
* @access public
*/
var $startQuote = null;
/**
* The ending character that this DataSource uses for quoted identifiers.
*
* @var string
* @access public
*/
var $endQuote = null;
/**
* Result
*
* @var array
* @access protected
*/
var $_result = null;
/**
* Queries count.
*
* @var int
* @access protected
*/
var $_queriesCnt = 0;
/**
* Total duration of all queries.
*
* @var unknown_type
* @access protected
*/
var $_queriesTime = null;
/**
* Log of queries executed by this DataSource
*
* @var unknown_type
* @access protected
*/
var $_queriesLog = array();
/**
* Maximum number of items in query log
*
* This is to prevent query log taking over too much memory.
*
* @var int Maximum number of queries in the queries log.
* @access protected
*/
var $_queriesLogMax = 200;
/**
* Caches serialzed results of executed queries
*
* @var array Maximum number of queries in the queries log.
* @access protected
*/
var $_queryCache = array();
/**
* The default configuration of a specific DataSource
*
* @var array
* @access protected
*/
var $_baseConfig = array();
/**
* Holds references to descriptions loaded by the DataSource
*
* @var array
* @access private
*/
var $__descriptions = array();
/**
* Holds a list of sources (tables) contained in the DataSource
*
* @var array
* @access protected
*/
var $_sources = null;
/**
* A reference to the physical connection of this DataSource
*
* @var array
* @access public
*/
var $connection = null;
/**
* The DataSource configuration
*
* @var array
* @access public
*/
var $config = array();
/**
* The DataSource configuration key name
*
* @var string
* @access public
*/
var $configKeyName = null;
/**
* Whether or not this DataSource is in the middle of a transaction
*
* @var boolean
* @access protected
*/
var $_transactionStarted = false;
/**
* Whether or not source data like available tables and schema descriptions
* should be cached
*
* @var boolean
* @access public
*/
var $cacheSources = true;
/**
* Constructor.
*
* @param array $config Array of configuration information for the datasource.
* @return void.
*/
function __construct($config = array()) {
parent::__construct();
$this->setConfig($config);
}
/**
* Caches/returns cached results for child instances
*
* @param mixed $data
* @return array Array of sources available in this datasource.
* @access public
*/
function listSources($data = null) {
if ($this->cacheSources === false) {
return null;
}
if ($this->_sources !== null) {
return $this->_sources;
}
$key = ConnectionManager::getSourceName($this) . '_' . $this->config['database'] . '_list';
$key = preg_replace('/[^A-Za-z0-9_\-.+]/', '_', $key);
$sources = Cache::read($key, '_cake_model_');
if (empty($sources)) {
$sources = $data;
Cache::write($key, $data, '_cake_model_');
}
$this->_sources = $sources;
return $sources;
}
/**
* Convenience method for DboSource::listSources(). Returns source names in lowercase.
*
* @param boolean $reset Whether or not the source list should be reset.
* @return array Array of sources available in this datasource
* @access public
*/
function sources($reset = false) {
if ($reset === true) {
$this->_sources = null;
}
return array_map('strtolower', $this->listSources());
}
/**
* Returns a Model description (metadata) or null if none found.
*
* @param Model $model
* @return array Array of Metadata for the $model
* @access public
*/
function describe(&$model) {
if ($this->cacheSources === false) {
return null;
}
$table = $model->tablePrefix . $model->table;
if (isset($this->__descriptions[$table])) {
return $this->__descriptions[$table];
}
$cache = $this->__cacheDescription($table);
if ($cache !== null) {
$this->__descriptions[$table] =& $cache;
return $cache;
}
return null;
}
/**
* Begin a transaction
*
* @return boolean Returns true if a transaction is not in progress
* @access public
*/
function begin(&$model) {
return !$this->_transactionStarted;
}
/**
* Commit a transaction
*
* @return boolean Returns true if a transaction is in progress
* @access public
*/
function commit(&$model) {
return $this->_transactionStarted;
}
/**
* Rollback a transaction
*
* @return boolean Returns true if a transaction is in progress
* @access public
*/
function rollback(&$model) {
return $this->_transactionStarted;
}
/**
* Converts column types to basic types
*
* @param string $real Real column type (i.e. "varchar(255)")
* @return string Abstract column type (i.e. "string")
* @access public
*/
function column($real) {
return false;
}
/**
* Used to create new records. The "C" CRUD.
*
* To-be-overridden in subclasses.
*
* @param Model $model The Model to be created.
* @param array $fields An Array of fields to be saved.
* @param array $values An Array of values to save.
* @return boolean success
* @access public
*/
function create(&$model, $fields = null, $values = null) {
return false;
}
/**
* Used to read records from the Datasource. The "R" in CRUD
*
* To-be-overridden in subclasses.
*
* @param Model $model The model being read.
* @param array $queryData An array of query data used to find the data you want
* @return mixed
* @access public
*/
function read(&$model, $queryData = array()) {
return false;
}
/**
* Update a record(s) in the datasource.
*
* To-be-overridden in subclasses.
*
* @param Model $model Instance of the model class being updated
* @param array $fields Array of fields to be updated
* @param array $values Array of values to be update $fields to.
* @return boolean Success
* @access public
*/
function update(&$model, $fields = null, $values = null) {
return false;
}
/**
* Delete a record(s) in the datasource.
*
* To-be-overridden in subclasses.
*
* @param Model $model The model class having record(s) deleted
* @param mixed $id Primary key of the model
* @access public
*/
function delete(&$model, $id = null) {
if ($id == null) {
$id = $model->id;
}
}
/**
* Returns the ID generated from the previous INSERT operation.
*
* @param unknown_type $source
* @return mixed Last ID key generated in previous INSERT
* @access public
*/
function lastInsertId($source = null) {
return false;
}
/**
* Returns the ID generated from the previous INSERT operation.
*
* @param unknown_type $source
* @return integer Number of rows returned by last operation
* @access public
*/
function lastNumRows($source = null) {
return false;
}
/**
* Returns the ID generated from the previous INSERT operation.
*
* @param unknown_type $source
* @return integer Number of rows affected by last query.
* @access public
*/
function lastAffected($source = null) {
return false;
}
/**
* Check whether the conditions for the Datasource being available
* are satisfied. Often used from connect() to check for support
* before establishing a connection.
*
* @return boolean Whether or not the Datasources conditions for use are met.
* @access public
*/
function enabled() {
return true;
}
/**
* Returns true if the DataSource supports the given interface (method)
*
* @param string $interface The name of the interface (method)
* @return boolean True on success
* @access public
*/
function isInterfaceSupported($interface) {
static $methods = false;
if ($methods === false) {
$methods = array_map('strtolower', get_class_methods($this));
}
return in_array(strtolower($interface), $methods);
}
/**
* Sets the configuration for the DataSource.
* Merges the $config information with the _baseConfig and the existing $config property.
*
* @param array $config The configuration array
* @return void
* @access public
*/
function setConfig($config = array()) {
$this->config = array_merge($this->_baseConfig, $this->config, $config);
}
/**
* Cache the DataSource description
*
* @param string $object The name of the object (model) to cache
* @param mixed $data The description of the model, usually a string or array
* @return mixed
* @access private
*/
function __cacheDescription($object, $data = null) {
if ($this->cacheSources === false) {
return null;
}
if ($data !== null) {
$this->__descriptions[$object] =& $data;
}
$key = ConnectionManager::getSourceName($this) . '_' . $object;
$cache = Cache::read($key, '_cake_model_');
if (empty($cache)) {
$cache = $data;
Cache::write($key, $cache, '_cake_model_');
}
return $cache;
}
/**
* Replaces `{$__cakeID__$}` and `{$__cakeForeignKey__$}` placeholders in query data.
*
* @param string $query Query string needing replacements done.
* @param array $data Array of data with values that will be inserted in placeholders.
* @param string $association Name of association model being replaced
* @param unknown_type $assocData
* @param Model $model Instance of the model to replace $__cakeID__$
* @param Model $linkModel Instance of model to replace $__cakeForeignKey__$
* @param array $stack
* @return string String of query data with placeholders replaced.
* @access public
* @todo Remove and refactor $assocData, ensure uses of the method have the param removed too.
*/
function insertQueryData($query, $data, $association, $assocData, &$model, &$linkModel, $stack) {
$keys = array('{$__cakeID__$}', '{$__cakeForeignKey__$}');
foreach ($keys as $key) {
$val = null;
if (strpos($query, $key) !== false) {
switch ($key) {
case '{$__cakeID__$}':
if (isset($data[$model->alias]) || isset($data[$association])) {
if (isset($data[$model->alias][$model->primaryKey])) {
$val = $data[$model->alias][$model->primaryKey];
} elseif (isset($data[$association][$model->primaryKey])) {
$val = $data[$association][$model->primaryKey];
}
} else {
$found = false;
foreach (array_reverse($stack) as $assoc) {
if (isset($data[$assoc]) && isset($data[$assoc][$model->primaryKey])) {
$val = $data[$assoc][$model->primaryKey];
$found = true;
break;
}
}
if (!$found) {
$val = '';
}
}
break;
case '{$__cakeForeignKey__$}':
foreach ($model->__associations as $id => $name) {
foreach ($model->$name as $assocName => $assoc) {
if ($assocName === $association) {
if (isset($assoc['foreignKey'])) {
$foreignKey = $assoc['foreignKey'];
if (isset($data[$model->alias][$foreignKey])) {
$val = $data[$model->alias][$foreignKey];
} elseif (isset($data[$association][$foreignKey])) {
$val = $data[$association][$foreignKey];
} else {
$found = false;
foreach (array_reverse($stack) as $assoc) {
if (isset($data[$assoc]) && isset($data[$assoc][$foreignKey])) {
$val = $data[$assoc][$foreignKey];
$found = true;
break;
}
}
if (!$found) {
$val = '';
}
}
}
break 3;
}
}
}
break;
}
if (empty($val) && $val !== '0') {
return false;
}
$query = str_replace($key, $this->value($val, $model->getColumnType($model->primaryKey)), $query);
}
}
return $query;
}
/**
* To-be-overridden in subclasses.
*
* @param Model $model Model instance
* @param string $key Key name to make
* @return string Key name for model.
* @access public
*/
function resolveKey(&$model, $key) {
return $model->alias . $key;
}
/**
* Closes the current datasource.
*
* @return void
* @access public
*/
function __destruct() {
if ($this->_transactionStarted) {
$null = null;
$this->rollback($null);
}
if ($this->connected) {
$this->close();
}
}
}

View File

@@ -0,0 +1,788 @@
<?php
/**
* MS SQL layer for DBO
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.model.datasources.dbo
* @since CakePHP(tm) v 0.10.5.1790
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* MS SQL layer for DBO
*
* Long description for class
*
* @package cake
* @subpackage cake.cake.libs.model.datasources.dbo
*/
class DboMssql extends DboSource {
/**
* Driver description
*
* @var string
*/
var $description = "MS SQL DBO Driver";
/**
* Starting quote character for quoted identifiers
*
* @var string
*/
var $startQuote = "[";
/**
* Ending quote character for quoted identifiers
*
* @var string
*/
var $endQuote = "]";
/**
* Creates a map between field aliases and numeric indexes. Workaround for the
* SQL Server driver's 30-character column name limitation.
*
* @var array
*/
var $__fieldMappings = array();
/**
* Base configuration settings for MS SQL driver
*
* @var array
*/
var $_baseConfig = array(
'persistent' => true,
'host' => 'localhost',
'login' => 'root',
'password' => '',
'database' => 'cake',
'port' => '1433',
);
/**
* MS SQL column definition
*
* @var array
*/
var $columns = array(
'primary_key' => array('name' => 'IDENTITY (1, 1) NOT NULL'),
'string' => array('name' => 'varchar', 'limit' => '255'),
'text' => array('name' => 'text'),
'integer' => array('name' => 'int', 'formatter' => 'intval'),
'float' => array('name' => 'numeric', 'formatter' => 'floatval'),
'datetime' => array('name' => 'datetime', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
'time' => array('name' => 'datetime', 'format' => 'H:i:s', 'formatter' => 'date'),
'date' => array('name' => 'datetime', 'format' => 'Y-m-d', 'formatter' => 'date'),
'binary' => array('name' => 'image'),
'boolean' => array('name' => 'bit')
);
/**
* Index of basic SQL commands
*
* @var array
* @access protected
*/
var $_commands = array(
'begin' => 'BEGIN TRANSACTION',
'commit' => 'COMMIT',
'rollback' => 'ROLLBACK'
);
/**
* Define if the last query had error
*
* @var string
* @access private
*/
var $__lastQueryHadError = false;
/**
* MS SQL DBO driver constructor; sets SQL Server error reporting defaults
*
* @param array $config Configuration data from app/config/databases.php
* @return boolean True if connected successfully, false on error
*/
function __construct($config, $autoConnect = true) {
if ($autoConnect) {
if (!function_exists('mssql_min_message_severity')) {
trigger_error(__("PHP SQL Server interface is not installed, cannot continue. For troubleshooting information, see http://php.net/mssql/", true), E_USER_WARNING);
}
mssql_min_message_severity(15);
mssql_min_error_severity(2);
}
return parent::__construct($config, $autoConnect);
}
/**
* Connects to the database using options in the given configuration array.
*
* @return boolean True if the database could be connected, else false
*/
function connect() {
$config = $this->config;
$os = env('OS');
if (!empty($os) && strpos($os, 'Windows') !== false) {
$sep = ',';
} else {
$sep = ':';
}
$this->connected = false;
if (is_numeric($config['port'])) {
$port = $sep . $config['port']; // Port number
} elseif ($config['port'] === null) {
$port = ''; // No port - SQL Server 2005
} else {
$port = '\\' . $config['port']; // Named pipe
}
if (!$config['persistent']) {
$this->connection = mssql_connect($config['host'] . $port, $config['login'], $config['password'], true);
} else {
$this->connection = mssql_pconnect($config['host'] . $port, $config['login'], $config['password']);
}
if (mssql_select_db($config['database'], $this->connection)) {
$this->_execute("SET DATEFORMAT ymd");
$this->connected = true;
}
return $this->connected;
}
/**
* Check that MsSQL is installed/loaded
*
* @return boolean
*/
function enabled() {
return extension_loaded('mssql');
}
/**
* Disconnects from database.
*
* @return boolean True if the database could be disconnected, else false
*/
function disconnect() {
@mssql_free_result($this->results);
$this->connected = !@mssql_close($this->connection);
return !$this->connected;
}
/**
* Executes given SQL statement.
*
* @param string $sql SQL statement
* @return resource Result resource identifier
* @access protected
*/
function _execute($sql) {
$result = @mssql_query($sql, $this->connection);
$this->__lastQueryHadError = ($result === false);
return $result;
}
/**
* Returns an array of sources (tables) in the database.
*
* @return array Array of tablenames in the database
*/
function listSources() {
$cache = parent::listSources();
if ($cache != null) {
return $cache;
}
$result = $this->fetchAll('SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES', false);
if (!$result || empty($result)) {
return array();
} else {
$tables = array();
foreach ($result as $table) {
$tables[] = $table[0]['TABLE_NAME'];
}
parent::listSources($tables);
return $tables;
}
}
/**
* Returns an array of the fields in given table name.
*
* @param Model $model Model object to describe
* @return array Fields in table. Keys are name and type
*/
function describe(&$model) {
$cache = parent::describe($model);
if ($cache != null) {
return $cache;
}
$table = $this->fullTableName($model, false);
$cols = $this->fetchAll("SELECT COLUMN_NAME as Field, DATA_TYPE as Type, COL_LENGTH('" . $table . "', COLUMN_NAME) as Length, IS_NULLABLE As [Null], COLUMN_DEFAULT as [Default], COLUMNPROPERTY(OBJECT_ID('" . $table . "'), COLUMN_NAME, 'IsIdentity') as [Key], NUMERIC_SCALE as Size FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '" . $table . "'", false);
$fields = false;
foreach ($cols as $column) {
$field = $column[0]['Field'];
$fields[$field] = array(
'type' => $this->column($column[0]['Type']),
'null' => (strtoupper($column[0]['Null']) == 'YES'),
'default' => preg_replace("/^[(]{1,2}'?([^')]*)?'?[)]{1,2}$/", "$1", $column[0]['Default']),
'length' => intval($column[0]['Length']),
'key' => ($column[0]['Key'] == '1') ? 'primary' : false
);
if ($fields[$field]['default'] === 'null') {
$fields[$field]['default'] = null;
} else {
$this->value($fields[$field]['default'], $fields[$field]['type']);
}
if ($fields[$field]['key'] && $fields[$field]['type'] == 'integer') {
$fields[$field]['length'] = 11;
} elseif (!$fields[$field]['key']) {
unset($fields[$field]['key']);
}
if (in_array($fields[$field]['type'], array('date', 'time', 'datetime', 'timestamp'))) {
$fields[$field]['length'] = null;
}
}
$this->__cacheDescription($this->fullTableName($model, false), $fields);
return $fields;
}
/**
* Returns a quoted and escaped string of $data for use in an SQL statement.
*
* @param string $data String to be prepared for use in an SQL statement
* @param string $column The column into which this data will be inserted
* @param boolean $safe Whether or not numeric data should be handled automagically if no column data is provided
* @return string Quoted and escaped data
*/
function value($data, $column = null, $safe = false) {
$parent = parent::value($data, $column, $safe);
if ($parent != null) {
return $parent;
}
if ($data === null) {
return 'NULL';
}
if (in_array($column, array('integer', 'float', 'binary')) && $data === '') {
return 'NULL';
}
if ($data === '') {
return "''";
}
switch ($column) {
case 'boolean':
$data = $this->boolean((bool)$data);
break;
default:
if (get_magic_quotes_gpc()) {
$data = stripslashes(str_replace("'", "''", $data));
} else {
$data = str_replace("'", "''", $data);
}
break;
}
if (in_array($column, array('integer', 'float', 'binary')) && is_numeric($data)) {
return $data;
}
return "'" . $data . "'";
}
/**
* Generates the fields list of an SQL query.
*
* @param Model $model
* @param string $alias Alias tablename
* @param mixed $fields
* @return array
*/
function fields(&$model, $alias = null, $fields = array(), $quote = true) {
if (empty($alias)) {
$alias = $model->alias;
}
$fields = parent::fields($model, $alias, $fields, false);
$count = count($fields);
if ($count >= 1 && strpos($fields[0], 'COUNT(*)') === false) {
$result = array();
for ($i = 0; $i < $count; $i++) {
$prepend = '';
if (strpos($fields[$i], 'DISTINCT') !== false) {
$prepend = 'DISTINCT ';
$fields[$i] = trim(str_replace('DISTINCT', '', $fields[$i]));
}
$fieldAlias = count($this->__fieldMappings);
if (!preg_match('/\s+AS\s+/i', $fields[$i])) {
if (substr($fields[$i], -1) == '*') {
if (strpos($fields[$i], '.') !== false && $fields[$i] != $alias . '.*') {
$build = explode('.', $fields[$i]);
$AssociatedModel = $model->{$build[0]};
} else {
$AssociatedModel = $model;
}
$_fields = $this->fields($AssociatedModel, $AssociatedModel->alias, array_keys($AssociatedModel->schema()));
$result = array_merge($result, $_fields);
continue;
}
if (strpos($fields[$i], '.') === false) {
$this->__fieldMappings[$alias . '__' . $fieldAlias] = $alias . '.' . $fields[$i];
$fieldName = $this->name($alias . '.' . $fields[$i]);
$fieldAlias = $this->name($alias . '__' . $fieldAlias);
} else {
$build = explode('.', $fields[$i]);
$this->__fieldMappings[$build[0] . '__' . $fieldAlias] = $fields[$i];
$fieldName = $this->name($build[0] . '.' . $build[1]);
$fieldAlias = $this->name(preg_replace("/^\[(.+)\]$/", "$1", $build[0]) . '__' . $fieldAlias);
}
if ($model->getColumnType($fields[$i]) == 'datetime') {
$fieldName = "CONVERT(VARCHAR(20), {$fieldName}, 20)";
}
$fields[$i] = "{$fieldName} AS {$fieldAlias}";
}
$result[] = $prepend . $fields[$i];
}
return $result;
} else {
return $fields;
}
}
/**
* Generates and executes an SQL INSERT statement for given model, fields, and values.
* Removes Identity (primary key) column from update data before returning to parent, if
* value is empty.
*
* @param Model $model
* @param array $fields
* @param array $values
* @param mixed $conditions
* @return array
*/
function create(&$model, $fields = null, $values = null) {
if (!empty($values)) {
$fields = array_combine($fields, $values);
}
$primaryKey = $this->_getPrimaryKey($model);
if (array_key_exists($primaryKey, $fields)) {
if (empty($fields[$primaryKey])) {
unset($fields[$primaryKey]);
} else {
$this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($model) . ' ON');
}
}
$result = parent::create($model, array_keys($fields), array_values($fields));
if (array_key_exists($primaryKey, $fields) && !empty($fields[$primaryKey])) {
$this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($model) . ' OFF');
}
return $result;
}
/**
* Generates and executes an SQL UPDATE statement for given model, fields, and values.
* Removes Identity (primary key) column from update data before returning to parent.
*
* @param Model $model
* @param array $fields
* @param array $values
* @param mixed $conditions
* @return array
*/
function update(&$model, $fields = array(), $values = null, $conditions = null) {
if (!empty($values)) {
$fields = array_combine($fields, $values);
}
if (isset($fields[$model->primaryKey])) {
unset($fields[$model->primaryKey]);
}
if (empty($fields)) {
return true;
}
return parent::update($model, array_keys($fields), array_values($fields), $conditions);
}
/**
* Returns a formatted error message from previous database operation.
*
* @return string Error message with error number
*/
function lastError() {
if ($this->__lastQueryHadError) {
$error = mssql_get_last_message();
if ($error && !preg_match('/contexto de la base de datos a|contesto di database|changed database|contexte de la base de don|datenbankkontext/i', $error)) {
return $error;
}
}
return null;
}
/**
* Returns number of affected rows in previous database operation. If no previous operation exists,
* this returns false.
*
* @return integer Number of affected rows
*/
function lastAffected() {
if ($this->_result) {
return mssql_rows_affected($this->connection);
}
return null;
}
/**
* Returns number of rows in previous resultset. If no previous resultset exists,
* this returns false.
*
* @return integer Number of rows in resultset
*/
function lastNumRows() {
if ($this->_result) {
return @mssql_num_rows($this->_result);
}
return null;
}
/**
* Returns the ID generated from the previous INSERT operation.
*
* @param unknown_type $source
* @return in
*/
function lastInsertId($source = null) {
$id = $this->fetchRow('SELECT SCOPE_IDENTITY() AS insertID', false);
return $id[0]['insertID'];
}
/**
* Returns a limit statement in the correct format for the particular database.
*
* @param integer $limit Limit of results returned
* @param integer $offset Offset from which to start results
* @return string SQL limit/offset statement
*/
function limit($limit, $offset = null) {
if ($limit) {
$rt = '';
if (!strpos(strtolower($limit), 'top') || strpos(strtolower($limit), 'top') === 0) {
$rt = ' TOP';
}
$rt .= ' ' . $limit;
if (is_int($offset) && $offset > 0) {
$rt .= ' OFFSET ' . $offset;
}
return $rt;
}
return null;
}
/**
* Converts database-layer column types to basic types
*
* @param string $real Real database-layer column type (i.e. "varchar(255)")
* @return string Abstract column type (i.e. "string")
*/
function column($real) {
if (is_array($real)) {
$col = $real['name'];
if (isset($real['limit'])) {
$col .= '(' . $real['limit'] . ')';
}
return $col;
}
$col = str_replace(')', '', $real);
$limit = null;
if (strpos($col, '(') !== false) {
list($col, $limit) = explode('(', $col);
}
if (in_array($col, array('date', 'time', 'datetime', 'timestamp'))) {
return $col;
}
if ($col == 'bit') {
return 'boolean';
}
if (strpos($col, 'int') !== false) {
return 'integer';
}
if (strpos($col, 'char') !== false) {
return 'string';
}
if (strpos($col, 'text') !== false) {
return 'text';
}
if (strpos($col, 'binary') !== false || $col == 'image') {
return 'binary';
}
if (in_array($col, array('float', 'real', 'decimal', 'numeric'))) {
return 'float';
}
return 'text';
}
/**
* Enter description here...
*
* @param unknown_type $results
*/
function resultSet(&$results) {
$this->results =& $results;
$this->map = array();
$numFields = mssql_num_fields($results);
$index = 0;
$j = 0;
while ($j < $numFields) {
$column = mssql_field_name($results, $j);
if (strpos($column, '__')) {
if (isset($this->__fieldMappings[$column]) && strpos($this->__fieldMappings[$column], '.')) {
$map = explode('.', $this->__fieldMappings[$column]);
} elseif (isset($this->__fieldMappings[$column])) {
$map = array(0, $this->__fieldMappings[$column]);
} else {
$map = array(0, $column);
}
$this->map[$index++] = $map;
} else {
$this->map[$index++] = array(0, $column);
}
$j++;
}
}
/**
* Builds final SQL statement
*
* @param string $type Query type
* @param array $data Query data
* @return string
*/
function renderStatement($type, $data) {
switch (strtolower($type)) {
case 'select':
extract($data);
$fields = trim($fields);
if (strpos($limit, 'TOP') !== false && strpos($fields, 'DISTINCT ') === 0) {
$limit = 'DISTINCT ' . trim($limit);
$fields = substr($fields, 9);
}
if (preg_match('/offset\s+([0-9]+)/i', $limit, $offset)) {
$limit = preg_replace('/\s*offset.*$/i', '', $limit);
preg_match('/top\s+([0-9]+)/i', $limit, $limitVal);
$offset = intval($offset[1]) + intval($limitVal[1]);
$rOrder = $this->__switchSort($order);
list($order2, $rOrder) = array($this->__mapFields($order), $this->__mapFields($rOrder));
return "SELECT * FROM (SELECT {$limit} * FROM (SELECT TOP {$offset} {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$group} {$order}) AS Set1 {$rOrder}) AS Set2 {$order2}";
} else {
return "SELECT {$limit} {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$group} {$order}";
}
break;
case "schema":
extract($data);
foreach ($indexes as $i => $index) {
if (preg_match('/PRIMARY KEY/', $index)) {
unset($indexes[$i]);
break;
}
}
foreach (array('columns', 'indexes') as $var) {
if (is_array(${$var})) {
${$var} = "\t" . implode(",\n\t", array_filter(${$var}));
}
}
return "CREATE TABLE {$table} (\n{$columns});\n{$indexes}";
break;
default:
return parent::renderStatement($type, $data);
break;
}
}
/**
* Reverses the sort direction of ORDER statements to get paging offsets to work correctly
*
* @param string $order
* @return string
* @access private
*/
function __switchSort($order) {
$order = preg_replace('/\s+ASC/i', '__tmp_asc__', $order);
$order = preg_replace('/\s+DESC/i', ' ASC', $order);
return preg_replace('/__tmp_asc__/', ' DESC', $order);
}
/**
* Translates field names used for filtering and sorting to shortened names using the field map
*
* @param string $sql A snippet of SQL representing an ORDER or WHERE statement
* @return string The value of $sql with field names replaced
* @access private
*/
function __mapFields($sql) {
if (empty($sql) || empty($this->__fieldMappings)) {
return $sql;
}
foreach ($this->__fieldMappings as $key => $val) {
$sql = preg_replace('/' . preg_quote($val) . '/', $this->name($key), $sql);
$sql = preg_replace('/' . preg_quote($this->name($val)) . '/', $this->name($key), $sql);
}
return $sql;
}
/**
* Returns an array of all result rows for a given SQL query.
* Returns false if no rows matched.
*
* @param string $sql SQL statement
* @param boolean $cache Enables returning/storing cached query results
* @return array Array of resultset rows, or false if no rows matched
*/
function read(&$model, $queryData = array(), $recursive = null) {
$results = parent::read($model, $queryData, $recursive);
$this->__fieldMappings = array();
return $results;
}
/**
* Fetches the next row from the current result set
*
* @return unknown
*/
function fetchResult() {
if ($row = mssql_fetch_row($this->results)) {
$resultRow = array();
$i = 0;
foreach ($row as $index => $field) {
list($table, $column) = $this->map[$index];
$resultRow[$table][$column] = $row[$index];
$i++;
}
return $resultRow;
} else {
return false;
}
}
/**
* Inserts multiple values into a table
*
* @param string $table
* @param string $fields
* @param array $values
* @access protected
*/
function insertMulti($table, $fields, $values) {
$primaryKey = $this->_getPrimaryKey($table);
$hasPrimaryKey = $primaryKey != null && (
(is_array($fields) && in_array($primaryKey, $fields)
|| (is_string($fields) && strpos($fields, $this->startQuote . $primaryKey . $this->endQuote) !== false))
);
if ($hasPrimaryKey) {
$this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($table) . ' ON');
}
parent::insertMulti($table, $fields, $values);
if ($hasPrimaryKey) {
$this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($table) . ' OFF');
}
}
/**
* Generate a database-native column schema string
*
* @param array $column An array structured like the following: array('name'=>'value', 'type'=>'value'[, options]),
* where options can be 'default', 'length', or 'key'.
* @return string
*/
function buildColumn($column) {
$result = preg_replace('/(int|integer)\([0-9]+\)/i', '$1', parent::buildColumn($column));
if (strpos($result, 'DEFAULT NULL') !== false) {
$result = str_replace('DEFAULT NULL', 'NULL', $result);
} else if (array_keys($column) == array('type', 'name')) {
$result .= ' NULL';
}
return $result;
}
/**
* Format indexes for create table
*
* @param array $indexes
* @param string $table
* @return string
*/
function buildIndex($indexes, $table = null) {
$join = array();
foreach ($indexes as $name => $value) {
if ($name == 'PRIMARY') {
$join[] = 'PRIMARY KEY (' . $this->name($value['column']) . ')';
} else if (isset($value['unique']) && $value['unique']) {
$out = "ALTER TABLE {$table} ADD CONSTRAINT {$name} UNIQUE";
if (is_array($value['column'])) {
$value['column'] = implode(', ', array_map(array(&$this, 'name'), $value['column']));
} else {
$value['column'] = $this->name($value['column']);
}
$out .= "({$value['column']});";
$join[] = $out;
}
}
return $join;
}
/**
* Makes sure it will return the primary key
*
* @param mixed $model
* @access protected
* @return string
*/
function _getPrimaryKey($model) {
if (is_object($model)) {
$schema = $model->schema();
} else {
$schema = $this->describe($model);
}
foreach ($schema as $field => $props) {
if (isset($props['key']) && $props['key'] == 'primary') {
return $field;
}
}
return null;
}
}

View File

@@ -0,0 +1,799 @@
<?php
/**
* MySQL layer for DBO
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.model.datasources.dbo
* @since CakePHP(tm) v 0.10.5.1790
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Provides common base for MySQL & MySQLi connections
*
* @package cake
* @subpackage cake.cake.libs.model.datasources.dbo
*/
class DboMysqlBase extends DboSource {
/**
* Description property.
*
* @var string
*/
var $description = "MySQL DBO Base Driver";
/**
* Start quote
*
* @var string
*/
var $startQuote = "`";
/**
* End quote
*
* @var string
*/
var $endQuote = "`";
/**
* use alias for update and delete. Set to true if version >= 4.1
*
* @var boolean
* @access protected
*/
var $_useAlias = true;
/**
* Index of basic SQL commands
*
* @var array
* @access protected
*/
var $_commands = array(
'begin' => 'START TRANSACTION',
'commit' => 'COMMIT',
'rollback' => 'ROLLBACK'
);
/**
* List of engine specific additional field parameters used on table creating
*
* @var array
* @access public
*/
var $fieldParameters = array(
'charset' => array('value' => 'CHARACTER SET', 'quote' => false, 'join' => ' ', 'column' => false, 'position' => 'beforeDefault'),
'collate' => array('value' => 'COLLATE', 'quote' => false, 'join' => ' ', 'column' => 'Collation', 'position' => 'beforeDefault'),
'comment' => array('value' => 'COMMENT', 'quote' => true, 'join' => ' ', 'column' => 'Comment', 'position' => 'afterDefault')
);
/**
* List of table engine specific parameters used on table creating
*
* @var array
* @access public
*/
var $tableParameters = array(
'charset' => array('value' => 'DEFAULT CHARSET', 'quote' => false, 'join' => '=', 'column' => 'charset'),
'collate' => array('value' => 'COLLATE', 'quote' => false, 'join' => '=', 'column' => 'Collation'),
'engine' => array('value' => 'ENGINE', 'quote' => false, 'join' => '=', 'column' => 'Engine')
);
/**
* MySQL column definition
*
* @var array
*/
var $columns = array(
'primary_key' => array('name' => 'NOT NULL AUTO_INCREMENT'),
'string' => array('name' => 'varchar', 'limit' => '255'),
'text' => array('name' => 'text'),
'integer' => array('name' => 'int', 'limit' => '11', 'formatter' => 'intval'),
'float' => array('name' => 'float', 'formatter' => 'floatval'),
'datetime' => array('name' => 'datetime', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
'time' => array('name' => 'time', 'format' => 'H:i:s', 'formatter' => 'date'),
'date' => array('name' => 'date', 'format' => 'Y-m-d', 'formatter' => 'date'),
'binary' => array('name' => 'blob'),
'boolean' => array('name' => 'tinyint', 'limit' => '1')
);
/**
* Returns an array of the fields in given table name.
*
* @param string $tableName Name of database table to inspect
* @return array Fields in table. Keys are name and type
*/
function describe(&$model) {
$cache = parent::describe($model);
if ($cache != null) {
return $cache;
}
$fields = false;
$cols = $this->query('SHOW FULL COLUMNS FROM ' . $this->fullTableName($model));
foreach ($cols as $column) {
$colKey = array_keys($column);
if (isset($column[$colKey[0]]) && !isset($column[0])) {
$column[0] = $column[$colKey[0]];
}
if (isset($column[0])) {
$fields[$column[0]['Field']] = array(
'type' => $this->column($column[0]['Type']),
'null' => ($column[0]['Null'] == 'YES' ? true : false),
'default' => $column[0]['Default'],
'length' => $this->length($column[0]['Type']),
);
if (!empty($column[0]['Key']) && isset($this->index[$column[0]['Key']])) {
$fields[$column[0]['Field']]['key'] = $this->index[$column[0]['Key']];
}
foreach ($this->fieldParameters as $name => $value) {
if (!empty($column[0][$value['column']])) {
$fields[$column[0]['Field']][$name] = $column[0][$value['column']];
}
}
if (isset($fields[$column[0]['Field']]['collate'])) {
$charset = $this->getCharsetName($fields[$column[0]['Field']]['collate']);
if ($charset) {
$fields[$column[0]['Field']]['charset'] = $charset;
}
}
}
}
$this->__cacheDescription($this->fullTableName($model, false), $fields);
return $fields;
}
/**
* Generates and executes an SQL UPDATE statement for given model, fields, and values.
*
* @param Model $model
* @param array $fields
* @param array $values
* @param mixed $conditions
* @return array
*/
function update(&$model, $fields = array(), $values = null, $conditions = null) {
if (!$this->_useAlias) {
return parent::update($model, $fields, $values, $conditions);
}
if ($values == null) {
$combined = $fields;
} else {
$combined = array_combine($fields, $values);
}
$alias = $joins = false;
$fields = $this->_prepareUpdateFields($model, $combined, empty($conditions), !empty($conditions));
$fields = implode(', ', $fields);
$table = $this->fullTableName($model);
if (!empty($conditions)) {
$alias = $this->name($model->alias);
if ($model->name == $model->alias) {
$joins = implode(' ', $this->_getJoins($model));
}
}
$conditions = $this->conditions($this->defaultConditions($model, $conditions, $alias), true, true, $model);
if ($conditions === false) {
return false;
}
if (!$this->execute($this->renderStatement('update', compact('table', 'alias', 'joins', 'fields', 'conditions')))) {
$model->onError();
return false;
}
return true;
}
/**
* Generates and executes an SQL DELETE statement for given id/conditions on given model.
*
* @param Model $model
* @param mixed $conditions
* @return boolean Success
*/
function delete(&$model, $conditions = null) {
if (!$this->_useAlias) {
return parent::delete($model, $conditions);
}
$alias = $this->name($model->alias);
$table = $this->fullTableName($model);
$joins = implode(' ', $this->_getJoins($model));
if (empty($conditions)) {
$alias = $joins = false;
}
$conditions = $this->conditions($this->defaultConditions($model, $conditions, $alias), true, true, $model);
if ($conditions === false) {
return false;
}
if ($this->execute($this->renderStatement('delete', compact('alias', 'table', 'joins', 'conditions'))) === false) {
$model->onError();
return false;
}
return true;
}
/**
* Sets the database encoding
*
* @param string $enc Database encoding
*/
function setEncoding($enc) {
return $this->_execute('SET NAMES ' . $enc) != false;
}
/**
* Returns an array of the indexes in given datasource name.
*
* @param string $model Name of model to inspect
* @return array Fields in table. Keys are column and unique
*/
function index($model) {
$index = array();
$table = $this->fullTableName($model);
if ($table) {
$indexes = $this->query('SHOW INDEX FROM ' . $table);
if (isset($indexes[0]['STATISTICS'])) {
$keys = Set::extract($indexes, '{n}.STATISTICS');
} else {
$keys = Set::extract($indexes, '{n}.0');
}
foreach ($keys as $i => $key) {
if (!isset($index[$key['Key_name']])) {
$col = array();
$index[$key['Key_name']]['column'] = $key['Column_name'];
$index[$key['Key_name']]['unique'] = intval($key['Non_unique'] == 0);
} else {
if (!is_array($index[$key['Key_name']]['column'])) {
$col[] = $index[$key['Key_name']]['column'];
}
$col[] = $key['Column_name'];
$index[$key['Key_name']]['column'] = $col;
}
}
}
return $index;
}
/**
* Generate a MySQL Alter Table syntax for the given Schema comparison
*
* @param array $compare Result of a CakeSchema::compare()
* @return array Array of alter statements to make.
*/
function alterSchema($compare, $table = null) {
if (!is_array($compare)) {
return false;
}
$out = '';
$colList = array();
foreach ($compare as $curTable => $types) {
$indexes = $tableParameters = $colList = array();
if (!$table || $table == $curTable) {
$out .= 'ALTER TABLE ' . $this->fullTableName($curTable) . " \n";
foreach ($types as $type => $column) {
if (isset($column['indexes'])) {
$indexes[$type] = $column['indexes'];
unset($column['indexes']);
}
if (isset($column['tableParameters'])) {
$tableParameters[$type] = $column['tableParameters'];
unset($column['tableParameters']);
}
switch ($type) {
case 'add':
foreach ($column as $field => $col) {
$col['name'] = $field;
$alter = 'ADD ' . $this->buildColumn($col);
if (isset($col['after'])) {
$alter .= ' AFTER ' . $this->name($col['after']);
}
$colList[] = $alter;
}
break;
case 'drop':
foreach ($column as $field => $col) {
$col['name'] = $field;
$colList[] = 'DROP ' . $this->name($field);
}
break;
case 'change':
foreach ($column as $field => $col) {
if (!isset($col['name'])) {
$col['name'] = $field;
}
$colList[] = 'CHANGE ' . $this->name($field) . ' ' . $this->buildColumn($col);
}
break;
}
}
$colList = array_merge($colList, $this->_alterIndexes($curTable, $indexes));
$colList = array_merge($colList, $this->_alterTableParameters($curTable, $tableParameters));
$out .= "\t" . join(",\n\t", $colList) . ";\n\n";
}
}
return $out;
}
/**
* Generate a MySQL "drop table" statement for the given Schema object
*
* @param object $schema An instance of a subclass of CakeSchema
* @param string $table Optional. If specified only the table name given will be generated.
* Otherwise, all tables defined in the schema are generated.
* @return string
*/
function dropSchema($schema, $table = null) {
if (!is_a($schema, 'CakeSchema')) {
trigger_error(__('Invalid schema object', true), E_USER_WARNING);
return null;
}
$out = '';
foreach ($schema->tables as $curTable => $columns) {
if (!$table || $table == $curTable) {
$out .= 'DROP TABLE IF EXISTS ' . $this->fullTableName($curTable) . ";\n";
}
}
return $out;
}
/**
* Generate MySQL table parameter alteration statementes for a table.
*
* @param string $table Table to alter parameters for.
* @param array $parameters Parameters to add & drop.
* @return array Array of table property alteration statementes.
* @todo Implement this method.
*/
function _alterTableParameters($table, $parameters) {
if (isset($parameters['change'])) {
return $this->buildTableParameters($parameters['change']);
}
return array();
}
/**
* Generate MySQL index alteration statements for a table.
*
* @param string $table Table to alter indexes for
* @param array $new Indexes to add and drop
* @return array Index alteration statements
*/
function _alterIndexes($table, $indexes) {
$alter = array();
if (isset($indexes['drop'])) {
foreach($indexes['drop'] as $name => $value) {
$out = 'DROP ';
if ($name == 'PRIMARY') {
$out .= 'PRIMARY KEY';
} else {
$out .= 'KEY ' . $name;
}
$alter[] = $out;
}
}
if (isset($indexes['add'])) {
foreach ($indexes['add'] as $name => $value) {
$out = 'ADD ';
if ($name == 'PRIMARY') {
$out .= 'PRIMARY ';
$name = null;
} else {
if (!empty($value['unique'])) {
$out .= 'UNIQUE ';
}
}
if (is_array($value['column'])) {
$out .= 'KEY '. $name .' (' . implode(', ', array_map(array(&$this, 'name'), $value['column'])) . ')';
} else {
$out .= 'KEY '. $name .' (' . $this->name($value['column']) . ')';
}
$alter[] = $out;
}
}
return $alter;
}
/**
* Inserts multiple values into a table
*
* @param string $table
* @param string $fields
* @param array $values
*/
function insertMulti($table, $fields, $values) {
$table = $this->fullTableName($table);
if (is_array($fields)) {
$fields = implode(', ', array_map(array(&$this, 'name'), $fields));
}
$values = implode(', ', $values);
$this->query("INSERT INTO {$table} ({$fields}) VALUES {$values}");
}
/**
* Returns an detailed array of sources (tables) in the database.
*
* @param string $name Table name to get parameters
* @return array Array of tablenames in the database
*/
function listDetailedSources($name = null) {
$condition = '';
if (is_string($name)) {
$condition = ' LIKE ' . $this->value($name);
}
$result = $this->query('SHOW TABLE STATUS FROM ' . $this->name($this->config['database']) . $condition . ';');
if (!$result) {
return array();
} else {
$tables = array();
foreach ($result as $row) {
$tables[$row['TABLES']['Name']] = $row['TABLES'];
if (!empty($row['TABLES']['Collation'])) {
$charset = $this->getCharsetName($row['TABLES']['Collation']);
if ($charset) {
$tables[$row['TABLES']['Name']]['charset'] = $charset;
}
}
}
if (is_string($name)) {
return $tables[$name];
}
return $tables;
}
}
/**
* Converts database-layer column types to basic types
*
* @param string $real Real database-layer column type (i.e. "varchar(255)")
* @return string Abstract column type (i.e. "string")
*/
function column($real) {
if (is_array($real)) {
$col = $real['name'];
if (isset($real['limit'])) {
$col .= '('.$real['limit'].')';
}
return $col;
}
$col = str_replace(')', '', $real);
$limit = $this->length($real);
if (strpos($col, '(') !== false) {
list($col, $vals) = explode('(', $col);
}
if (in_array($col, array('date', 'time', 'datetime', 'timestamp'))) {
return $col;
}
if (($col == 'tinyint' && $limit == 1) || $col == 'boolean') {
return 'boolean';
}
if (strpos($col, 'int') !== false) {
return 'integer';
}
if (strpos($col, 'char') !== false || $col == 'tinytext') {
return 'string';
}
if (strpos($col, 'text') !== false) {
return 'text';
}
if (strpos($col, 'blob') !== false || $col == 'binary') {
return 'binary';
}
if (strpos($col, 'float') !== false || strpos($col, 'double') !== false || strpos($col, 'decimal') !== false) {
return 'float';
}
if (strpos($col, 'enum') !== false) {
return "enum($vals)";
}
return 'text';
}
}
/**
* MySQL DBO driver object
*
* Provides connection and SQL generation for MySQL RDMS
*
* @package cake
* @subpackage cake.cake.libs.model.datasources.dbo
*/
class DboMysql extends DboMysqlBase {
/**
* Datasource description
*
* @var string
*/
var $description = "MySQL DBO Driver";
/**
* Base configuration settings for MySQL driver
*
* @var array
*/
var $_baseConfig = array(
'persistent' => true,
'host' => 'localhost',
'login' => 'root',
'password' => '',
'database' => 'cake',
'port' => '3306'
);
/**
* Connects to the database using options in the given configuration array.
*
* @return boolean True if the database could be connected, else false
*/
function connect() {
$config = $this->config;
$this->connected = false;
if (!$config['persistent']) {
$this->connection = mysql_connect($config['host'] . ':' . $config['port'], $config['login'], $config['password'], true);
$config['connect'] = 'mysql_connect';
} else {
$this->connection = mysql_pconnect($config['host'] . ':' . $config['port'], $config['login'], $config['password']);
}
if (mysql_select_db($config['database'], $this->connection)) {
$this->connected = true;
}
if (!empty($config['encoding'])) {
$this->setEncoding($config['encoding']);
}
$this->_useAlias = (bool)version_compare(mysql_get_server_info($this->connection), "4.1", ">=");
return $this->connected;
}
/**
* Check whether the MySQL extension is installed/loaded
*
* @return boolean
*/
function enabled() {
return extension_loaded('mysql');
}
/**
* Disconnects from database.
*
* @return boolean True if the database could be disconnected, else false
*/
function disconnect() {
if (isset($this->results) && is_resource($this->results)) {
mysql_free_result($this->results);
}
$this->connected = !@mysql_close($this->connection);
return !$this->connected;
}
/**
* Executes given SQL statement.
*
* @param string $sql SQL statement
* @return resource Result resource identifier
* @access protected
*/
function _execute($sql) {
return mysql_query($sql, $this->connection);
}
/**
* Returns an array of sources (tables) in the database.
*
* @return array Array of tablenames in the database
*/
function listSources() {
$cache = parent::listSources();
if ($cache != null) {
return $cache;
}
$result = $this->_execute('SHOW TABLES FROM ' . $this->name($this->config['database']) . ';');
if (!$result) {
return array();
} else {
$tables = array();
while ($line = mysql_fetch_row($result)) {
$tables[] = $line[0];
}
parent::listSources($tables);
return $tables;
}
}
/**
* Returns a quoted and escaped string of $data for use in an SQL statement.
*
* @param string $data String to be prepared for use in an SQL statement
* @param string $column The column into which this data will be inserted
* @param boolean $safe Whether or not numeric data should be handled automagically if no column data is provided
* @return string Quoted and escaped data
*/
function value($data, $column = null, $safe = false) {
$parent = parent::value($data, $column, $safe);
if ($parent != null) {
return $parent;
}
if ($data === null || (is_array($data) && empty($data))) {
return 'NULL';
}
if ($data === '' && $column !== 'integer' && $column !== 'float' && $column !== 'boolean') {
return "''";
}
if (empty($column)) {
$column = $this->introspectType($data);
}
switch ($column) {
case 'boolean':
return $this->boolean((bool)$data);
break;
case 'integer':
case 'float':
if ($data === '') {
return 'NULL';
}
if (is_float($data)) {
return sprintf('%F', $data);
}
if ((is_int($data) || $data === '0') || (
is_numeric($data) && strpos($data, ',') === false &&
$data[0] != '0' && strpos($data, 'e') === false)
) {
return $data;
}
default:
return "'" . mysql_real_escape_string($data, $this->connection) . "'";
break;
}
}
/**
* Returns a formatted error message from previous database operation.
*
* @return string Error message with error number
*/
function lastError() {
if (mysql_errno($this->connection)) {
return mysql_errno($this->connection).': '.mysql_error($this->connection);
}
return null;
}
/**
* Returns number of affected rows in previous database operation. If no previous operation exists,
* this returns false.
*
* @return integer Number of affected rows
*/
function lastAffected() {
if ($this->_result) {
return mysql_affected_rows($this->connection);
}
return null;
}
/**
* Returns number of rows in previous resultset. If no previous resultset exists,
* this returns false.
*
* @return integer Number of rows in resultset
*/
function lastNumRows() {
if ($this->hasResult()) {
return mysql_num_rows($this->_result);
}
return null;
}
/**
* Returns the ID generated from the previous INSERT operation.
*
* @param unknown_type $source
* @return in
*/
function lastInsertId($source = null) {
$id = $this->fetchRow('SELECT LAST_INSERT_ID() AS insertID', false);
if ($id !== false && !empty($id) && !empty($id[0]) && isset($id[0]['insertID'])) {
return $id[0]['insertID'];
}
return null;
}
/**
* Enter description here...
*
* @param unknown_type $results
*/
function resultSet(&$results) {
if (isset($this->results) && is_resource($this->results) && $this->results != $results) {
mysql_free_result($this->results);
}
$this->results =& $results;
$this->map = array();
$numFields = mysql_num_fields($results);
$index = 0;
$j = 0;
while ($j < $numFields) {
$column = mysql_fetch_field($results, $j);
if (!empty($column->table) && strpos($column->name, $this->virtualFieldSeparator) === false) {
$this->map[$index++] = array($column->table, $column->name);
} else {
$this->map[$index++] = array(0, $column->name);
}
$j++;
}
}
/**
* Fetches the next row from the current result set
*
* @return unknown
*/
function fetchResult() {
if ($row = mysql_fetch_row($this->results)) {
$resultRow = array();
$i = 0;
foreach ($row as $index => $field) {
list($table, $column) = $this->map[$index];
$resultRow[$table][$column] = $row[$index];
$i++;
}
return $resultRow;
} else {
return false;
}
}
/**
* Gets the database encoding
*
* @return string The database encoding
*/
function getEncoding() {
return mysql_client_encoding($this->connection);
}
/**
* Query charset by collation
*
* @param string $name Collation name
* @return string Character set name
*/
function getCharsetName($name) {
if ((bool)version_compare(mysql_get_server_info($this->connection), "5", ">=")) {
$cols = $this->query('SELECT CHARACTER_SET_NAME FROM INFORMATION_SCHEMA.COLLATIONS WHERE COLLATION_NAME= ' . $this->value($name) . ';');
if (isset($cols[0]['COLLATIONS']['CHARACTER_SET_NAME'])) {
return $cols[0]['COLLATIONS']['CHARACTER_SET_NAME'];
}
}
return false;
}
}

View File

@@ -0,0 +1,338 @@
<?php
/**
* MySQLi layer for DBO
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.model.datasources.dbo
* @since CakePHP(tm) v 1.1.4.2974
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Datasource', 'DboMysql');
/**
* MySQLi DBO driver object
*
* Provides connection and SQL generation for MySQL RDMS using PHP's MySQLi Interface
*
* @package cake
* @subpackage cake.cake.libs.model.datasources.dbo
*/
class DboMysqli extends DboMysqlBase {
/**
* Datasource Description
*
* @var string
*/
var $description = "Mysqli DBO Driver";
/**
* Base configuration settings for Mysqli driver
*
* @var array
*/
var $_baseConfig = array(
'persistent' => true,
'host' => 'localhost',
'login' => 'root',
'password' => '',
'database' => 'cake',
'port' => '3306'
);
/**
* Connects to the database using options in the given configuration array.
*
* @return boolean True if the database could be connected, else false
*/
function connect() {
$config = $this->config;
$this->connected = false;
if (is_numeric($config['port'])) {
$config['socket'] = null;
} else {
$config['socket'] = $config['port'];
$config['port'] = null;
}
$this->connection = mysqli_connect($config['host'], $config['login'], $config['password'], $config['database'], $config['port'], $config['socket']);
if ($this->connection !== false) {
$this->connected = true;
}
$this->_useAlias = (bool)version_compare(mysqli_get_server_info($this->connection), "4.1", ">=");
if (!empty($config['encoding'])) {
$this->setEncoding($config['encoding']);
}
return $this->connected;
}
/**
* Check that MySQLi is installed/enabled
*
* @return boolean
*/
function enabled() {
return extension_loaded('mysqli');
}
/**
* Disconnects from database.
*
* @return boolean True if the database could be disconnected, else false
*/
function disconnect() {
if (isset($this->results) && is_resource($this->results)) {
mysqli_free_result($this->results);
}
$this->connected = !@mysqli_close($this->connection);
return !$this->connected;
}
/**
* Executes given SQL statement.
*
* @param string $sql SQL statement
* @return resource Result resource identifier
* @access protected
*/
function _execute($sql) {
if (preg_match('/^\s*call/i', $sql)) {
return $this->_executeProcedure($sql);
}
return mysqli_query($this->connection, $sql);
}
/**
* Executes given SQL statement (procedure call).
*
* @param string $sql SQL statement (procedure call)
* @return resource Result resource identifier for first recordset
* @access protected
*/
function _executeProcedure($sql) {
$answer = mysqli_multi_query($this->connection, $sql);
$firstResult = mysqli_store_result($this->connection);
if (mysqli_more_results($this->connection)) {
while ($lastResult = mysqli_next_result($this->connection));
}
return $firstResult;
}
/**
* Returns an array of sources (tables) in the database.
*
* @return array Array of tablenames in the database
*/
function listSources() {
$cache = parent::listSources();
if ($cache !== null) {
return $cache;
}
$result = $this->_execute('SHOW TABLES FROM ' . $this->name($this->config['database']) . ';');
if (!$result) {
return array();
}
$tables = array();
while ($line = mysqli_fetch_row($result)) {
$tables[] = $line[0];
}
parent::listSources($tables);
return $tables;
}
/**
* Returns a quoted and escaped string of $data for use in an SQL statement.
*
* @param string $data String to be prepared for use in an SQL statement
* @param string $column The column into which this data will be inserted
* @param boolean $safe Whether or not numeric data should be handled automagically if no column data is provided
* @return string Quoted and escaped data
*/
function value($data, $column = null, $safe = false) {
$parent = parent::value($data, $column, $safe);
if ($parent != null) {
return $parent;
}
if ($data === null || (is_array($data) && empty($data))) {
return 'NULL';
}
if ($data === '' && $column !== 'integer' && $column !== 'float' && $column !== 'boolean') {
return "''";
}
if (empty($column)) {
$column = $this->introspectType($data);
}
switch ($column) {
case 'boolean':
return $this->boolean((bool)$data);
break;
case 'integer' :
case 'float' :
case null :
if ($data === '') {
return 'NULL';
}
if ((is_int($data) || is_float($data) || $data === '0') || (
is_numeric($data) && strpos($data, ',') === false &&
$data[0] != '0' && strpos($data, 'e') === false)) {
return $data;
}
default:
$data = "'" . mysqli_real_escape_string($this->connection, $data) . "'";
break;
}
return $data;
}
/**
* Returns a formatted error message from previous database operation.
*
* @return string Error message with error number
*/
function lastError() {
if (mysqli_errno($this->connection)) {
return mysqli_errno($this->connection).': '.mysqli_error($this->connection);
}
return null;
}
/**
* Returns number of affected rows in previous database operation. If no previous operation exists,
* this returns false.
*
* @return integer Number of affected rows
*/
function lastAffected() {
if ($this->_result) {
return mysqli_affected_rows($this->connection);
}
return null;
}
/**
* Returns number of rows in previous resultset. If no previous resultset exists,
* this returns false.
*
* @return integer Number of rows in resultset
*/
function lastNumRows() {
if ($this->hasResult()) {
return mysqli_num_rows($this->_result);
}
return null;
}
/**
* Returns the ID generated from the previous INSERT operation.
*
* @param unknown_type $source
* @return in
*/
function lastInsertId($source = null) {
$id = $this->fetchRow('SELECT LAST_INSERT_ID() AS insertID', false);
if ($id !== false && !empty($id) && !empty($id[0]) && isset($id[0]['insertID'])) {
return $id[0]['insertID'];
}
return null;
}
/**
* Enter description here...
*
* @param unknown_type $results
*/
function resultSet(&$results) {
if (isset($this->results) && is_resource($this->results) && $this->results != $results) {
mysqli_free_result($this->results);
}
$this->results =& $results;
$this->map = array();
$numFields = mysqli_num_fields($results);
$index = 0;
$j = 0;
while ($j < $numFields) {
$column = mysqli_fetch_field_direct($results, $j);
if (!empty($column->table)) {
$this->map[$index++] = array($column->table, $column->name);
} else {
$this->map[$index++] = array(0, $column->name);
}
$j++;
}
}
/**
* Fetches the next row from the current result set
*
* @return unknown
*/
function fetchResult() {
if ($row = mysqli_fetch_row($this->results)) {
$resultRow = array();
foreach ($row as $index => $field) {
$table = $column = null;
if (count($this->map[$index]) === 2) {
list($table, $column) = $this->map[$index];
}
$resultRow[$table][$column] = $row[$index];
}
return $resultRow;
}
return false;
}
/**
* Gets the database encoding
*
* @return string The database encoding
*/
function getEncoding() {
return mysqli_client_encoding($this->connection);
}
/**
* Query charset by collation
*
* @param string $name Collation name
* @return string Character set name
*/
function getCharsetName($name) {
if ((bool)version_compare(mysqli_get_server_info($this->connection), "5", ">=")) {
$cols = $this->query('SELECT CHARACTER_SET_NAME FROM INFORMATION_SCHEMA.COLLATIONS WHERE COLLATION_NAME= ' . $this->value($name) . ';');
if (isset($cols[0]['COLLATIONS']['CHARACTER_SET_NAME'])) {
return $cols[0]['COLLATIONS']['CHARACTER_SET_NAME'];
}
}
return false;
}
/**
* Checks if the result is valid
*
* @return boolean True if the result is valid, else false
*/
function hasResult() {
return is_object($this->_result);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,977 @@
<?php
/**
* PostgreSQL layer for DBO.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.model.datasources.dbo
* @since CakePHP(tm) v 0.9.1.114
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* PostgreSQL layer for DBO.
*
* Long description for class
*
* @package cake
* @subpackage cake.cake.libs.model.datasources.dbo
*/
class DboPostgres extends DboSource {
/**
* Driver description
*
* @var string
* @access public
*/
var $description = "PostgreSQL DBO Driver";
/**
* Index of basic SQL commands
*
* @var array
* @access protected
*/
var $_commands = array(
'begin' => 'BEGIN',
'commit' => 'COMMIT',
'rollback' => 'ROLLBACK'
);
/**
* Base driver configuration settings. Merged with user settings.
*
* @var array
* @access protected
*/
var $_baseConfig = array(
'persistent' => true,
'host' => 'localhost',
'login' => 'root',
'password' => '',
'database' => 'cake',
'schema' => 'public',
'port' => 5432,
'encoding' => ''
);
var $columns = array(
'primary_key' => array('name' => 'serial NOT NULL'),
'string' => array('name' => 'varchar', 'limit' => '255'),
'text' => array('name' => 'text'),
'integer' => array('name' => 'integer', 'formatter' => 'intval'),
'float' => array('name' => 'float', 'formatter' => 'floatval'),
'datetime' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
'time' => array('name' => 'time', 'format' => 'H:i:s', 'formatter' => 'date'),
'date' => array('name' => 'date', 'format' => 'Y-m-d', 'formatter' => 'date'),
'binary' => array('name' => 'bytea'),
'boolean' => array('name' => 'boolean'),
'number' => array('name' => 'numeric'),
'inet' => array('name' => 'inet')
);
/**
* Starting Quote
*
* @var string
* @access public
*/
var $startQuote = '"';
/**
* Ending Quote
*
* @var string
* @access public
*/
var $endQuote = '"';
/**
* Contains mappings of custom auto-increment sequences, if a table uses a sequence name
* other than what is dictated by convention.
*
* @var array
*/
var $_sequenceMap = array();
/**
* Connects to the database using options in the given configuration array.
*
* @return True if successfully connected.
*/
function connect() {
$config = $this->config;
$conn = "host='{$config['host']}' port='{$config['port']}' dbname='{$config['database']}' ";
$conn .= "user='{$config['login']}' password='{$config['password']}'";
if (!$config['persistent']) {
$this->connection = pg_connect($conn, PGSQL_CONNECT_FORCE_NEW);
} else {
$this->connection = pg_pconnect($conn);
}
$this->connected = false;
if ($this->connection) {
$this->connected = true;
$this->_execute("SET search_path TO " . $config['schema']);
}
if (!empty($config['encoding'])) {
$this->setEncoding($config['encoding']);
}
return $this->connected;
}
/**
* Check if PostgreSQL is enabled/loaded
*
* @return boolean
*/
function enabled() {
return extension_loaded('pgsql');
}
/**
* Disconnects from database.
*
* @return boolean True if the database could be disconnected, else false
*/
function disconnect() {
if ($this->hasResult()) {
pg_free_result($this->_result);
}
if (is_resource($this->connection)) {
$this->connected = !pg_close($this->connection);
} else {
$this->connected = false;
}
return !$this->connected;
}
/**
* Executes given SQL statement.
*
* @param string $sql SQL statement
* @return resource Result resource identifier
*/
function _execute($sql) {
return pg_query($this->connection, $sql);
}
/**
* Returns an array of tables in the database. If there are no tables, an error is raised and the application exits.
*
* @return array Array of tablenames in the database
*/
function listSources() {
$cache = parent::listSources();
if ($cache != null) {
return $cache;
}
$schema = $this->config['schema'];
$sql = "SELECT table_name as name FROM INFORMATION_SCHEMA.tables WHERE table_schema = '{$schema}';";
$result = $this->fetchAll($sql, false);
if (!$result) {
return array();
} else {
$tables = array();
foreach ($result as $item) {
$tables[] = $item[0]['name'];
}
parent::listSources($tables);
return $tables;
}
}
/**
* Returns an array of the fields in given table name.
*
* @param string $tableName Name of database table to inspect
* @return array Fields in table. Keys are name and type
*/
function &describe(&$model) {
$fields = parent::describe($model);
$table = $this->fullTableName($model, false);
$this->_sequenceMap[$table] = array();
if ($fields === null) {
$cols = $this->fetchAll(
"SELECT DISTINCT column_name AS name, data_type AS type, is_nullable AS null,
column_default AS default, ordinal_position AS position, character_maximum_length AS char_length,
character_octet_length AS oct_length FROM information_schema.columns
WHERE table_name = " . $this->value($table) . " AND table_schema = " .
$this->value($this->config['schema'])." ORDER BY position",
false
);
foreach ($cols as $column) {
$colKey = array_keys($column);
if (isset($column[$colKey[0]]) && !isset($column[0])) {
$column[0] = $column[$colKey[0]];
}
if (isset($column[0])) {
$c = $column[0];
if (!empty($c['char_length'])) {
$length = intval($c['char_length']);
} elseif (!empty($c['oct_length'])) {
if ($c['type'] == 'character varying') {
$length = null;
$c['type'] = 'text';
} else {
$length = intval($c['oct_length']);
}
} else {
$length = $this->length($c['type']);
}
$fields[$c['name']] = array(
'type' => $this->column($c['type']),
'null' => ($c['null'] == 'NO' ? false : true),
'default' => preg_replace(
"/^'(.*)'$/",
"$1",
preg_replace('/::.*/', '', $c['default'])
),
'length' => $length
);
if ($c['name'] == $model->primaryKey) {
$fields[$c['name']]['key'] = 'primary';
if ($fields[$c['name']]['type'] !== 'string') {
$fields[$c['name']]['length'] = 11;
}
}
if (
$fields[$c['name']]['default'] == 'NULL' ||
preg_match('/nextval\([\'"]?([\w.]+)/', $c['default'], $seq)
) {
$fields[$c['name']]['default'] = null;
if (!empty($seq) && isset($seq[1])) {
$this->_sequenceMap[$table][$c['name']] = $seq[1];
}
}
}
}
$this->__cacheDescription($table, $fields);
}
if (isset($model->sequence)) {
$this->_sequenceMap[$table][$model->primaryKey] = $model->sequence;
}
return $fields;
}
/**
* Returns a quoted and escaped string of $data for use in an SQL statement.
*
* @param string $data String to be prepared for use in an SQL statement
* @param string $column The column into which this data will be inserted
* @param boolean $read Value to be used in READ or WRITE context
* @return string Quoted and escaped
* @todo Add logic that formats/escapes data based on column type
*/
function value($data, $column = null, $read = true) {
$parent = parent::value($data, $column);
if ($parent != null) {
return $parent;
}
if ($data === null || (is_array($data) && empty($data))) {
return 'NULL';
}
if (empty($column)) {
$column = $this->introspectType($data);
}
switch($column) {
case 'binary':
$data = pg_escape_bytea($data);
break;
case 'boolean':
if ($data === true || $data === 't' || $data === 'true') {
return 'TRUE';
} elseif ($data === false || $data === 'f' || $data === 'false') {
return 'FALSE';
}
return (!empty($data) ? 'TRUE' : 'FALSE');
break;
case 'float':
if (is_float($data)) {
$data = sprintf('%F', $data);
}
case 'inet':
case 'integer':
case 'date':
case 'datetime':
case 'timestamp':
case 'time':
if ($data === '') {
return $read ? 'NULL' : 'DEFAULT';
}
default:
$data = pg_escape_string($data);
break;
}
return "'" . $data . "'";
}
/**
* Returns a formatted error message from previous database operation.
*
* @return string Error message
*/
function lastError() {
$error = pg_last_error($this->connection);
return ($error) ? $error : null;
}
/**
* Returns number of affected rows in previous database operation. If no previous operation exists, this returns false.
*
* @return integer Number of affected rows
*/
function lastAffected() {
return ($this->_result) ? pg_affected_rows($this->_result) : false;
}
/**
* Returns number of rows in previous resultset. If no previous resultset exists,
* this returns false.
*
* @return integer Number of rows in resultset
*/
function lastNumRows() {
return ($this->_result) ? pg_num_rows($this->_result) : false;
}
/**
* Returns the ID generated from the previous INSERT operation.
*
* @param string $source Name of the database table
* @param string $field Name of the ID database field. Defaults to "id"
* @return integer
*/
function lastInsertId($source, $field = 'id') {
$seq = $this->getSequence($source, $field);
$data = $this->fetchRow("SELECT currval('{$seq}') as max");
return $data[0]['max'];
}
/**
* Gets the associated sequence for the given table/field
*
* @param mixed $table Either a full table name (with prefix) as a string, or a model object
* @param string $field Name of the ID database field. Defaults to "id"
* @return string The associated sequence name from the sequence map, defaults to "{$table}_{$field}_seq"
*/
function getSequence($table, $field = 'id') {
if (is_object($table)) {
$table = $this->fullTableName($table, false);
}
if (isset($this->_sequenceMap[$table]) && isset($this->_sequenceMap[$table][$field])) {
return $this->_sequenceMap[$table][$field];
} else {
return "{$table}_{$field}_seq";
}
}
/**
* Deletes all the records in a table and drops all associated auto-increment sequences
*
* @param mixed $table A string or model class representing the table to be truncated
* @param integer $reset If -1, sequences are dropped, if 0 (default), sequences are reset,
* and if 1, sequences are not modified
* @return boolean SQL TRUNCATE TABLE statement, false if not applicable.
* @access public
*/
function truncate($table, $reset = 0) {
if (parent::truncate($table)) {
$table = $this->fullTableName($table, false);
if (isset($this->_sequenceMap[$table]) && $reset !== 1) {
foreach ($this->_sequenceMap[$table] as $field => $sequence) {
if ($reset === 0) {
$this->execute("ALTER SEQUENCE \"{$sequence}\" RESTART WITH 1");
} elseif ($reset === -1) {
$this->execute("DROP SEQUENCE IF EXISTS \"{$sequence}\"");
}
}
}
return true;
}
return false;
}
/**
* Prepares field names to be quoted by parent
*
* @param string $data
* @return string SQL field
*/
function name($data) {
if (is_string($data)) {
$data = str_replace('"__"', '__', $data);
}
return parent::name($data);
}
/**
* Generates the fields list of an SQL query.
*
* @param Model $model
* @param string $alias Alias tablename
* @param mixed $fields
* @return array
*/
function fields(&$model, $alias = null, $fields = array(), $quote = true) {
if (empty($alias)) {
$alias = $model->alias;
}
$fields = parent::fields($model, $alias, $fields, false);
if (!$quote) {
return $fields;
}
$count = count($fields);
if ($count >= 1 && strpos($fields[0], 'COUNT(*)') === false) {
$result = array();
for ($i = 0; $i < $count; $i++) {
if (!preg_match('/^.+\\(.*\\)/', $fields[$i]) && !preg_match('/\s+AS\s+/', $fields[$i])) {
if (substr($fields[$i], -1) == '*') {
if (strpos($fields[$i], '.') !== false && $fields[$i] != $alias . '.*') {
$build = explode('.', $fields[$i]);
$AssociatedModel = $model->{$build[0]};
} else {
$AssociatedModel = $model;
}
$_fields = $this->fields($AssociatedModel, $AssociatedModel->alias, array_keys($AssociatedModel->schema()));
$result = array_merge($result, $_fields);
continue;
}
$prepend = '';
if (strpos($fields[$i], 'DISTINCT') !== false) {
$prepend = 'DISTINCT ';
$fields[$i] = trim(str_replace('DISTINCT', '', $fields[$i]));
}
if (strrpos($fields[$i], '.') === false) {
$fields[$i] = $prepend . $this->name($alias) . '.' . $this->name($fields[$i]) . ' AS ' . $this->name($alias . '__' . $fields[$i]);
} else {
$build = explode('.', $fields[$i]);
$fields[$i] = $prepend . $this->name($build[0]) . '.' . $this->name($build[1]) . ' AS ' . $this->name($build[0] . '__' . $build[1]);
}
} else {
$fields[$i] = preg_replace_callback('/\(([\s\.\w]+)\)/', array(&$this, '__quoteFunctionField'), $fields[$i]);
}
$result[] = $fields[$i];
}
return $result;
}
return $fields;
}
/**
* Auxiliary function to quote matched `(Model.fields)` from a preg_replace_callback call
*
* @param string matched string
* @return string quoted strig
* @access private
*/
function __quoteFunctionField($match) {
$prepend = '';
if (strpos($match[1], 'DISTINCT') !== false) {
$prepend = 'DISTINCT ';
$match[1] = trim(str_replace('DISTINCT', '', $match[1]));
}
if (strpos($match[1], '.') === false) {
$match[1] = $this->name($match[1]);
} else {
$parts = explode('.', $match[1]);
if (!Set::numeric($parts)) {
$match[1] = $this->name($match[1]);
}
}
return '(' . $prepend .$match[1] . ')';
}
/**
* Returns an array of the indexes in given datasource name.
*
* @param string $model Name of model to inspect
* @return array Fields in table. Keys are column and unique
*/
function index($model) {
$index = array();
$table = $this->fullTableName($model, false);
if ($table) {
$indexes = $this->query("SELECT c2.relname, i.indisprimary, i.indisunique, i.indisclustered, i.indisvalid, pg_catalog.pg_get_indexdef(i.indexrelid, 0, true) as statement, c2.reltablespace
FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i
WHERE c.oid = (
SELECT c.oid
FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname ~ '^(" . $table . ")$'
AND pg_catalog.pg_table_is_visible(c.oid)
AND n.nspname ~ '^(" . $this->config['schema'] . ")$'
)
AND c.oid = i.indrelid AND i.indexrelid = c2.oid
ORDER BY i.indisprimary DESC, i.indisunique DESC, c2.relname", false);
foreach ($indexes as $i => $info) {
$key = array_pop($info);
if ($key['indisprimary']) {
$key['relname'] = 'PRIMARY';
}
$col = array();
preg_match('/\(([^\)]+)\)/', $key['statement'], $indexColumns);
$parsedColumn = $indexColumns[1];
if (strpos($indexColumns[1], ',') !== false) {
$parsedColumn = explode(', ', $indexColumns[1]);
}
$index[$key['relname']]['unique'] = $key['indisunique'];
$index[$key['relname']]['column'] = $parsedColumn;
}
}
return $index;
}
/**
* Alter the Schema of a table.
*
* @param array $compare Results of CakeSchema::compare()
* @param string $table name of the table
* @access public
* @return array
*/
function alterSchema($compare, $table = null) {
if (!is_array($compare)) {
return false;
}
$out = '';
$colList = array();
foreach ($compare as $curTable => $types) {
$indexes = $colList = array();
if (!$table || $table == $curTable) {
$out .= 'ALTER TABLE ' . $this->fullTableName($curTable) . " \n";
foreach ($types as $type => $column) {
if (isset($column['indexes'])) {
$indexes[$type] = $column['indexes'];
unset($column['indexes']);
}
switch ($type) {
case 'add':
foreach ($column as $field => $col) {
$col['name'] = $field;
$alter = 'ADD COLUMN '.$this->buildColumn($col);
if (isset($col['after'])) {
$alter .= ' AFTER '. $this->name($col['after']);
}
$colList[] = $alter;
}
break;
case 'drop':
foreach ($column as $field => $col) {
$col['name'] = $field;
$colList[] = 'DROP COLUMN '.$this->name($field);
}
break;
case 'change':
foreach ($column as $field => $col) {
if (!isset($col['name'])) {
$col['name'] = $field;
}
$fieldName = $this->name($field);
$default = isset($col['default']) ? $col['default'] : null;
$nullable = isset($col['null']) ? $col['null'] : null;
unset($col['default'], $col['null']);
$colList[] = 'ALTER COLUMN '. $fieldName .' TYPE ' . str_replace($fieldName, '', $this->buildColumn($col));
if (isset($nullable)) {
$nullable = ($nullable) ? 'DROP NOT NULL' : 'SET NOT NULL';
$colList[] = 'ALTER COLUMN '. $fieldName .' ' . $nullable;
}
if (isset($default)) {
$colList[] = 'ALTER COLUMN '. $fieldName .' SET DEFAULT ' . $this->value($default, $col['type']);
} else {
$colList[] = 'ALTER COLUMN '. $fieldName .' DROP DEFAULT';
}
}
break;
}
}
if (isset($indexes['drop']['PRIMARY'])) {
$colList[] = 'DROP CONSTRAINT ' . $curTable . '_pkey';
}
if (isset($indexes['add']['PRIMARY'])) {
$cols = $indexes['add']['PRIMARY']['column'];
if (is_array($cols)) {
$cols = implode(', ', $cols);
}
$colList[] = 'ADD PRIMARY KEY (' . $cols . ')';
}
if (!empty($colList)) {
$out .= "\t" . implode(",\n\t", $colList) . ";\n\n";
} else {
$out = '';
}
$out .= implode(";\n\t", $this->_alterIndexes($curTable, $indexes));
}
}
return $out;
}
/**
* Generate PostgreSQL index alteration statements for a table.
*
* @param string $table Table to alter indexes for
* @param array $new Indexes to add and drop
* @return array Index alteration statements
*/
function _alterIndexes($table, $indexes) {
$alter = array();
if (isset($indexes['drop'])) {
foreach($indexes['drop'] as $name => $value) {
$out = 'DROP ';
if ($name == 'PRIMARY') {
continue;
} else {
$out .= 'INDEX ' . $name;
}
$alter[] = $out;
}
}
if (isset($indexes['add'])) {
foreach ($indexes['add'] as $name => $value) {
$out = 'CREATE ';
if ($name == 'PRIMARY') {
continue;
} else {
if (!empty($value['unique'])) {
$out .= 'UNIQUE ';
}
$out .= 'INDEX ';
}
if (is_array($value['column'])) {
$out .= $name . ' ON ' . $table . ' (' . implode(', ', array_map(array(&$this, 'name'), $value['column'])) . ')';
} else {
$out .= $name . ' ON ' . $table . ' (' . $this->name($value['column']) . ')';
}
$alter[] = $out;
}
}
return $alter;
}
/**
* Returns a limit statement in the correct format for the particular database.
*
* @param integer $limit Limit of results returned
* @param integer $offset Offset from which to start results
* @return string SQL limit/offset statement
*/
function limit($limit, $offset = null) {
if ($limit) {
$rt = '';
if (!strpos(strtolower($limit), 'limit') || strpos(strtolower($limit), 'limit') === 0) {
$rt = ' LIMIT';
}
$rt .= ' ' . $limit;
if ($offset) {
$rt .= ' OFFSET ' . $offset;
}
return $rt;
}
return null;
}
/**
* Converts database-layer column types to basic types
*
* @param string $real Real database-layer column type (i.e. "varchar(255)")
* @return string Abstract column type (i.e. "string")
*/
function column($real) {
if (is_array($real)) {
$col = $real['name'];
if (isset($real['limit'])) {
$col .= '(' . $real['limit'] . ')';
}
return $col;
}
$col = str_replace(')', '', $real);
$limit = null;
if (strpos($col, '(') !== false) {
list($col, $limit) = explode('(', $col);
}
$floats = array(
'float', 'float4', 'float8', 'double', 'double precision', 'decimal', 'real', 'numeric'
);
switch (true) {
case (in_array($col, array('date', 'time', 'inet', 'boolean'))):
return $col;
case (strpos($col, 'timestamp') !== false):
return 'datetime';
case (strpos($col, 'time') === 0):
return 'time';
case (strpos($col, 'int') !== false && $col != 'interval'):
return 'integer';
case (strpos($col, 'char') !== false || $col == 'uuid'):
return 'string';
case (strpos($col, 'text') !== false):
return 'text';
case (strpos($col, 'bytea') !== false):
return 'binary';
case (in_array($col, $floats)):
return 'float';
default:
return 'text';
break;
}
}
/**
* Gets the length of a database-native column description, or null if no length
*
* @param string $real Real database-layer column type (i.e. "varchar(255)")
* @return int An integer representing the length of the column
*/
function length($real) {
$col = str_replace(array(')', 'unsigned'), '', $real);
$limit = null;
if (strpos($col, '(') !== false) {
list($col, $limit) = explode('(', $col);
}
if ($col == 'uuid') {
return 36;
}
if ($limit != null) {
return intval($limit);
}
return null;
}
/**
* Enter description here...
*
* @param unknown_type $results
*/
function resultSet(&$results) {
$this->results =& $results;
$this->map = array();
$num_fields = pg_num_fields($results);
$index = 0;
$j = 0;
while ($j < $num_fields) {
$columnName = pg_field_name($results, $j);
if (strpos($columnName, '__')) {
$parts = explode('__', $columnName);
$this->map[$index++] = array($parts[0], $parts[1]);
} else {
$this->map[$index++] = array(0, $columnName);
}
$j++;
}
}
/**
* Fetches the next row from the current result set
*
* @return unknown
*/
function fetchResult() {
if ($row = pg_fetch_row($this->results)) {
$resultRow = array();
foreach ($row as $index => $field) {
list($table, $column) = $this->map[$index];
$type = pg_field_type($this->results, $index);
switch ($type) {
case 'bool':
$resultRow[$table][$column] = $this->boolean($row[$index], false);
break;
case 'binary':
case 'bytea':
$resultRow[$table][$column] = pg_unescape_bytea($row[$index]);
break;
default:
$resultRow[$table][$column] = $row[$index];
break;
}
}
return $resultRow;
} else {
return false;
}
}
/**
* Translates between PHP boolean values and PostgreSQL boolean values
*
* @param mixed $data Value to be translated
* @param boolean $quote True to quote value, false otherwise
* @return mixed Converted boolean value
*/
function boolean($data, $quote = true) {
switch (true) {
case ($data === true || $data === false):
return $data;
case ($data === 't' || $data === 'f'):
return ($data === 't');
case ($data === 'true' || $data === 'false'):
return ($data === 'true');
case ($data === 'TRUE' || $data === 'FALSE'):
return ($data === 'TRUE');
default:
return (bool)$data;
break;
}
}
/**
* Sets the database encoding
*
* @param mixed $enc Database encoding
* @return boolean True on success, false on failure
*/
function setEncoding($enc) {
return pg_set_client_encoding($this->connection, $enc) == 0;
}
/**
* Gets the database encoding
*
* @return string The database encoding
*/
function getEncoding() {
return pg_client_encoding($this->connection);
}
/**
* Generate a Postgres-native column schema string
*
* @param array $column An array structured like the following:
* array('name'=>'value', 'type'=>'value'[, options]),
* where options can be 'default', 'length', or 'key'.
* @return string
*/
function buildColumn($column) {
$col = $this->columns[$column['type']];
if (!isset($col['length']) && !isset($col['limit'])) {
unset($column['length']);
}
$out = preg_replace('/integer\([0-9]+\)/', 'integer', parent::buildColumn($column));
$out = str_replace('integer serial', 'serial', $out);
if (strpos($out, 'timestamp DEFAULT')) {
if (isset($column['null']) && $column['null']) {
$out = str_replace('DEFAULT NULL', '', $out);
} else {
$out = str_replace('DEFAULT NOT NULL', '', $out);
}
}
if (strpos($out, 'DEFAULT DEFAULT')) {
if (isset($column['null']) && $column['null']) {
$out = str_replace('DEFAULT DEFAULT', 'DEFAULT NULL', $out);
} elseif (in_array($column['type'], array('integer', 'float'))) {
$out = str_replace('DEFAULT DEFAULT', 'DEFAULT 0', $out);
} elseif ($column['type'] == 'boolean') {
$out = str_replace('DEFAULT DEFAULT', 'DEFAULT FALSE', $out);
}
}
return $out;
}
/**
* Format indexes for create table
*
* @param array $indexes
* @param string $table
* @return string
*/
function buildIndex($indexes, $table = null) {
$join = array();
if (!is_array($indexes)) {
return array();
}
foreach ($indexes as $name => $value) {
if ($name == 'PRIMARY') {
$out = 'PRIMARY KEY (' . $this->name($value['column']) . ')';
} else {
$out = 'CREATE ';
if (!empty($value['unique'])) {
$out .= 'UNIQUE ';
}
if (is_array($value['column'])) {
$value['column'] = implode(', ', array_map(array(&$this, 'name'), $value['column']));
} else {
$value['column'] = $this->name($value['column']);
}
$out .= "INDEX {$name} ON {$table}({$value['column']});";
}
$join[] = $out;
}
return $join;
}
/**
* Overrides DboSource::renderStatement to handle schema generation with Postgres-style indexes
*
* @param string $type
* @param array $data
* @return string
*/
function renderStatement($type, $data) {
switch (strtolower($type)) {
case 'schema':
extract($data);
foreach ($indexes as $i => $index) {
if (preg_match('/PRIMARY KEY/', $index)) {
unset($indexes[$i]);
$columns[] = $index;
break;
}
}
$join = array('columns' => ",\n\t", 'indexes' => "\n");
foreach (array('columns', 'indexes') as $var) {
if (is_array(${$var})) {
${$var} = implode($join[$var], array_filter(${$var}));
}
}
return "CREATE TABLE {$table} (\n\t{$columns}\n);\n{$indexes}";
break;
default:
return parent::renderStatement($type, $data);
break;
}
}
}

View File

@@ -0,0 +1,623 @@
<?php
/**
* SQLite layer for DBO
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.model.datasources.dbo
* @since CakePHP(tm) v 0.9.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* DBO implementation for the SQLite DBMS.
*
* Long description for class
*
* @package cake
* @subpackage cake.cake.libs.model.datasources.dbo
*/
class DboSqlite extends DboSource {
/**
* Datasource Description
*
* @var string
*/
var $description = "SQLite DBO Driver";
/**
* Opening quote for quoted identifiers
*
* @var string
*/
var $startQuote = '"';
/**
* Closing quote for quoted identifiers
*
* @var string
*/
var $endQuote = '"';
/**
* Keeps the transaction statistics of CREATE/UPDATE/DELETE queries
*
* @var array
* @access protected
*/
var $_queryStats = array();
/**
* Base configuration settings for SQLite driver
*
* @var array
*/
var $_baseConfig = array(
'persistent' => true,
'database' => null
);
/**
* Index of basic SQL commands
*
* @var array
* @access protected
*/
var $_commands = array(
'begin' => 'BEGIN TRANSACTION',
'commit' => 'COMMIT TRANSACTION',
'rollback' => 'ROLLBACK TRANSACTION'
);
/**
* SQLite column definition
*
* @var array
*/
var $columns = array(
'primary_key' => array('name' => 'integer primary key'),
'string' => array('name' => 'varchar', 'limit' => '255'),
'text' => array('name' => 'text'),
'integer' => array('name' => 'integer', 'limit' => 11, 'formatter' => 'intval'),
'float' => array('name' => 'float', 'formatter' => 'floatval'),
'datetime' => array('name' => 'datetime', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
'time' => array('name' => 'time', 'format' => 'H:i:s', 'formatter' => 'date'),
'date' => array('name' => 'date', 'format' => 'Y-m-d', 'formatter' => 'date'),
'binary' => array('name' => 'blob'),
'boolean' => array('name' => 'boolean')
);
/**
* List of engine specific additional field parameters used on table creating
*
* @var array
* @access public
*/
var $fieldParameters = array(
'collate' => array(
'value' => 'COLLATE',
'quote' => false,
'join' => ' ',
'column' => 'Collate',
'position' => 'afterDefault',
'options' => array(
'BINARY', 'NOCASE', 'RTRIM'
)
),
);
/**
* Connects to the database using config['database'] as a filename.
*
* @param array $config Configuration array for connecting
* @return mixed
*/
function connect() {
$config = $this->config;
if (!$config['persistent']) {
$this->connection = sqlite_open($config['database']);
} else {
$this->connection = sqlite_popen($config['database']);
}
$this->connected = is_resource($this->connection);
if ($this->connected) {
$this->_execute('PRAGMA count_changes = 1;');
}
return $this->connected;
}
/**
* Check that SQLite is enabled/installed
*
* @return boolean
*/
function enabled() {
return extension_loaded('sqlite');
}
/**
* Disconnects from database.
*
* @return boolean True if the database could be disconnected, else false
*/
function disconnect() {
@sqlite_close($this->connection);
$this->connected = false;
return $this->connected;
}
/**
* Executes given SQL statement.
*
* @param string $sql SQL statement
* @return resource Result resource identifier
*/
function _execute($sql) {
$result = sqlite_query($this->connection, $sql);
if (preg_match('/^(INSERT|UPDATE|DELETE)/', $sql)) {
$this->resultSet($result);
list($this->_queryStats) = $this->fetchResult();
}
return $result;
}
/**
* Overrides DboSource::execute() to correctly handle query statistics
*
* @param string $sql
* @return unknown
*/
function execute($sql) {
$result = parent::execute($sql);
$this->_queryStats = array();
return $result;
}
/**
* Returns an array of tables in the database. If there are no tables, an error is raised and the application exits.
*
* @return array Array of tablenames in the database
*/
function listSources() {
$cache = parent::listSources();
if ($cache != null) {
return $cache;
}
$result = $this->fetchAll("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;", false);
if (empty($result)) {
return array();
} else {
$tables = array();
foreach ($result as $table) {
$tables[] = $table[0]['name'];
}
parent::listSources($tables);
return $tables;
}
return array();
}
/**
* Returns an array of the fields in given table name.
*
* @param string $tableName Name of database table to inspect
* @return array Fields in table. Keys are name and type
*/
function describe(&$model) {
$cache = parent::describe($model);
if ($cache != null) {
return $cache;
}
$fields = array();
$result = $this->fetchAll('PRAGMA table_info(' . $this->fullTableName($model) . ')');
foreach ($result as $column) {
$fields[$column[0]['name']] = array(
'type' => $this->column($column[0]['type']),
'null' => !$column[0]['notnull'],
'default' => $column[0]['dflt_value'],
'length' => $this->length($column[0]['type'])
);
if ($column[0]['pk'] == 1) {
$colLength = $this->length($column[0]['type']);
$fields[$column[0]['name']] = array(
'type' => $fields[$column[0]['name']]['type'],
'null' => false,
'default' => $column[0]['dflt_value'],
'key' => $this->index['PRI'],
'length'=> ($colLength != null) ? $colLength : 11
);
}
}
$this->__cacheDescription($model->tablePrefix . $model->table, $fields);
return $fields;
}
/**
* Returns a quoted and escaped string of $data for use in an SQL statement.
*
* @param string $data String to be prepared for use in an SQL statement
* @return string Quoted and escaped
*/
function value($data, $column = null, $safe = false) {
$parent = parent::value($data, $column, $safe);
if ($parent != null) {
return $parent;
}
if ($data === null) {
return 'NULL';
}
if ($data === '' && $column !== 'integer' && $column !== 'float' && $column !== 'boolean') {
return "''";
}
switch ($column) {
case 'boolean':
$data = $this->boolean((bool)$data);
break;
case 'integer':
case 'float':
if ($data === '') {
return 'NULL';
}
default:
$data = sqlite_escape_string($data);
break;
}
return "'" . $data . "'";
}
/**
* Generates and executes an SQL UPDATE statement for given model, fields, and values.
*
* @param Model $model
* @param array $fields
* @param array $values
* @param mixed $conditions
* @return array
*/
function update(&$model, $fields = array(), $values = null, $conditions = null) {
if (empty($values) && !empty($fields)) {
foreach ($fields as $field => $value) {
if (strpos($field, $model->alias . '.') !== false) {
unset($fields[$field]);
$field = str_replace($model->alias . '.', "", $field);
$field = str_replace($model->alias . '.', "", $field);
$fields[$field] = $value;
}
}
}
$result = parent::update($model, $fields, $values, $conditions);
return $result;
}
/**
* Deletes all the records in a table and resets the count of the auto-incrementing
* primary key, where applicable.
*
* @param mixed $table A string or model class representing the table to be truncated
* @return boolean SQL TRUNCATE TABLE statement, false if not applicable.
* @access public
*/
function truncate($table) {
return $this->execute('DELETE From ' . $this->fullTableName($table));
}
/**
* Returns a formatted error message from previous database operation.
*
* @return string Error message
*/
function lastError() {
$error = sqlite_last_error($this->connection);
if ($error) {
return $error.': '.sqlite_error_string($error);
}
return null;
}
/**
* Returns number of affected rows in previous database operation. If no previous operation exists, this returns false.
*
* @return integer Number of affected rows
*/
function lastAffected() {
if (!empty($this->_queryStats)) {
foreach (array('rows inserted', 'rows updated', 'rows deleted') as $key) {
if (array_key_exists($key, $this->_queryStats)) {
return $this->_queryStats[$key];
}
}
}
return false;
}
/**
* Returns number of rows in previous resultset. If no previous resultset exists,
* this returns false.
*
* @return integer Number of rows in resultset
*/
function lastNumRows() {
if ($this->hasResult()) {
sqlite_num_rows($this->_result);
}
return false;
}
/**
* Returns the ID generated from the previous INSERT operation.
*
* @return int
*/
function lastInsertId() {
return sqlite_last_insert_rowid($this->connection);
}
/**
* Converts database-layer column types to basic types
*
* @param string $real Real database-layer column type (i.e. "varchar(255)")
* @return string Abstract column type (i.e. "string")
*/
function column($real) {
if (is_array($real)) {
$col = $real['name'];
if (isset($real['limit'])) {
$col .= '('.$real['limit'].')';
}
return $col;
}
$col = strtolower(str_replace(')', '', $real));
$limit = null;
if (strpos($col, '(') !== false) {
list($col, $limit) = explode('(', $col);
}
if (in_array($col, array('text', 'integer', 'float', 'boolean', 'timestamp', 'date', 'datetime', 'time'))) {
return $col;
}
if (strpos($col, 'varchar') !== false) {
return 'string';
}
if (in_array($col, array('blob', 'clob'))) {
return 'binary';
}
if (strpos($col, 'numeric') !== false) {
return 'float';
}
return 'text';
}
/**
* Enter description here...
*
* @param unknown_type $results
*/
function resultSet(&$results) {
$this->results =& $results;
$this->map = array();
$fieldCount = sqlite_num_fields($results);
$index = $j = 0;
while ($j < $fieldCount) {
$columnName = str_replace('"', '', sqlite_field_name($results, $j));
if (strpos($columnName, '.')) {
$parts = explode('.', $columnName);
$this->map[$index++] = array($parts[0], $parts[1]);
} else {
$this->map[$index++] = array(0, $columnName);
}
$j++;
}
}
/**
* Fetches the next row from the current result set
*
* @return unknown
*/
function fetchResult() {
if ($row = sqlite_fetch_array($this->results, SQLITE_ASSOC)) {
$resultRow = array();
$i = 0;
foreach ($row as $index => $field) {
if (strpos($index, '.')) {
list($table, $column) = explode('.', str_replace('"', '', $index));
$resultRow[$table][$column] = $row[$index];
} else {
$resultRow[0][str_replace('"', '', $index)] = $row[$index];
}
$i++;
}
return $resultRow;
} else {
return false;
}
}
/**
* Returns a limit statement in the correct format for the particular database.
*
* @param integer $limit Limit of results returned
* @param integer $offset Offset from which to start results
* @return string SQL limit/offset statement
*/
function limit($limit, $offset = null) {
if ($limit) {
$rt = '';
if (!strpos(strtolower($limit), 'limit') || strpos(strtolower($limit), 'limit') === 0) {
$rt = ' LIMIT';
}
$rt .= ' ' . $limit;
if ($offset) {
$rt .= ' OFFSET ' . $offset;
}
return $rt;
}
return null;
}
/**
* Generate a database-native column schema string
*
* @param array $column An array structured like the following: array('name'=>'value', 'type'=>'value'[, options]),
* where options can be 'default', 'length', or 'key'.
* @return string
*/
function buildColumn($column) {
$name = $type = null;
$column = array_merge(array('null' => true), $column);
extract($column);
if (empty($name) || empty($type)) {
trigger_error(__('Column name or type not defined in schema', true), E_USER_WARNING);
return null;
}
if (!isset($this->columns[$type])) {
trigger_error(sprintf(__('Column type %s does not exist', true), $type), E_USER_WARNING);
return null;
}
$real = $this->columns[$type];
$out = $this->name($name) . ' ' . $real['name'];
if (isset($column['key']) && $column['key'] == 'primary' && $type == 'integer') {
return $this->name($name) . ' ' . $this->columns['primary_key']['name'];
}
return parent::buildColumn($column);
}
/**
* Sets the database encoding
*
* @param string $enc Database encoding
*/
function setEncoding($enc) {
if (!in_array($enc, array("UTF-8", "UTF-16", "UTF-16le", "UTF-16be"))) {
return false;
}
return $this->_execute("PRAGMA encoding = \"{$enc}\"") !== false;
}
/**
* Gets the database encoding
*
* @return string The database encoding
*/
function getEncoding() {
return $this->fetchRow('PRAGMA encoding');
}
/**
* Removes redundant primary key indexes, as they are handled in the column def of the key.
*
* @param array $indexes
* @param string $table
* @return string
*/
function buildIndex($indexes, $table = null) {
$join = array();
foreach ($indexes as $name => $value) {
if ($name == 'PRIMARY') {
continue;
}
$out = 'CREATE ';
if (!empty($value['unique'])) {
$out .= 'UNIQUE ';
}
if (is_array($value['column'])) {
$value['column'] = implode(', ', array_map(array(&$this, 'name'), $value['column']));
} else {
$value['column'] = $this->name($value['column']);
}
$out .= "INDEX {$name} ON {$table}({$value['column']});";
$join[] = $out;
}
return $join;
}
/**
* Overrides DboSource::index to handle SQLite indexe introspection
* Returns an array of the indexes in given table name.
*
* @param string $model Name of model to inspect
* @return array Fields in table. Keys are column and unique
*/
function index(&$model) {
$index = array();
$table = $this->fullTableName($model);
if ($table) {
$indexes = $this->query('PRAGMA index_list(' . $table . ')');
$tableInfo = $this->query('PRAGMA table_info(' . $table . ')');
foreach ($indexes as $i => $info) {
$key = array_pop($info);
$keyInfo = $this->query('PRAGMA index_info("' . $key['name'] . '")');
foreach ($keyInfo as $keyCol) {
if (!isset($index[$key['name']])) {
$col = array();
if (preg_match('/autoindex/', $key['name'])) {
$key['name'] = 'PRIMARY';
}
$index[$key['name']]['column'] = $keyCol[0]['name'];
$index[$key['name']]['unique'] = intval($key['unique'] == 1);
} else {
if (!is_array($index[$key['name']]['column'])) {
$col[] = $index[$key['name']]['column'];
}
$col[] = $keyCol[0]['name'];
$index[$key['name']]['column'] = $col;
}
}
}
}
return $index;
}
/**
* Overrides DboSource::renderStatement to handle schema generation with SQLite-style indexes
*
* @param string $type
* @param array $data
* @return string
*/
function renderStatement($type, $data) {
switch (strtolower($type)) {
case 'schema':
extract($data);
foreach (array('columns', 'indexes') as $var) {
if (is_array(${$var})) {
${$var} = "\t" . implode(",\n\t", array_filter(${$var}));
}
}
return "CREATE TABLE {$table} (\n{$columns});\n{$indexes}";
break;
default:
return parent::renderStatement($type, $data);
break;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,332 @@
<?php
/**
* This is core configuration file.
*
* Use it to configure core behaviour ofCake.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.model
* @since CakePHP(tm) v 0.2.9
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Load Model and AppModel
*/
App::import('Model', 'App');
/**
* ACL Node
*
*
* @package cake
* @subpackage cake.cake.libs.model
*/
class AclNode extends AppModel {
/**
* Explicitly disable in-memory query caching for ACL models
*
* @var boolean
* @access public
*/
var $cacheQueries = false;
/**
* ACL models use the Tree behavior
*
* @var array
* @access public
*/
var $actsAs = array('Tree' => 'nested');
/**
* Constructor
*
*/
function __construct() {
$config = Configure::read('Acl.database');
if (isset($config)) {
$this->useDbConfig = $config;
}
parent::__construct();
}
/**
* Retrieves the Aro/Aco node for this model
*
* @param mixed $ref Array with 'model' and 'foreign_key', model object, or string value
* @return array Node found in database
* @access public
*/
function node($ref = null) {
$db =& ConnectionManager::getDataSource($this->useDbConfig);
$type = $this->alias;
$result = null;
if (!empty($this->useTable)) {
$table = $this->useTable;
} else {
$table = Inflector::pluralize(Inflector::underscore($type));
}
if (empty($ref)) {
return null;
} elseif (is_string($ref)) {
$path = explode('/', $ref);
$start = $path[0];
unset($path[0]);
$queryData = array(
'conditions' => array(
$db->name("{$type}.lft") . ' <= ' . $db->name("{$type}0.lft"),
$db->name("{$type}.rght") . ' >= ' . $db->name("{$type}0.rght")),
'fields' => array('id', 'parent_id', 'model', 'foreign_key', 'alias'),
'joins' => array(array(
'table' => $db->fullTableName($this),
'alias' => "{$type}0",
'type' => 'LEFT',
'conditions' => array("{$type}0.alias" => $start)
)),
'order' => $db->name("{$type}.lft") . ' DESC'
);
foreach ($path as $i => $alias) {
$j = $i - 1;
$queryData['joins'][] = array(
'table' => $db->fullTableName($this),
'alias' => "{$type}{$i}",
'type' => 'LEFT',
'conditions' => array(
$db->name("{$type}{$i}.lft") . ' > ' . $db->name("{$type}{$j}.lft"),
$db->name("{$type}{$i}.rght") . ' < ' . $db->name("{$type}{$j}.rght"),
$db->name("{$type}{$i}.alias") . ' = ' . $db->value($alias, 'string'),
$db->name("{$type}{$j}.id") . ' = ' . $db->name("{$type}{$i}.parent_id")
)
);
$queryData['conditions'] = array('or' => array(
$db->name("{$type}.lft") . ' <= ' . $db->name("{$type}0.lft") . ' AND ' . $db->name("{$type}.rght") . ' >= ' . $db->name("{$type}0.rght"),
$db->name("{$type}.lft") . ' <= ' . $db->name("{$type}{$i}.lft") . ' AND ' . $db->name("{$type}.rght") . ' >= ' . $db->name("{$type}{$i}.rght"))
);
}
$result = $db->read($this, $queryData, -1);
$path = array_values($path);
if (
!isset($result[0][$type]) ||
(!empty($path) && $result[0][$type]['alias'] != $path[count($path) - 1]) ||
(empty($path) && $result[0][$type]['alias'] != $start)
) {
return false;
}
} elseif (is_object($ref) && is_a($ref, 'Model')) {
$ref = array('model' => $ref->alias, 'foreign_key' => $ref->id);
} elseif (is_array($ref) && !(isset($ref['model']) && isset($ref['foreign_key']))) {
$name = key($ref);
if (PHP5) {
$model = ClassRegistry::init(array('class' => $name, 'alias' => $name));
} else {
$model =& ClassRegistry::init(array('class' => $name, 'alias' => $name));
}
if (empty($model)) {
trigger_error(sprintf(__("Model class '%s' not found in AclNode::node() when trying to bind %s object", true), $type, $this->alias), E_USER_WARNING);
return null;
}
$tmpRef = null;
if (method_exists($model, 'bindNode')) {
$tmpRef = $model->bindNode($ref);
}
if (empty($tmpRef)) {
$ref = array('model' => $name, 'foreign_key' => $ref[$name][$model->primaryKey]);
} else {
if (is_string($tmpRef)) {
return $this->node($tmpRef);
}
$ref = $tmpRef;
}
}
if (is_array($ref)) {
if (is_array(current($ref)) && is_string(key($ref))) {
$name = key($ref);
$ref = current($ref);
}
foreach ($ref as $key => $val) {
if (strpos($key, $type) !== 0 && strpos($key, '.') === false) {
unset($ref[$key]);
$ref["{$type}0.{$key}"] = $val;
}
}
$queryData = array(
'conditions' => $ref,
'fields' => array('id', 'parent_id', 'model', 'foreign_key', 'alias'),
'joins' => array(array(
'table' => $db->fullTableName($this),
'alias' => "{$type}0",
'type' => 'LEFT',
'conditions' => array(
$db->name("{$type}.lft") . ' <= ' . $db->name("{$type}0.lft"),
$db->name("{$type}.rght") . ' >= ' . $db->name("{$type}0.rght")
)
)),
'order' => $db->name("{$type}.lft") . ' DESC'
);
$result = $db->read($this, $queryData, -1);
if (!$result) {
trigger_error(sprintf(__("AclNode::node() - Couldn't find %s node identified by \"%s\"", true), $type, print_r($ref, true)), E_USER_WARNING);
}
}
return $result;
}
}
/**
* Access Control Object
*
* @package cake
* @subpackage cake.cake.libs.model
*/
class Aco extends AclNode {
/**
* Model name
*
* @var string
* @access public
*/
var $name = 'Aco';
/**
* Binds to ARO nodes through permissions settings
*
* @var array
* @access public
*/
var $hasAndBelongsToMany = array('Aro' => array('with' => 'Permission'));
}
/**
* Action for Access Control Object
*
* @package cake
* @subpackage cake.cake.libs.model
*/
class AcoAction extends AppModel {
/**
* Model name
*
* @var string
* @access public
*/
var $name = 'AcoAction';
/**
* ACO Actions belong to ACOs
*
* @var array
* @access public
*/
var $belongsTo = array('Aco');
}
/**
* Access Request Object
*
* @package cake
* @subpackage cake.cake.libs.model
*/
class Aro extends AclNode {
/**
* Model name
*
* @var string
* @access public
*/
var $name = 'Aro';
/**
* AROs are linked to ACOs by means of Permission
*
* @var array
* @access public
*/
var $hasAndBelongsToMany = array('Aco' => array('with' => 'Permission'));
}
/**
* Permissions linking AROs with ACOs
*
* @package cake
* @subpackage cake.cake.libs.model
*/
class Permission extends AppModel {
/**
* Model name
*
* @var string
* @access public
*/
var $name = 'Permission';
/**
* Explicitly disable in-memory query caching
*
* @var boolean
* @access public
*/
var $cacheQueries = false;
/**
* Override default table name
*
* @var string
* @access public
*/
var $useTable = 'aros_acos';
/**
* Permissions link AROs with ACOs
*
* @var array
* @access public
*/
var $belongsTo = array('Aro', 'Aco');
/**
* No behaviors for this model
*
* @var array
* @access public
*/
var $actsAs = null;
/**
* Constructor, used to tell this model to use the
* database configured for ACL
*/
function __construct() {
$config = Configure::read('Acl.database');
if (!empty($config)) {
$this->useDbConfig = $config;
}
parent::__construct();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,533 @@
<?php
/**
* Model behaviors base class.
*
* Adds methods and automagic functionality to Cake Models.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.model
* @since CakePHP(tm) v 1.2.0.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Model behavior base class.
*
* Defines the Behavior interface, and contains common model interaction functionality.
*
* @package cake
* @subpackage cake.cake.libs.model
*/
class ModelBehavior extends Object {
/**
* Contains configuration settings for use with individual model objects. This
* is used because if multiple models use this Behavior, each will use the same
* object instance. Individual model settings should be stored as an
* associative array, keyed off of the model name.
*
* @var array
* @access public
* @see Model::$alias
*/
var $settings = array();
/**
* Allows the mapping of preg-compatible regular expressions to public or
* private methods in this class, where the array key is a /-delimited regular
* expression, and the value is a class method. Similar to the functionality of
* the findBy* / findAllBy* magic methods.
*
* @var array
* @access public
*/
var $mapMethods = array();
/**
* Setup this behavior with the specified configuration settings.
*
* @param object $model Model using this behavior
* @param array $config Configuration settings for $model
* @access public
*/
function setup(&$model, $config = array()) { }
/**
* Clean up any initialization this behavior has done on a model. Called when a behavior is dynamically
* detached from a model using Model::detach().
*
* @param object $model Model using this behavior
* @access public
* @see BehaviorCollection::detach()
*/
function cleanup(&$model) {
if (isset($this->settings[$model->alias])) {
unset($this->settings[$model->alias]);
}
}
/**
* Before find callback
*
* @param object $model Model using this behavior
* @param array $queryData Data used to execute this query, i.e. conditions, order, etc.
* @return mixed False if the operation should abort. An array will replace the value of $query.
* @access public
*/
function beforeFind(&$model, $query) { }
/**
* After find callback. Can be used to modify any results returned by find and findAll.
*
* @param object $model Model using this behavior
* @param mixed $results The results of the find operation
* @param boolean $primary Whether this model is being queried directly (vs. being queried as an association)
* @return mixed An array value will replace the value of $results - any other value will be ignored.
* @access public
*/
function afterFind(&$model, $results, $primary) { }
/**
* Before validate callback
*
* @param object $model Model using this behavior
* @return mixed False if the operation should abort. Any other result will continue.
* @access public
*/
function beforeValidate(&$model) { }
/**
* Before save callback
*
* @param object $model Model using this behavior
* @return mixed False if the operation should abort. Any other result will continue.
* @access public
*/
function beforeSave(&$model) { }
/**
* After save callback
*
* @param object $model Model using this behavior
* @param boolean $created True if this save created a new record
* @access public
*/
function afterSave(&$model, $created) { }
/**
* Before delete callback
*
* @param object $model Model using this behavior
* @param boolean $cascade If true records that depend on this record will also be deleted
* @return mixed False if the operation should abort. Any other result will continue.
* @access public
*/
function beforeDelete(&$model, $cascade = true) { }
/**
* After delete callback
*
* @param object $model Model using this behavior
* @access public
*/
function afterDelete(&$model) { }
/**
* DataSource error callback
*
* @param object $model Model using this behavior
* @param string $error Error generated in DataSource
* @access public
*/
function onError(&$model, $error) { }
/**
* Overrides Object::dispatchMethod to account for PHP4's broken reference support
*
* @see Object::dispatchMethod
* @access public
* @return mixed
*/
function dispatchMethod(&$model, $method, $params = array()) {
if (empty($params)) {
return $this->{$method}($model);
}
$params = array_values($params);
switch (count($params)) {
case 1:
return $this->{$method}($model, $params[0]);
case 2:
return $this->{$method}($model, $params[0], $params[1]);
case 3:
return $this->{$method}($model, $params[0], $params[1], $params[2]);
case 4:
return $this->{$method}($model, $params[0], $params[1], $params[2], $params[3]);
case 5:
return $this->{$method}($model, $params[0], $params[1], $params[2], $params[3], $params[4]);
default:
$params = array_merge(array(&$model), $params);
return call_user_func_array(array(&$this, $method), $params);
break;
}
}
/**
* If $model's whitelist property is non-empty, $field will be added to it.
* Note: this method should *only* be used in beforeValidate or beforeSave to ensure
* that it only modifies the whitelist for the current save operation. Also make sure
* you explicitly set the value of the field which you are allowing.
*
* @param object $model Model using this behavior
* @param string $field Field to be added to $model's whitelist
* @access protected
* @return void
*/
function _addToWhitelist(&$model, $field) {
if (is_array($field)) {
foreach ($field as $f) {
$this->_addToWhitelist($model, $f);
}
return;
}
if (!empty($model->whitelist) && !in_array($field, $model->whitelist)) {
$model->whitelist[] = $field;
}
}
}
/**
* Model behavior collection class.
*
* Defines the Behavior interface, and contains common model interaction functionality.
*
* @package cake
* @subpackage cake.cake.libs.model
*/
class BehaviorCollection extends Object {
/**
* Stores a reference to the attached name
*
* @var string
* @access public
*/
var $modelName = null;
/**
* Lists the currently-attached behavior objects
*
* @var array
* @access private
*/
var $_attached = array();
/**
* Lists the currently-attached behavior objects which are disabled
*
* @var array
* @access private
*/
var $_disabled = array();
/**
* Keeps a list of all methods of attached behaviors
*
* @var array
*/
var $__methods = array();
/**
* Keeps a list of all methods which have been mapped with regular expressions
*
* @var array
*/
var $__mappedMethods = array();
/**
* Attaches a model object and loads a list of behaviors
*
* @access public
* @return void
*/
function init($modelName, $behaviors = array()) {
$this->modelName = $modelName;
if (!empty($behaviors)) {
foreach (Set::normalize($behaviors) as $behavior => $config) {
$this->attach($behavior, $config);
}
}
}
/**
* Attaches a behavior to a model
*
* @param string $behavior CamelCased name of the behavior to load
* @param array $config Behavior configuration parameters
* @return boolean True on success, false on failure
* @access public
*/
function attach($behavior, $config = array()) {
list($plugin, $name) = pluginSplit($behavior);
$class = $name . 'Behavior';
if (!App::import('Behavior', $behavior)) {
$this->cakeError('missingBehaviorFile', array(array(
'behavior' => $behavior,
'file' => Inflector::underscore($behavior) . '.php',
'code' => 500,
'base' => '/'
)));
return false;
}
if (!class_exists($class)) {
$this->cakeError('missingBehaviorClass', array(array(
'behavior' => $class,
'file' => Inflector::underscore($class) . '.php',
'code' => 500,
'base' => '/'
)));
return false;
}
if (!isset($this->{$name})) {
if (ClassRegistry::isKeySet($class)) {
if (PHP5) {
$this->{$name} = ClassRegistry::getObject($class);
} else {
$this->{$name} =& ClassRegistry::getObject($class);
}
} else {
if (PHP5) {
$this->{$name} = new $class;
} else {
$this->{$name} =& new $class;
}
ClassRegistry::addObject($class, $this->{$name});
if (!empty($plugin)) {
ClassRegistry::addObject($plugin.'.'.$class, $this->{$name});
}
}
} elseif (isset($this->{$name}->settings) && isset($this->{$name}->settings[$this->modelName])) {
if ($config !== null && $config !== false) {
$config = array_merge($this->{$name}->settings[$this->modelName], $config);
} else {
$config = array();
}
}
if (empty($config)) {
$config = array();
}
$this->{$name}->setup(ClassRegistry::getObject($this->modelName), $config);
foreach ($this->{$name}->mapMethods as $method => $alias) {
$this->__mappedMethods[$method] = array($alias, $name);
}
$methods = get_class_methods($this->{$name});
$parentMethods = array_flip(get_class_methods('ModelBehavior'));
$callbacks = array(
'setup', 'cleanup', 'beforeFind', 'afterFind', 'beforeSave', 'afterSave',
'beforeDelete', 'afterDelete', 'afterError'
);
foreach ($methods as $m) {
if (!isset($parentMethods[$m])) {
$methodAllowed = (
$m[0] != '_' && !array_key_exists($m, $this->__methods) &&
!in_array($m, $callbacks)
);
if ($methodAllowed) {
$this->__methods[$m] = array($m, $name);
}
}
}
if (!in_array($name, $this->_attached)) {
$this->_attached[] = $name;
}
if (in_array($name, $this->_disabled) && !(isset($config['enabled']) && $config['enabled'] === false)) {
$this->enable($name);
} elseif (isset($config['enabled']) && $config['enabled'] === false) {
$this->disable($name);
}
return true;
}
/**
* Detaches a behavior from a model
*
* @param string $name CamelCased name of the behavior to unload
* @return void
* @access public
*/
function detach($name) {
list($plugin, $name) = pluginSplit($name);
if (isset($this->{$name})) {
$this->{$name}->cleanup(ClassRegistry::getObject($this->modelName));
unset($this->{$name});
}
foreach ($this->__methods as $m => $callback) {
if (is_array($callback) && $callback[1] == $name) {
unset($this->__methods[$m]);
}
}
$this->_attached = array_values(array_diff($this->_attached, (array)$name));
}
/**
* Enables callbacks on a behavior or array of behaviors
*
* @param mixed $name CamelCased name of the behavior(s) to enable (string or array)
* @return void
* @access public
*/
function enable($name) {
$this->_disabled = array_diff($this->_disabled, (array)$name);
}
/**
* Disables callbacks on a behavior or array of behaviors. Public behavior methods are still
* callable as normal.
*
* @param mixed $name CamelCased name of the behavior(s) to disable (string or array)
* @return void
* @access public
*/
function disable($name) {
foreach ((array)$name as $behavior) {
if (in_array($behavior, $this->_attached) && !in_array($behavior, $this->_disabled)) {
$this->_disabled[] = $behavior;
}
}
}
/**
* Gets the list of currently-enabled behaviors, or, the current status of a single behavior
*
* @param string $name Optional. The name of the behavior to check the status of. If omitted,
* returns an array of currently-enabled behaviors
* @return mixed If $name is specified, returns the boolean status of the corresponding behavior.
* Otherwise, returns an array of all enabled behaviors.
* @access public
*/
function enabled($name = null) {
if (!empty($name)) {
return (in_array($name, $this->_attached) && !in_array($name, $this->_disabled));
}
return array_diff($this->_attached, $this->_disabled);
}
/**
* Dispatches a behavior method
*
* @return array All methods for all behaviors attached to this object
* @access public
*/
function dispatchMethod(&$model, $method, $params = array(), $strict = false) {
$methods = array_keys($this->__methods);
foreach ($methods as $key => $value) {
$methods[$key] = strtolower($value);
}
$method = strtolower($method);
$check = array_flip($methods);
$found = isset($check[$method]);
$call = null;
if ($strict && !$found) {
trigger_error(sprintf(__("BehaviorCollection::dispatchMethod() - Method %s not found in any attached behavior", true), $method), E_USER_WARNING);
return null;
} elseif ($found) {
$methods = array_combine($methods, array_values($this->__methods));
$call = $methods[$method];
} else {
$count = count($this->__mappedMethods);
$mapped = array_keys($this->__mappedMethods);
for ($i = 0; $i < $count; $i++) {
if (preg_match($mapped[$i] . 'i', $method)) {
$call = $this->__mappedMethods[$mapped[$i]];
array_unshift($params, $method);
break;
}
}
}
if (!empty($call)) {
return $this->{$call[1]}->dispatchMethod($model, $call[0], $params);
}
return array('unhandled');
}
/**
* Dispatches a behavior callback on all attached behavior objects
*
* @param model $model
* @param string $callback
* @param array $params
* @param array $options
* @return mixed
* @access public
*/
function trigger(&$model, $callback, $params = array(), $options = array()) {
if (empty($this->_attached)) {
return true;
}
$options = array_merge(array('break' => false, 'breakOn' => array(null, false), 'modParams' => false), $options);
$count = count($this->_attached);
for ($i = 0; $i < $count; $i++) {
$name = $this->_attached[$i];
if (in_array($name, $this->_disabled)) {
continue;
}
$result = $this->{$name}->dispatchMethod($model, $callback, $params);
if ($options['break'] && ($result === $options['breakOn'] || (is_array($options['breakOn']) && in_array($result, $options['breakOn'], true)))) {
return $result;
} elseif ($options['modParams'] && is_array($result)) {
$params[0] = $result;
}
}
if ($options['modParams'] && isset($params[0])) {
return $params[0];
}
return true;
}
/**
* Gets the method list for attached behaviors, i.e. all public, non-callback methods
*
* @return array All public methods for all behaviors attached to this collection
* @access public
*/
function methods() {
return $this->__methods;
}
/**
* Gets the list of attached behaviors, or, whether the given behavior is attached
*
* @param string $name Optional. The name of the behavior to check the status of. If omitted,
* returns an array of currently-attached behaviors
* @return mixed If $name is specified, returns the boolean status of the corresponding behavior.
* Otherwise, returns an array of all attached behaviors.
* @access public
*/
function attached($name = null) {
if (!empty($name)) {
return (in_array($name, $this->_attached));
}
return $this->_attached;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,298 @@
<?php
/**
* Object class, allowing __construct and __destruct in PHP4.
*
* Also includes methods for logging and the special method RequestAction,
* to call other Controllers' Actions from anywhere.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs
* @since CakePHP(tm) v 0.2.9
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Object class, allowing __construct and __destruct in PHP4.
*
* Also includes methods for logging and the special method RequestAction,
* to call other Controllers' Actions from anywhere.
*
* @package cake
* @subpackage cake.cake.libs
*/
class Object {
/**
* A hack to support __construct() on PHP 4
* Hint: descendant classes have no PHP4 class_name() constructors,
* so this constructor gets called first and calls the top-layer __construct()
* which (if present) should call parent::__construct()
*
* @return Object
*/
function Object() {
$args = func_get_args();
if (method_exists($this, '__destruct')) {
register_shutdown_function (array(&$this, '__destruct'));
}
call_user_func_array(array(&$this, '__construct'), $args);
}
/**
* Class constructor, overridden in descendant classes.
*/
function __construct() {
}
/**
* Object-to-string conversion.
* Each class can override this method as necessary.
*
* @return string The name of this class
* @access public
*/
function toString() {
$class = get_class($this);
return $class;
}
/**
* Calls a controller's method from any location. Can be used to connect controllers together
* or tie plugins into a main application. requestAction can be used to return rendered views
* or fetch the return value from controller actions.
*
* @param mixed $url String or array-based url.
* @param array $extra if array includes the key "return" it sets the AutoRender to true.
* @return mixed Boolean true or false on success/failure, or contents
* of rendered action if 'return' is set in $extra.
* @access public
*/
function requestAction($url, $extra = array()) {
if (empty($url)) {
return false;
}
if (!class_exists('dispatcher')) {
require CAKE . 'dispatcher.php';
}
if (in_array('return', $extra, true)) {
$extra = array_merge($extra, array('return' => 0, 'autoRender' => 1));
}
if (is_array($url) && !isset($extra['url'])) {
$extra['url'] = array();
}
$params = array_merge(array('autoRender' => 0, 'return' => 1, 'bare' => 1, 'requested' => 1), $extra);
$dispatcher = new Dispatcher;
return $dispatcher->dispatch($url, $params);
}
/**
* Calls a method on this object with the given parameters. Provides an OO wrapper
* for `call_user_func_array`
*
* @param string $method Name of the method to call
* @param array $params Parameter list to use when calling $method
* @return mixed Returns the result of the method call
* @access public
*/
function dispatchMethod($method, $params = array()) {
switch (count($params)) {
case 0:
return $this->{$method}();
case 1:
return $this->{$method}($params[0]);
case 2:
return $this->{$method}($params[0], $params[1]);
case 3:
return $this->{$method}($params[0], $params[1], $params[2]);
case 4:
return $this->{$method}($params[0], $params[1], $params[2], $params[3]);
case 5:
return $this->{$method}($params[0], $params[1], $params[2], $params[3], $params[4]);
default:
return call_user_func_array(array(&$this, $method), $params);
break;
}
}
/**
* Stop execution of the current script. Wraps exit() making
* testing easier.
*
* @param $status see http://php.net/exit for values
* @return void
* @access public
*/
function _stop($status = 0) {
exit($status);
}
/**
* Convience method to write a message to CakeLog. See CakeLog::write()
* for more information on writing to logs.
*
* @param string $msg Log message
* @param integer $type Error type constant. Defined in app/config/core.php.
* @return boolean Success of log write
* @access public
*/
function log($msg, $type = LOG_ERROR) {
if (!class_exists('CakeLog')) {
require LIBS . 'cake_log.php';
}
if (!is_string($msg)) {
$msg = print_r($msg, true);
}
return CakeLog::write($type, $msg);
}
/**
* Allows setting of multiple properties of the object in a single line of code. Will only set
* properties that are part of a class declaration.
*
* @param array $properties An associative array containing properties and corresponding values.
* @return void
* @access protected
*/
function _set($properties = array()) {
if (is_array($properties) && !empty($properties)) {
$vars = get_object_vars($this);
foreach ($properties as $key => $val) {
if (array_key_exists($key, $vars)) {
$this->{$key} = $val;
}
}
}
}
/**
* Used to report user friendly errors.
* If there is a file app/error.php or app/app_error.php this file will be loaded
* error.php is the AppError class it should extend ErrorHandler class.
*
* @param string $method Method to be called in the error class (AppError or ErrorHandler classes)
* @param array $messages Message that is to be displayed by the error class
* @return error message
* @access public
*/
function cakeError($method, $messages = array()) {
if (!class_exists('ErrorHandler')) {
App::import('Core', 'Error');
if (file_exists(APP . 'error.php')) {
include_once (APP . 'error.php');
} elseif (file_exists(APP . 'app_error.php')) {
include_once (APP . 'app_error.php');
}
}
if (class_exists('AppError')) {
$error = new AppError($method, $messages);
} else {
$error = new ErrorHandler($method, $messages);
}
return $error;
}
/**
* Checks for a persistent class file, if found file is opened and true returned
* If file is not found a file is created and false returned
* If used in other locations of the model you should choose a unique name for the persistent file
* There are many uses for this method, see manual for examples
*
* @param string $name name of the class to persist
* @param string $object the object to persist
* @return boolean Success
* @access protected
* @todo add examples to manual
*/
function _persist($name, $return = null, &$object, $type = null) {
$file = CACHE . 'persistent' . DS . strtolower($name) . '.php';
if ($return === null) {
if (!file_exists($file)) {
return false;
} else {
return true;
}
}
if (!file_exists($file)) {
$this->_savePersistent($name, $object);
return false;
} else {
$this->__openPersistent($name, $type);
return true;
}
}
/**
* You should choose a unique name for the persistent file
*
* There are many uses for this method, see manual for examples
*
* @param string $name name used for object to cache
* @param object $object the object to persist
* @return boolean true on save, throws error if file can not be created
* @access protected
*/
function _savePersistent($name, &$object) {
$file = 'persistent' . DS . strtolower($name) . '.php';
$objectArray = array(&$object);
$data = str_replace('\\', '\\\\', serialize($objectArray));
$data = '<?php $' . $name . ' = \'' . str_replace('\'', '\\\'', $data) . '\' ?>';
$duration = '+999 days';
if (Configure::read() >= 1) {
$duration = '+10 seconds';
}
cache($file, $data, $duration);
}
/**
* Open the persistent class file for reading
* Used by Object::_persist()
*
* @param string $name Name of persisted class
* @param string $type Type of persistance (e.g: registry)
* @return void
* @access private
*/
function __openPersistent($name, $type = null) {
$file = CACHE . 'persistent' . DS . strtolower($name) . '.php';
include($file);
switch ($type) {
case 'registry':
$vars = unserialize(${$name});
foreach ($vars['0'] as $key => $value) {
if (strpos($key, '_behavior') !== false) {
App::import('Behavior', Inflector::classify(substr($key, 0, -9)));
} else {
App::import('Model', Inflector::camelize($key));
}
unset ($value);
}
unset($vars);
$vars = unserialize(${$name});
foreach ($vars['0'] as $key => $value) {
ClassRegistry::addObject($key, $value);
unset ($value);
}
unset($vars);
break;
default:
$vars = unserialize(${$name});
$this->{$name} = $vars['0'];
unset($vars);
break;
}
}
}

View File

@@ -0,0 +1,36 @@
<?php
/**
* Overload abstraction interface. Merges differences between PHP4 and 5.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs
* @since CakePHP(tm) v 1.2
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Overloadable class selector
*
* @package cake
* @subpackage cake.cake.libs
*/
/**
* Load the interface class based on the version of PHP.
*
*/
if (!PHP5) {
require(LIBS . 'overloadable_php4.php');
} else {
require(LIBS . 'overloadable_php5.php');
}

View File

@@ -0,0 +1,165 @@
<?php
/**
* Overload abstraction interface. Merges differences between PHP4 and 5.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs
* @since CakePHP(tm) v 1.2
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Overloadable class selector
*
* Load the interface class based on the version of PHP.
*
* @package cake
* @subpackage cake.cake.libs
*/
class Overloadable extends Object {
/**
* Constructor.
*
* @access private
*/
function __construct() {
$this->overload();
parent::__construct();
}
/**
* Overload implementation.
*
* @access public
*/
function overload() {
if (function_exists('overload')) {
if (func_num_args() > 0) {
foreach (func_get_args() as $class) {
if (is_object($class)) {
overload(get_class($class));
} elseif (is_string($class)) {
overload($class);
}
}
} else {
overload(get_class($this));
}
}
}
/**
* Magic method handler.
*
* @param string $method Method name
* @param array $params Parameters to send to method
* @param mixed $return Where to store return value from method
* @return boolean Success
* @access private
*/
function __call($method, $params, &$return) {
if (!method_exists($this, 'call__')) {
trigger_error(sprintf(__('Magic method handler call__ not defined in %s', true), get_class($this)), E_USER_ERROR);
}
$return = $this->call__($method, $params);
return true;
}
}
Overloadable::overload('Overloadable');
/**
* Overloadable2 class selector
*
* Load the interface class based on the version of PHP.
*
* @package cake
* @subpackage cake.cake.libs
*/
class Overloadable2 extends Object {
/**
* Constructor
*
* @access private
*/
function __construct() {
$this->overload();
parent::__construct();
}
/**
* Overload implementation.
*
* @access public
*/
function overload() {
if (function_exists('overload')) {
if (func_num_args() > 0) {
foreach (func_get_args() as $class) {
if (is_object($class)) {
overload(get_class($class));
} elseif (is_string($class)) {
overload($class);
}
}
} else {
overload(get_class($this));
}
}
}
/**
* Magic method handler.
*
* @param string $method Method name
* @param array $params Parameters to send to method
* @param mixed $return Where to store return value from method
* @return boolean Success
* @access private
*/
function __call($method, $params, &$return) {
if (!method_exists($this, 'call__')) {
trigger_error(sprintf(__('Magic method handler call__ not defined in %s', true), get_class($this)), E_USER_ERROR);
}
$return = $this->call__($method, $params);
return true;
}
/**
* Getter.
*
* @param mixed $name What to get
* @param mixed $value Where to store returned value
* @return boolean Success
* @access private
*/
function __get($name, &$value) {
$value = $this->get__($name);
return true;
}
/**
* Setter.
*
* @param mixed $name What to set
* @param mixed $value Value to set
* @return boolean Success
* @access private
*/
function __set($name, $value) {
$this->set__($name, $value);
return true;
}
}
Overloadable::overload('Overloadable2');

View File

@@ -0,0 +1,108 @@
<?php
/**
* Overload abstraction interface. Merges differences between PHP4 and 5.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs
* @since CakePHP(tm) v 1.2
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Overloadable class selector
*
* Load the interface class based on the version of PHP.
*
* @package cake
* @subpackage cake.cake.libs
*/
class Overloadable extends Object {
/**
* Overload implementation. No need for implementation in PHP5.
*
* @access public
*/
function overload() { }
/**
* Magic method handler.
*
* @param string $method Method name
* @param array $params Parameters to send to method
* @return mixed Return value from method
* @access private
*/
function __call($method, $params) {
if (!method_exists($this, 'call__')) {
trigger_error(sprintf(__('Magic method handler call__ not defined in %s', true), get_class($this)), E_USER_ERROR);
}
return $this->call__($method, $params);
}
}
/**
* Overloadable2 class selector
*
* Load the interface class based on the version of PHP.
*
* @package cake
*/
class Overloadable2 extends Object {
/**
* Overload implementation. No need for implementation in PHP5.
*
* @access public
*/
function overload() { }
/**
* Magic method handler.
*
* @param string $method Method name
* @param array $params Parameters to send to method
* @return mixed Return value from method
* @access private
*/
function __call($method, $params) {
if (!method_exists($this, 'call__')) {
trigger_error(sprintf(__('Magic method handler call__ not defined in %s', true), get_class($this)), E_USER_ERROR);
}
return $this->call__($method, $params);
}
/**
* Getter.
*
* @param mixed $name What to get
* @param mixed $value Where to store returned value
* @return boolean Success
* @access private
*/
function __get($name) {
return $this->get__($name);
}
/**
* Setter.
*
* @param mixed $name What to set
* @param mixed $value Value to set
* @return boolean Success
* @access private
*/
function __set($name, $value) {
return $this->set__($name, $value);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,348 @@
<?php
/**
* Washes strings from unwanted noise.
*
* Helpful methods to make unsafe strings usable.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs
* @since CakePHP(tm) v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Data Sanitization.
*
* Removal of alpahnumeric characters, SQL-safe slash-added strings, HTML-friendly strings,
* and all of the above on arrays.
*
* @package cake
* @subpackage cake.cake.libs
*/
class Sanitize {
/**
* Removes any non-alphanumeric characters.
*
* @param string $string String to sanitize
* @param array $allowed An array of additional characters that are not to be removed.
* @return string Sanitized string
* @access public
* @static
*/
function paranoid($string, $allowed = array()) {
$allow = null;
if (!empty($allowed)) {
foreach ($allowed as $value) {
$allow .= "\\$value";
}
}
if (is_array($string)) {
$cleaned = array();
foreach ($string as $key => $clean) {
$cleaned[$key] = preg_replace("/[^{$allow}a-zA-Z0-9]/", '', $clean);
}
} else {
$cleaned = preg_replace("/[^{$allow}a-zA-Z0-9]/", '', $string);
}
return $cleaned;
}
/**
* Makes a string SQL-safe.
*
* @param string $string String to sanitize
* @param string $connection Database connection being used
* @return string SQL safe string
* @access public
* @static
*/
function escape($string, $connection = 'default') {
$db =& ConnectionManager::getDataSource($connection);
if (is_numeric($string) || $string === null || is_bool($string)) {
return $string;
}
$string = substr($db->value($string), 1);
$string = substr($string, 0, -1);
return $string;
}
/**
* Returns given string safe for display as HTML. Renders entities.
*
* strip_tags() does not validating HTML syntax or structure, so it might strip whole passages
* with broken HTML.
*
* ### Options:
*
* - remove (boolean) if true strips all HTML tags before encoding
* - charset (string) the charset used to encode the string
* - quotes (int) see http://php.net/manual/en/function.htmlentities.php
*
* @param string $string String from where to strip tags
* @param array $options Array of options to use.
* @return string Sanitized string
* @access public
* @static
*/
function html($string, $options = array()) {
static $defaultCharset = false;
if ($defaultCharset === false) {
$defaultCharset = Configure::read('App.encoding');
if ($defaultCharset === null) {
$defaultCharset = 'UTF-8';
}
}
$default = array(
'remove' => false,
'charset' => $defaultCharset,
'quotes' => ENT_QUOTES
);
$options = array_merge($default, $options);
if ($options['remove']) {
$string = strip_tags($string);
}
return htmlentities($string, $options['quotes'], $options['charset']);
}
/**
* Strips extra whitespace from output
*
* @param string $str String to sanitize
* @return string whitespace sanitized string
* @access public
* @static
*/
function stripWhitespace($str) {
$r = preg_replace('/[\n\r\t]+/', '', $str);
return preg_replace('/\s{2,}/', ' ', $r);
}
/**
* Strips image tags from output
*
* @param string $str String to sanitize
* @return string Sting with images stripped.
* @access public
* @static
*/
function stripImages($str) {
$str = preg_replace('/(<a[^>]*>)(<img[^>]+alt=")([^"]*)("[^>]*>)(<\/a>)/i', '$1$3$5<br />', $str);
$str = preg_replace('/(<img[^>]+alt=")([^"]*)("[^>]*>)/i', '$2<br />', $str);
$str = preg_replace('/<img[^>]*>/i', '', $str);
return $str;
}
/**
* Strips scripts and stylesheets from output
*
* @param string $str String to sanitize
* @return string String with <script>, <style>, <link> elements removed.
* @access public
* @static
*/
function stripScripts($str) {
return preg_replace('/(<link[^>]+rel="[^"]*stylesheet"[^>]*>|<img[^>]*>|style="[^"]*")|<script[^>]*>.*?<\/script>|<style[^>]*>.*?<\/style>|<!--.*?-->/is', '', $str);
}
/**
* Strips extra whitespace, images, scripts and stylesheets from output
*
* @param string $str String to sanitize
* @return string sanitized string
* @access public
*/
function stripAll($str) {
$str = Sanitize::stripWhitespace($str);
$str = Sanitize::stripImages($str);
$str = Sanitize::stripScripts($str);
return $str;
}
/**
* Strips the specified tags from output. First parameter is string from
* where to remove tags. All subsequent parameters are tags.
*
* Ex.`$clean = Sanitize::stripTags($dirty, 'b', 'p', 'div');`
*
* Will remove all `<b>`, `<p>`, and `<div>` tags from the $dirty string.
*
* @param string $str String to sanitize
* @param string $tag Tag to remove (add more parameters as needed)
* @return string sanitized String
* @access public
* @static
*/
function stripTags() {
$params = params(func_get_args());
$str = $params[0];
for ($i = 1, $count = count($params); $i < $count; $i++) {
$str = preg_replace('/<' . $params[$i] . '\b[^>]*>/i', '', $str);
$str = preg_replace('/<\/' . $params[$i] . '[^>]*>/i', '', $str);
}
return $str;
}
/**
* Sanitizes given array or value for safe input. Use the options to specify
* the connection to use, and what filters should be applied (with a boolean
* value). Valid filters:
*
* - odd_spaces - removes any non space whitespace characters
* - encode - Encode any html entities. Encode must be true for the `remove_html` to work.
* - dollar - Escape `$` with `\$`
* - carriage - Remove `\r`
* - unicode -
* - escape - Should the string be SQL escaped.
* - backslash -
* - remove_html - Strip HTML with strip_tags. `encode` must be true for this option to work.
*
* @param mixed $data Data to sanitize
* @param mixed $options If string, DB connection being used, otherwise set of options
* @return mixed Sanitized data
* @access public
* @static
*/
function clean($data, $options = array()) {
if (empty($data)) {
return $data;
}
if (is_string($options)) {
$options = array('connection' => $options);
} else if (!is_array($options)) {
$options = array();
}
$options = array_merge(array(
'connection' => 'default',
'odd_spaces' => true,
'remove_html' => false,
'encode' => true,
'dollar' => true,
'carriage' => true,
'unicode' => true,
'escape' => true,
'backslash' => true
), $options);
if (is_array($data)) {
foreach ($data as $key => $val) {
$data[$key] = Sanitize::clean($val, $options);
}
return $data;
} else {
if ($options['odd_spaces']) {
$data = str_replace(chr(0xCA), '', str_replace(' ', ' ', $data));
}
if ($options['encode']) {
$data = Sanitize::html($data, array('remove' => $options['remove_html']));
}
if ($options['dollar']) {
$data = str_replace("\\\$", "$", $data);
}
if ($options['carriage']) {
$data = str_replace("\r", "", $data);
}
$data = str_replace("'", "'", str_replace("!", "!", $data));
if ($options['unicode']) {
$data = preg_replace("/&amp;#([0-9]+);/s", "&#\\1;", $data);
}
if ($options['escape']) {
$data = Sanitize::escape($data, $options['connection']);
}
if ($options['backslash']) {
$data = preg_replace("/\\\(?!&amp;#|\?#)/", "\\", $data);
}
return $data;
}
}
/**
* Formats column data from definition in DBO's $columns array
*
* @param Model $model The model containing the data to be formatted
* @access public
* @static
*/
function formatColumns(&$model) {
foreach ($model->data as $name => $values) {
if ($name == $model->alias) {
$curModel =& $model;
} elseif (isset($model->{$name}) && is_object($model->{$name}) && is_subclass_of($model->{$name}, 'Model')) {
$curModel =& $model->{$name};
} else {
$curModel = null;
}
if ($curModel != null) {
foreach ($values as $column => $data) {
$colType = $curModel->getColumnType($column);
if ($colType != null) {
$db =& ConnectionManager::getDataSource($curModel->useDbConfig);
$colData = $db->columns[$colType];
if (isset($colData['limit']) && strlen(strval($data)) > $colData['limit']) {
$data = substr(strval($data), 0, $colData['limit']);
}
if (isset($colData['formatter']) || isset($colData['format'])) {
switch (strtolower($colData['formatter'])) {
case 'date':
$data = date($colData['format'], strtotime($data));
break;
case 'sprintf':
$data = sprintf($colData['format'], $data);
break;
case 'intval':
$data = intval($data);
break;
case 'floatval':
$data = floatval($data);
break;
}
}
$model->data[$name][$column]=$data;
/*
switch ($colType) {
case 'integer':
case 'int':
return $data;
break;
case 'string':
case 'text':
case 'binary':
case 'date':
case 'time':
case 'datetime':
case 'timestamp':
case 'date':
return "'" . $data . "'";
break;
}
*/
}
}
}
}
}
}

View File

@@ -0,0 +1,191 @@
<?php
/**
* Core Security
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs
* @since CakePHP(tm) v .0.10.0.1233
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Security Library contains utility methods related to security
*
* @package cake
* @subpackage cake.cake.libs
*/
class Security extends Object {
/**
* Default hash method
*
* @var string
* @access public
*/
var $hashType = null;
/**
* Singleton implementation to get object instance.
*
* @return object
* @access public
* @static
*/
function &getInstance() {
static $instance = array();
if (!$instance) {
$instance[0] =& new Security;
}
return $instance[0];
}
/**
* Get allowed minutes of inactivity based on security level.
*
* @return integer Allowed inactivity in minutes
* @access public
* @static
*/
function inactiveMins() {
switch (Configure::read('Security.level')) {
case 'high':
return 10;
break;
case 'medium':
return 100;
break;
case 'low':
default:
return 300;
break;
}
}
/**
* Generate authorization hash.
*
* @return string Hash
* @access public
* @static
*/
function generateAuthKey() {
if (!class_exists('String')) {
App::import('Core', 'String');
}
return Security::hash(String::uuid());
}
/**
* Validate authorization hash.
*
* @param string $authKey Authorization hash
* @return boolean Success
* @access public
* @static
* @todo Complete implementation
*/
function validateAuthKey($authKey) {
return true;
}
/**
* Create a hash from string using given method.
* Fallback on next available method.
*
* @param string $string String to hash
* @param string $type Method to use (sha1/sha256/md5)
* @param boolean $salt If true, automatically appends the application's salt
* value to $string (Security.salt)
* @return string Hash
* @access public
* @static
*/
function hash($string, $type = null, $salt = false) {
$_this =& Security::getInstance();
if ($salt) {
if (is_string($salt)) {
$string = $salt . $string;
} else {
$string = Configure::read('Security.salt') . $string;
}
}
if (empty($type)) {
$type = $_this->hashType;
}
$type = strtolower($type);
if ($type == 'sha1' || $type == null) {
if (function_exists('sha1')) {
$return = sha1($string);
return $return;
}
$type = 'sha256';
}
if ($type == 'sha256' && function_exists('mhash')) {
return bin2hex(mhash(MHASH_SHA256, $string));
}
if (function_exists('hash')) {
return hash($type, $string);
}
return md5($string);
}
/**
* Sets the default hash method for the Security object. This affects all objects using
* Security::hash().
*
* @param string $hash Method to use (sha1/sha256/md5)
* @access public
* @return void
* @static
* @see Security::hash()
*/
function setHash($hash) {
$_this =& Security::getInstance();
$_this->hashType = $hash;
}
/**
* Encrypts/Decrypts a text using the given key.
*
* @param string $text Encrypted string to decrypt, normal string to encrypt
* @param string $key Key to use
* @return string Encrypted/Decrypted string
* @access public
* @static
*/
function cipher($text, $key) {
if (empty($key)) {
trigger_error(__('You cannot use an empty key for Security::cipher()', true), E_USER_WARNING);
return '';
}
srand(Configure::read('Security.cipherSeed'));
$out = '';
$keyLength = strlen($key);
for ($i = 0, $textLength = strlen($text); $i < $textLength; $i++) {
$j = ord(substr($key, $i % $keyLength, 1));
while ($j--) {
rand(0, 255);
}
$mask = rand(0, 255);
$out .= chr(ord(substr($text, $i, 1)) ^ $mask);
}
srand();
return $out;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,329 @@
<?php
/**
* String handling methods.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs
* @since CakePHP(tm) v 1.2.0.5551
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* String handling methods.
*
*
* @package cake
* @subpackage cake.cake.libs
*/
class String {
/**
* Generate a random UUID
*
* @see http://www.ietf.org/rfc/rfc4122.txt
* @return RFC 4122 UUID
* @static
*/
function uuid() {
$node = env('SERVER_ADDR');
$pid = null;
if (strpos($node, ':') !== false) {
if (substr_count($node, '::')) {
$node = str_replace(
'::', str_repeat(':0000', 8 - substr_count($node, ':')) . ':', $node
);
}
$node = explode(':', $node) ;
$ipv6 = '' ;
foreach ($node as $id) {
$ipv6 .= str_pad(base_convert($id, 16, 2), 16, 0, STR_PAD_LEFT);
}
$node = base_convert($ipv6, 2, 10);
if (strlen($node) < 38) {
$node = null;
} else {
$node = crc32($node);
}
} elseif (empty($node)) {
$host = env('HOSTNAME');
if (empty($host)) {
$host = env('HOST');
}
if (!empty($host)) {
$ip = gethostbyname($host);
if ($ip === $host) {
$node = crc32($host);
} else {
$node = ip2long($ip);
}
}
} elseif ($node !== '127.0.0.1') {
$node = ip2long($node);
} else {
$node = null;
}
if (empty($node)) {
$node = crc32(Configure::read('Security.salt'));
}
if (function_exists('zend_thread_id')) {
$pid = zend_thread_id();
} else {
$pid = getmypid();
}
if (!$pid || $pid > 65535) {
$pid = mt_rand(0, 0xfff) | 0x4000;
}
list($timeMid, $timeLow) = explode(' ', microtime());
$uuid = sprintf(
"%08x-%04x-%04x-%02x%02x-%04x%08x", (int)$timeLow, (int)substr($timeMid, 2) & 0xffff,
mt_rand(0, 0xfff) | 0x4000, mt_rand(0, 0x3f) | 0x80, mt_rand(0, 0xff), $pid, $node
);
return $uuid;
}
/**
* Tokenizes a string using $separator, ignoring any instance of $separator that appears between
* $leftBound and $rightBound
*
* @param string $data The data to tokenize
* @param string $separator The token to split the data on.
* @param string $leftBound The left boundary to ignore separators in.
* @param string $rightBound The right boundary to ignore separators in.
* @return array Array of tokens in $data.
* @access public
* @static
*/
function tokenize($data, $separator = ',', $leftBound = '(', $rightBound = ')') {
if (empty($data) || is_array($data)) {
return $data;
}
$depth = 0;
$offset = 0;
$buffer = '';
$results = array();
$length = strlen($data);
$open = false;
while ($offset <= $length) {
$tmpOffset = -1;
$offsets = array(
strpos($data, $separator, $offset),
strpos($data, $leftBound, $offset),
strpos($data, $rightBound, $offset)
);
for ($i = 0; $i < 3; $i++) {
if ($offsets[$i] !== false && ($offsets[$i] < $tmpOffset || $tmpOffset == -1)) {
$tmpOffset = $offsets[$i];
}
}
if ($tmpOffset !== -1) {
$buffer .= substr($data, $offset, ($tmpOffset - $offset));
if ($data{$tmpOffset} == $separator && $depth == 0) {
$results[] = $buffer;
$buffer = '';
} else {
$buffer .= $data{$tmpOffset};
}
if ($leftBound != $rightBound) {
if ($data{$tmpOffset} == $leftBound) {
$depth++;
}
if ($data{$tmpOffset} == $rightBound) {
$depth--;
}
} else {
if ($data{$tmpOffset} == $leftBound) {
if (!$open) {
$depth++;
$open = true;
} else {
$depth--;
$open = false;
}
}
}
$offset = ++$tmpOffset;
} else {
$results[] = $buffer . substr($data, $offset);
$offset = $length + 1;
}
}
if (empty($results) && !empty($buffer)) {
$results[] = $buffer;
}
if (!empty($results)) {
$data = array_map('trim', $results);
} else {
$data = array();
}
return $data;
}
/**
* Replaces variable placeholders inside a $str with any given $data. Each key in the $data array
* corresponds to a variable placeholder name in $str.
* Example: `String::insert(':name is :age years old.', array('name' => 'Bob', '65'));`
* Returns: Bob is 65 years old.
*
* Available $options are:
*
* - before: The character or string in front of the name of the variable placeholder (Defaults to `:`)
* - after: The character or string after the name of the variable placeholder (Defaults to null)
* - escape: The character or string used to escape the before character / string (Defaults to `\`)
* - format: A regex to use for matching variable placeholders. Default is: `/(?<!\\)\:%s/`
* (Overwrites before, after, breaks escape / clean)
* - clean: A boolean or array with instructions for String::cleanInsert
*
* @param string $str A string containing variable placeholders
* @param string $data A key => val array where each key stands for a placeholder variable name
* to be replaced with val
* @param string $options An array of options, see description above
* @return string
* @access public
* @static
*/
function insert($str, $data, $options = array()) {
$defaults = array(
'before' => ':', 'after' => null, 'escape' => '\\', 'format' => null, 'clean' => false
);
$options += $defaults;
$format = $options['format'];
$data = (array)$data;
if (empty($data)) {
return ($options['clean']) ? String::cleanInsert($str, $options) : $str;
}
if (!isset($format)) {
$format = sprintf(
'/(?<!%s)%s%%s%s/',
preg_quote($options['escape'], '/'),
str_replace('%', '%%', preg_quote($options['before'], '/')),
str_replace('%', '%%', preg_quote($options['after'], '/'))
);
}
if (strpos($str, '?') !== false && is_numeric(key($data))) {
$offset = 0;
while (($pos = strpos($str, '?', $offset)) !== false) {
$val = array_shift($data);
$offset = $pos + strlen($val);
$str = substr_replace($str, $val, $pos, 1);
}
return ($options['clean']) ? String::cleanInsert($str, $options) : $str;
} else {
asort($data);
$hashKeys = array();
foreach ($data as $key => $value) {
$hashKeys[] = crc32($key);
}
$tempData = array_combine(array_keys($data), array_values($hashKeys));
krsort($tempData);
foreach ($tempData as $key => $hashVal) {
$key = sprintf($format, preg_quote($key, '/'));
$str = preg_replace($key, $hashVal, $str);
}
$dataReplacements = array_combine($hashKeys, array_values($data));
foreach ($dataReplacements as $tmpHash => $tmpValue) {
$tmpValue = (is_array($tmpValue)) ? '' : $tmpValue;
$str = str_replace($tmpHash, $tmpValue, $str);
}
}
if (!isset($options['format']) && isset($options['before'])) {
$str = str_replace($options['escape'].$options['before'], $options['before'], $str);
}
return ($options['clean']) ? String::cleanInsert($str, $options) : $str;
}
/**
* Cleans up a String::insert() formated string with given $options depending on the 'clean' key in
* $options. The default method used is text but html is also available. The goal of this function
* is to replace all whitespace and uneeded markup around placeholders that did not get replaced
* by String::insert().
*
* @param string $str
* @param string $options
* @return string
* @access public
* @static
* @see String::insert()
*/
function cleanInsert($str, $options) {
$clean = $options['clean'];
if (!$clean) {
return $str;
}
if ($clean === true) {
$clean = array('method' => 'text');
}
if (!is_array($clean)) {
$clean = array('method' => $options['clean']);
}
switch ($clean['method']) {
case 'html':
$clean = array_merge(array(
'word' => '[\w,.]+',
'andText' => true,
'replacement' => '',
), $clean);
$kleenex = sprintf(
'/[\s]*[a-z]+=(")(%s%s%s[\s]*)+\\1/i',
preg_quote($options['before'], '/'),
$clean['word'],
preg_quote($options['after'], '/')
);
$str = preg_replace($kleenex, $clean['replacement'], $str);
if ($clean['andText']) {
$options['clean'] = array('method' => 'text');
$str = String::cleanInsert($str, $options);
}
break;
case 'text':
$clean = array_merge(array(
'word' => '[\w,.]+',
'gap' => '[\s]*(?:(?:and|or)[\s]*)?',
'replacement' => '',
), $clean);
$kleenex = sprintf(
'/(%s%s%s%s|%s%s%s%s)/',
preg_quote($options['before'], '/'),
$clean['word'],
preg_quote($options['after'], '/'),
$clean['gap'],
$clean['gap'],
preg_quote($options['before'], '/'),
$clean['word'],
preg_quote($options['after'], '/')
);
$str = preg_replace($kleenex, $clean['replacement'], $str);
break;
}
return $str;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,26 @@
<?php
/**
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.templates.elements.email.html
* @since CakePHP(tm) v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
?>
<?php
$content = explode("\n", $content);
foreach ($content as $line):
echo '<p> ' . $line . '</p>';
endforeach;
?>

View File

@@ -0,0 +1,20 @@
<?php
/**
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.templates.elements.email.text
* @since CakePHP(tm) v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
?>
<?php echo $content; ?>

View File

@@ -0,0 +1,61 @@
<?php
/**
* SQL Dump element. Dumps out SQL log information
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.templates.elements
* @since CakePHP(tm) v 1.3
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
if (!class_exists('ConnectionManager') || Configure::read('debug') < 2) {
return false;
}
$noLogs = !isset($logs);
if ($noLogs):
$sources = ConnectionManager::sourceList();
$logs = array();
foreach ($sources as $source):
$db =& ConnectionManager::getDataSource($source);
if (!$db->isInterfaceSupported('getLog')):
continue;
endif;
$logs[$source] = $db->getLog();
endforeach;
endif;
if ($noLogs || isset($_forced_from_dbo_)):
foreach ($logs as $source => $logInfo):
$text = $logInfo['count'] > 1 ? 'queries' : 'query';
printf(
'<table class="cake-sql-log" id="cakeSqlLog_%s" summary="Cake SQL Log" cellspacing="0" border = "0">',
preg_replace('/[^A-Za-z0-9_]/', '_', uniqid(time(), true))
);
printf('<caption>(%s) %s %s took %s ms</caption>', $source, $logInfo['count'], $text, $logInfo['time']);
?>
<thead>
<tr><th>Nr</th><th>Query</th><th>Error</th><th>Affected</th><th>Num. rows</th><th>Took (ms)</th></tr>
</thead>
<tbody>
<?php
foreach ($logInfo['log'] as $k => $i) :
echo "<tr><td>" . ($k + 1) . "</td><td>" . h($i['query']) . "</td><td>{$i['error']}</td><td style = \"text-align: right\">{$i['affected']}</td><td style = \"text-align: right\">{$i['numRows']}</td><td style = \"text-align: right\">{$i['took']}</td></tr>\n";
endforeach;
?>
</tbody></table>
<?php
endforeach;
else:
echo '<p>Encountered unexpected $logs cannot generate SQL log</p>';
endif;
?>

View File

@@ -0,0 +1,24 @@
<?php
/**
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.templates.errors
* @since CakePHP(tm) v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
?>
<h2><?php echo $name; ?></h2>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('The requested address %s was not found on this server.', true), "<strong>'{$message}'</strong>"); ?>
</p>

View File

@@ -0,0 +1,24 @@
<?php
/**
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.templates.errors
* @since CakePHP(tm) v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
?>
<h2><?php echo $name; ?></h2>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('An Internal Error Has Occurred.', true), "<strong>'{$message}'</strong>"); ?>
</p>

View File

@@ -0,0 +1,46 @@
<?php
/**
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.templates.errors
* @since CakePHP(tm) v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
?>
<h2><?php printf(__('Missing Method in %s', true), $controller); ?></h2>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('The action %1$s is not defined in controller %2$s', true), '<em>' . $action . '</em>', '<em>' . $controller . '</em>'); ?>
</p>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('Create %1$s%2$s in file: %3$s.', true), '<em>' . $controller . '::</em>', '<em>' . $action . '()</em>', APP_DIR . DS . 'controllers' . DS . Inflector::underscore($controller) . '.php'); ?>
</p>
<pre>
&lt;?php
class <?php echo $controller;?> extends AppController {
var $name = '<?php echo $controllerName;?>';
<strong>
function <?php echo $action;?>() {
}
</strong>
}
?&gt;
</pre>
<p class="notice">
<strong><?php __('Notice'); ?>: </strong>
<?php printf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . 'views' . DS . 'errors' . DS . 'missing_action.ctp'); ?>
</p>

View File

@@ -0,0 +1,39 @@
<?php
/**
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.templates.errors
* @since CakePHP(tm) v 1.3
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
?>
<h2><?php __('Missing Behavior Class'); ?></h2>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('The behavior class <em>%s</em> can not be found or does not exist.', true), $behaviorClass); ?>
</p>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('Create the class below in file: %s', true), APP_DIR . DS . 'models' . DS . 'behaviors' . DS . $file); ?>
</p>
<pre>
&lt;?php
class <?php echo $behaviorClass;?> extends ModelBehavior {
}
?&gt;
</pre>
<p class="notice">
<strong><?php __('Notice'); ?>: </strong>
<?php printf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . 'views' . DS . 'errors' . DS . 'missing_behavior_class.ctp'); ?>
</p>

View File

@@ -0,0 +1,39 @@
<?php
/**
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.templates.errors
* @since CakePHP(tm) v 1.3
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
?>
<h2><?php __('Missing Behavior File'); ?></h2>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('The Behavior file %s can not be found or does not exist.', true), APP_DIR . DS . 'models' . DS . 'behaviors' . DS . $file); ?>
</p>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('Create the class below in file: %s', true), APP_DIR . DS . 'models' . DS . 'behaviors' . DS . $file); ?>
</p>
<pre>
&lt;?php
class <?php echo $behaviorClass;?> extends ModelBehavior {
}
?&gt;
</pre>
<p class="notice">
<strong><?php __('Notice'); ?>: </strong>
<?php printf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . 'views' . DS . 'errors' . DS . 'missing_behavior_file.ctp'); ?>
</p>

View File

@@ -0,0 +1,39 @@
<?php
/**
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.templates.errors
* @since CakePHP(tm) v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
?>
<h2><?php __('Missing Component Class'); ?></h2>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('Component class %1$s in %2$s was not found.', true), '<em>' . $component . 'Component</em>', '<em>' . $controller . 'Controller</em>'); ?>
</p>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('Create the class %s in file: %s', true), '<em>' . $component . 'Component</em>', APP_DIR . DS . 'controllers' . DS . 'components' . DS . $file); ?>
</p>
<pre>
&lt;?php
class <?php echo $component;?>Component extends Object {<br />
}
?&gt;
</pre>
<p class="notice">
<strong><?php __('Notice'); ?>: </strong>
<?php printf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . 'views' . DS . 'errors' . DS . 'missing_component_class.ctp'); ?>
</p>

View File

@@ -0,0 +1,39 @@
<?php
/**
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.templates.errors
* @since CakePHP(tm) v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
?>
<h2><?php __('Missing Component File'); ?></h2>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php __('The component file was not found.'); ?>
</p>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('Create the class %s in file: %s', true), '<em>' . $component . 'Component</em>', APP_DIR . DS . 'controllers' . DS . 'components' . DS . $file); ?>
</p>
<pre>
&lt;?php
class <?php echo $component;?>Component extends Object {<br />
}
?&gt;
</pre>
<p class="notice">
<strong><?php __('Notice'); ?>: </strong>
<?php printf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . 'views' . DS . 'errors' . DS . 'missing_component_file.ctp'); ?>
</p>

View File

@@ -0,0 +1,32 @@
<?php
/**
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.templates.errors
* @since CakePHP(tm) v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
?>
<h2><?php __('Missing Database Connection'); ?></h2>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('%s requires a database connection', true), $model); ?>
</p>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('Confirm you have created the file : %s.', true), APP_DIR . DS . 'config' . DS . 'database.php'); ?>
</p>
<p class="notice">
<strong><?php __('Notice'); ?>: </strong>
<?php printf(__('If you want to customize this error message, create %s.', true), APP_DIR . DS . 'views' . DS . 'errors' . DS . basename(__FILE__)); ?>
</p>

View File

@@ -0,0 +1,40 @@
<?php
/**
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.templates.errors
* @since CakePHP(tm) v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
?>
<h2><?php __('Missing Controller'); ?></h2>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('%s could not be found.', true), '<em>' . $controller . '</em>'); ?>
</p>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('Create the class %s below in file: %s', true), '<em>' . $controller . '</em>', APP_DIR . DS . 'controllers' . DS . Inflector::underscore($controller) . '.php'); ?>
</p>
<pre>
&lt;?php
class <?php echo $controller;?> extends AppController {
var $name = '<?php echo $controllerName;?>';
}
?&gt;
</pre>
<p class="notice">
<strong><?php __('Notice'); ?>: </strong>
<?php printf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . 'views' . DS . 'errors' . DS . 'missing_controller.ctp'); ?>
</p>

View File

@@ -0,0 +1,39 @@
<?php
/**
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.templates.errors
* @since CakePHP(tm) v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
?>
<h2><?php __('Missing Helper Class'); ?></h2>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('The helper class <em>%s</em> can not be found or does not exist.', true), $helperClass); ?>
</p>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('Create the class below in file: %s', true), APP_DIR . DS . 'views' . DS . 'helpers' . DS . $file); ?>
</p>
<pre>
&lt;?php
class <?php echo $helperClass;?> extends AppHelper {
}
?&gt;
</pre>
<p class="notice">
<strong><?php __('Notice'); ?>: </strong>
<?php printf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . 'views' . DS . 'errors' . DS . 'missing_helper_class.ctp'); ?>
</p>

View File

@@ -0,0 +1,39 @@
<?php
/**
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.templates.errors
* @since CakePHP(tm) v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
?>
<h2><?php __('Missing Helper File'); ?></h2>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('The helper file %s can not be found or does not exist.', true), APP_DIR . DS . 'views' . DS . 'helpers' . DS . $file); ?>
</p>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('Create the class below in file: %s', true), APP_DIR . DS . 'views' . DS . 'helpers' . DS . $file); ?>
</p>
<pre>
&lt;?php
class <?php echo $helperClass;?> extends AppHelper {
}
?&gt;
</pre>
<p class="notice">
<strong><?php __('Notice'); ?>: </strong>
<?php printf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . 'views' . DS . 'errors' . DS . 'missing_helper_file.ctp'); ?>
</p>

View File

@@ -0,0 +1,32 @@
<?php
/**
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.templates.errors
* @since CakePHP(tm) v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
?>
<h2><?php __('Missing Layout'); ?></h2>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('The layout file %s can not be found or does not exist.', true), '<em>' . $file . '</em>'); ?>
</p>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('Confirm you have created the file: %s', true), '<em>' . $file . '</em>'); ?>
</p>
<p class="notice">
<strong><?php __('Notice'); ?>: </strong>
<?php printf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . 'views' . DS . 'errors' . DS . 'missing_layout.ctp'); ?>
</p>

View File

@@ -0,0 +1,41 @@
<?php
/**
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.templates.errors
* @since CakePHP(tm) v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
?>
<h2><?php __('Missing Model'); ?></h2>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('<em>%s</em> could not be found.', true), $model); ?>
</p>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('Create the class %s in file: %s', true), '<em>' . $model . '</em>', APP_DIR . DS . 'models' . DS . Inflector::underscore($model) . '.php'); ?>
</p>
<pre>
&lt;?php
class <?php echo $model;?> extends AppModel {
var $name = '<?php echo $model;?>';
}
?&gt;
</pre>
<p class="notice">
<strong><?php __('Notice'); ?>: </strong>
<?php printf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . 'views' . DS . 'errors' . DS . 'missing_model.ctp'); ?>
</p>

View File

@@ -0,0 +1,32 @@
<?php
/**
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.templates.errors
* @since CakePHP(tm) v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
?>
<h2><?php __('Missing Database Connection'); ?></h2>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php __('Scaffold requires a database connection'); ?>
</p>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('Confirm you have created the file: %s', true), APP_DIR . DS . 'config' . DS . 'database.php'); ?>
</p>
<p class="notice">
<strong><?php __('Notice'); ?>: </strong>
<?php printf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . 'views' . DS . 'errors' . DS . 'missing_scaffolddb.ctp'); ?>
</p>

View File

@@ -0,0 +1,28 @@
<?php
/**
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.templates.errors
* @since CakePHP(tm) v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
?>
<h2><?php __('Missing Database Table'); ?></h2>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('Database table %1$s for model %2$s was not found.', true), '<em>' . $table . '</em>', '<em>' . $model . '</em>'); ?>
</p>
<p class="notice">
<strong><?php __('Notice'); ?>: </strong>
<?php printf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . 'views' . DS . 'errors' . DS . 'missing_table.ctp'); ?>
</p>

View File

@@ -0,0 +1,32 @@
<?php
/**
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.templates.errors
* @since CakePHP(tm) v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
?>
<h2><?php __('Missing View'); ?></h2>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('The view for %1$s%2$s was not found.', true), '<em>' . $controller . 'Controller::</em>', '<em>' . $action . '()</em>'); ?>
</p>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('Confirm you have created the file: %s', true), $file); ?>
</p>
<p class="notice">
<strong><?php __('Notice'); ?>: </strong>
<?php printf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . 'views' . DS . 'errors' . DS . 'missing_view.ctp'); ?>
</p>

View File

@@ -0,0 +1,28 @@
<?php
/**
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.templates.errors
* @since CakePHP(tm) v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
?>
<h2><?php printf(__('Private Method in %s', true), $controller); ?></h2>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php printf(__('%s%s cannot be accessed directly.', true), '<em>' . $controller . '::</em>', '<em>' . $action . '()</em>'); ?>
</p>
<p class="notice">
<strong><?php __('Notice'); ?>: </strong>
<?php printf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . 'views' . DS . 'errors' . DS . 'private_action.ctp'); ?>
</p>

View File

@@ -0,0 +1,35 @@
<?php
/**
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.templates.errors
* @since CakePHP(tm) v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
?>
<h2><?php __('Scaffold Error'); ?></h2>
<p class="error">
<strong><?php __('Error'); ?>: </strong>
<?php __('Method _scaffoldError in was not found in the controller'); ?>
</p>
<p class="notice">
<strong><?php __('Notice'); ?>: </strong>
<?php printf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . 'views' . DS . 'errors' . DS . 'scaffold_error.ctp'); ?>
</p>
<pre>
&lt;?php
function _scaffoldError() {<br />
}
?&gt;
</pre>

View File

@@ -0,0 +1,912 @@
<?php
/**
* Backend for helpers.
*
* Internal methods for the Helpers.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view
* @since CakePHP(tm) v 0.2.9
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Included libs
*/
App::import('Core', 'Overloadable');
/**
* Abstract base class for all other Helpers in CakePHP.
* Provides common methods and features.
*
* @package cake
* @subpackage cake.cake.libs.view
*/
class Helper extends Overloadable {
/**
* List of helpers used by this helper
*
* @var array
*/
var $helpers = null;
/**
* Base URL
*
* @var string
*/
var $base = null;
/**
* Webroot path
*
* @var string
*/
var $webroot = null;
/**
* The current theme name if any.
*
* @var string
*/
var $theme = null;
/**
* URL to current action.
*
* @var string
*/
var $here = null;
/**
* Parameter array.
*
* @var array
*/
var $params = array();
/**
* Current action.
*
* @var string
*/
var $action = null;
/**
* Plugin path
*
* @var string
*/
var $plugin = null;
/**
* POST data for models
*
* @var array
*/
var $data = null;
/**
* List of named arguments
*
* @var array
*/
var $namedArgs = null;
/**
* URL argument separator character
*
* @var string
*/
var $argSeparator = null;
/**
* Contains model validation errors of form post-backs
*
* @access public
* @var array
*/
var $validationErrors = null;
/**
* Holds tag templates.
*
* @access public
* @var array
*/
var $tags = array();
/**
* Holds the content to be cleaned.
*
* @access private
* @var mixed
*/
var $__tainted = null;
/**
* Holds the cleaned content.
*
* @access private
* @var mixed
*/
var $__cleaned = null;
/**
* Default overload methods
*
* @access protected
*/
function get__($name) {}
function set__($name, $value) {}
function call__($method, $params) {
trigger_error(sprintf(__('Method %1$s::%2$s does not exist', true), get_class($this), $method), E_USER_WARNING);
}
/**
* Parses tag templates into $this->tags.
*
* @param $name file name inside app/config to load.
* @return array merged tags from config/$name.php
* @access public
*/
function loadConfig($name = 'tags') {
if (file_exists(CONFIGS . $name .'.php')) {
require(CONFIGS . $name .'.php');
if (isset($tags)) {
$this->tags = array_merge($this->tags, $tags);
}
}
return $this->tags;
}
/**
* Finds URL for specified action.
*
* Returns a URL pointing at the provided parameters.
*
* @param mixed $url Either a relative string url like `/products/view/23` or
* an array of url parameters. Using an array for urls will allow you to leverage
* the reverse routing features of CakePHP.
* @param boolean $full If true, the full base URL will be prepended to the result
* @return string Full translated URL with base path.
* @access public
* @link http://book.cakephp.org/view/1448/url
*/
function url($url = null, $full = false) {
return h(Router::url($url, $full));
}
/**
* Checks if a file exists when theme is used, if no file is found default location is returned
*
* @param string $file The file to create a webroot path to.
* @return string Web accessible path to file.
* @access public
*/
function webroot($file) {
$asset = explode('?', $file);
$asset[1] = isset($asset[1]) ? '?' . $asset[1] : null;
$webPath = "{$this->webroot}" . $asset[0];
$file = $asset[0];
if (!empty($this->theme)) {
$file = trim($file, '/');
$theme = $this->theme . '/';
if (DS === '\\') {
$file = str_replace('/', '\\', $file);
}
if (file_exists(Configure::read('App.www_root') . 'theme' . DS . $this->theme . DS . $file)) {
$webPath = "{$this->webroot}theme/" . $theme . $asset[0];
} else {
$viewPaths = App::path('views');
foreach ($viewPaths as $viewPath) {
$path = $viewPath . 'themed'. DS . $this->theme . DS . 'webroot' . DS . $file;
if (file_exists($path)) {
$webPath = "{$this->webroot}theme/" . $theme . $asset[0];
break;
}
}
}
}
if (strpos($webPath, '//') !== false) {
return str_replace('//', '/', $webPath . $asset[1]);
}
return $webPath . $asset[1];
}
/**
* Adds a timestamp to a file based resource based on the value of `Asset.timestamp` in
* Configure. If Asset.timestamp is true and debug > 0, or Asset.timestamp == 'force'
* a timestamp will be added.
*
* @param string $path The file path to timestamp, the path must be inside WWW_ROOT
* @return string Path with a timestamp added, or not.
* @access public
*/
function assetTimestamp($path) {
$timestampEnabled = (
(Configure::read('Asset.timestamp') === true && Configure::read() > 0) ||
Configure::read('Asset.timestamp') === 'force'
);
if (strpos($path, '?') === false && $timestampEnabled) {
$filepath = preg_replace('/^' . preg_quote($this->webroot, '/') . '/', '', $path);
$webrootPath = WWW_ROOT . str_replace('/', DS, $filepath);
if (file_exists($webrootPath)) {
return $path . '?' . @filemtime($webrootPath);
}
$segments = explode('/', ltrim($filepath, '/'));
if ($segments[0] === 'theme') {
$theme = $segments[1];
unset($segments[0], $segments[1]);
$themePath = App::themePath($theme) . 'webroot' . DS . implode(DS, $segments);
return $path . '?' . @filemtime($themePath);
} else {
$plugin = $segments[0];
unset($segments[0]);
$pluginPath = App::pluginPath($plugin) . 'webroot' . DS . implode(DS, $segments);
return $path . '?' . @filemtime($pluginPath);
}
}
return $path;
}
/**
* Used to remove harmful tags from content. Removes a number of well known XSS attacks
* from content. However, is not guaranteed to remove all possiblities. Escaping
* content is the best way to prevent all possible attacks.
*
* @param mixed $output Either an array of strings to clean or a single string to clean.
* @return cleaned content for output
* @access public
*/
function clean($output) {
$this->__reset();
if (empty($output)) {
return null;
}
if (is_array($output)) {
foreach ($output as $key => $value) {
$return[$key] = $this->clean($value);
}
return $return;
}
$this->__tainted = $output;
$this->__clean();
return $this->__cleaned;
}
/**
* Returns a space-delimited string with items of the $options array. If a
* key of $options array happens to be one of:
*
* - 'compact'
* - 'checked'
* - 'declare'
* - 'readonly'
* - 'disabled'
* - 'selected'
* - 'defer'
* - 'ismap'
* - 'nohref'
* - 'noshade'
* - 'nowrap'
* - 'multiple'
* - 'noresize'
*
* And its value is one of:
*
* - '1' (string)
* - 1 (integer)
* - true (boolean)
* - 'true' (string)
*
* Then the value will be reset to be identical with key's name.
* If the value is not one of these 3, the parameter is not output.
*
* 'escape' is a special option in that it controls the conversion of
* attributes to their html-entity encoded equivalents. Set to false to disable html-encoding.
*
* If value for any option key is set to `null` or `false`, that option will be excluded from output.
*
* @param array $options Array of options.
* @param array $exclude Array of options to be excluded, the options here will not be part of the return.
* @param string $insertBefore String to be inserted before options.
* @param string $insertAfter String to be inserted after options.
* @return string Composed attributes.
* @access public
*/
function _parseAttributes($options, $exclude = null, $insertBefore = ' ', $insertAfter = null) {
if (is_array($options)) {
$options = array_merge(array('escape' => true), $options);
if (!is_array($exclude)) {
$exclude = array();
}
$keys = array_diff(array_keys($options), array_merge($exclude, array('escape')));
$values = array_intersect_key(array_values($options), $keys);
$escape = $options['escape'];
$attributes = array();
foreach ($keys as $index => $key) {
if ($values[$index] !== false && $values[$index] !== null) {
$attributes[] = $this->__formatAttribute($key, $values[$index], $escape);
}
}
$out = implode(' ', $attributes);
} else {
$out = $options;
}
return $out ? $insertBefore . $out . $insertAfter : '';
}
/**
* Formats an individual attribute, and returns the string value of the composed attribute.
* Works with minimized attributes that have the same value as their name such as 'disabled' and 'checked'
*
* @param string $key The name of the attribute to create
* @param string $value The value of the attribute to create.
* @return string The composed attribute.
* @access private
*/
function __formatAttribute($key, $value, $escape = true) {
$attribute = '';
$attributeFormat = '%s="%s"';
$minimizedAttributes = array('compact', 'checked', 'declare', 'readonly', 'disabled',
'selected', 'defer', 'ismap', 'nohref', 'noshade', 'nowrap', 'multiple', 'noresize');
if (is_array($value)) {
$value = '';
}
if (in_array($key, $minimizedAttributes)) {
if ($value === 1 || $value === true || $value === 'true' || $value === '1' || $value == $key) {
$attribute = sprintf($attributeFormat, $key, $key);
}
} else {
$attribute = sprintf($attributeFormat, $key, ($escape ? h($value) : $value));
}
return $attribute;
}
/**
* Sets this helper's model and field properties to the dot-separated value-pair in $entity.
*
* @param mixed $entity A field name, like "ModelName.fieldName" or "ModelName.ID.fieldName"
* @param boolean $setScope Sets the view scope to the model specified in $tagValue
* @return void
* @access public
*/
function setEntity($entity, $setScope = false) {
$view =& ClassRegistry::getObject('view');
if ($setScope) {
$view->modelScope = false;
} elseif (!empty($view->entityPath) && $view->entityPath == $entity) {
return;
}
if ($entity === null) {
$view->model = null;
$view->association = null;
$view->modelId = null;
$view->modelScope = false;
$view->entityPath = null;
return;
}
$view->entityPath = $entity;
$model = $view->model;
$sameScope = $hasField = false;
$parts = array_values(Set::filter(explode('.', $entity), true));
if (empty($parts)) {
return;
}
$count = count($parts);
if ($count === 1) {
$sameScope = true;
} else {
if (is_numeric($parts[0])) {
$sameScope = true;
}
$reverse = array_reverse($parts);
$field = array_shift($reverse);
while(!empty($reverse)) {
$subject = array_shift($reverse);
if (is_numeric($subject)) {
continue;
}
if (ClassRegistry::isKeySet($subject)) {
$model = $subject;
break;
}
}
}
if (ClassRegistry::isKeySet($model)) {
$ModelObj =& ClassRegistry::getObject($model);
for ($i = 0; $i < $count; $i++) {
if (
is_a($ModelObj, 'Model') &&
($ModelObj->hasField($parts[$i]) ||
array_key_exists($parts[$i], $ModelObj->validate))
) {
$hasField = $i;
if ($hasField === 0 || ($hasField === 1 && is_numeric($parts[0]))) {
$sameScope = true;
}
break;
}
}
if ($sameScope === true && in_array($parts[0], array_keys($ModelObj->hasAndBelongsToMany))) {
$sameScope = false;
}
}
if (!$view->association && $parts[0] == $view->field && $view->field != $view->model) {
array_unshift($parts, $model);
$hasField = true;
}
$view->field = $view->modelId = $view->fieldSuffix = $view->association = null;
switch (count($parts)) {
case 1:
if ($view->modelScope === false) {
$view->model = $parts[0];
} else {
$view->field = $parts[0];
if ($sameScope === false) {
$view->association = $parts[0];
}
}
break;
case 2:
if ($view->modelScope === false) {
list($view->model, $view->field) = $parts;
} elseif ($sameScope === true && $hasField === 0) {
list($view->field, $view->fieldSuffix) = $parts;
} elseif ($sameScope === true && $hasField === 1) {
list($view->modelId, $view->field) = $parts;
} else {
list($view->association, $view->field) = $parts;
}
break;
case 3:
if ($sameScope === true && $hasField === 1) {
list($view->modelId, $view->field, $view->fieldSuffix) = $parts;
} elseif ($hasField === 2) {
list($view->association, $view->modelId, $view->field) = $parts;
} else {
list($view->association, $view->field, $view->fieldSuffix) = $parts;
}
break;
case 4:
if ($parts[0] === $view->model) {
list($view->model, $view->modelId, $view->field, $view->fieldSuffix) = $parts;
} else {
list($view->association, $view->modelId, $view->field, $view->fieldSuffix) = $parts;
}
break;
default:
$reverse = array_reverse($parts);
if ($hasField) {
$view->field = $field;
if (!is_numeric($reverse[1]) && $reverse[1] != $model) {
$view->field = $reverse[1];
$view->fieldSuffix = $field;
}
}
if (is_numeric($parts[0])) {
$view->modelId = $parts[0];
} elseif ($view->model == $parts[0] && is_numeric($parts[1])) {
$view->modelId = $parts[1];
}
$view->association = $model;
break;
}
if (!isset($view->model) || empty($view->model)) {
$view->model = $view->association;
$view->association = null;
} elseif ($view->model === $view->association) {
$view->association = null;
}
if ($setScope) {
$view->modelScope = true;
}
}
/**
* Gets the currently-used model of the rendering context.
*
* @return string
* @access public
*/
function model() {
$view =& ClassRegistry::getObject('view');
if (!empty($view->association)) {
return $view->association;
} else {
return $view->model;
}
}
/**
* Gets the ID of the currently-used model of the rendering context.
*
* @return mixed
* @access public
*/
function modelID() {
$view =& ClassRegistry::getObject('view');
return $view->modelId;
}
/**
* Gets the currently-used model field of the rendering context.
*
* @return string
* @access public
*/
function field() {
$view =& ClassRegistry::getObject('view');
return $view->field;
}
/**
* Returns false if given FORM field has no errors. Otherwise it returns the constant set in
* the array Model->validationErrors.
*
* @param string $model Model name as a string
* @param string $field Fieldname as a string
* @param integer $modelID Unique index identifying this record within the form
* @return boolean True on errors.
*/
function tagIsInvalid($model = null, $field = null, $modelID = null) {
$view =& ClassRegistry::getObject('view');
$errors = $this->validationErrors;
$entity = $view->entity();
if (!empty($entity)) {
return Set::extract($errors, join('.', $entity));
}
}
/**
* Generates a DOM ID for the selected element, if one is not set.
* Uses the current View::entity() settings to generate a CamelCased id attribute.
*
* @param mixed $options Either an array of html attributes to add $id into, or a string
* with a view entity path to get a domId for.
* @param string $id The name of the 'id' attribute.
* @return mixed If $options was an array, an array will be returned with $id set. If a string
* was supplied, a string will be returned.
* @todo Refactor this method to not have as many input/output options.
*/
function domId($options = null, $id = 'id') {
$view =& ClassRegistry::getObject('view');
if (is_array($options) && array_key_exists($id, $options) && $options[$id] === null) {
unset($options[$id]);
return $options;
} elseif (!is_array($options) && $options !== null) {
$this->setEntity($options);
return $this->domId();
}
$entity = $view->entity();
$model = array_shift($entity);
$dom = $model . join('', array_map(array('Inflector', 'camelize'), $entity));
if (is_array($options) && !array_key_exists($id, $options)) {
$options[$id] = $dom;
} elseif ($options === null) {
return $dom;
}
return $options;
}
/**
* Gets the input field name for the current tag. Creates input name attributes
* using CakePHP's data[Model][field] formatting.
*
* @param mixed $options If an array, should be an array of attributes that $key needs to be added to.
* If a string or null, will be used as the View entity.
* @param string $field
* @param string $key The name of the attribute to be set, defaults to 'name'
* @return mixed If an array was given for $options, an array with $key set will be returned.
* If a string was supplied a string will be returned.
* @access protected
* @todo Refactor this method to not have as many input/output options.
*/
function _name($options = array(), $field = null, $key = 'name') {
$view =& ClassRegistry::getObject('view');
if ($options === null) {
$options = array();
} elseif (is_string($options)) {
$field = $options;
$options = 0;
}
if (!empty($field)) {
$this->setEntity($field);
}
if (is_array($options) && array_key_exists($key, $options)) {
return $options;
}
switch ($field) {
case '_method':
$name = $field;
break;
default:
$name = 'data[' . implode('][', $view->entity()) . ']';
break;
}
if (is_array($options)) {
$options[$key] = $name;
return $options;
} else {
return $name;
}
}
/**
* Gets the data for the current tag
*
* @param mixed $options If an array, should be an array of attributes that $key needs to be added to.
* If a string or null, will be used as the View entity.
* @param string $field
* @param string $key The name of the attribute to be set, defaults to 'value'
* @return mixed If an array was given for $options, an array with $key set will be returned.
* If a string was supplied a string will be returned.
* @access public
* @todo Refactor this method to not have as many input/output options.
*/
function value($options = array(), $field = null, $key = 'value') {
if ($options === null) {
$options = array();
} elseif (is_string($options)) {
$field = $options;
$options = 0;
}
if (is_array($options) && isset($options[$key])) {
return $options;
}
if (!empty($field)) {
$this->setEntity($field);
}
$view =& ClassRegistry::getObject('view');
$result = null;
$entity = $view->entity();
if (!empty($this->data) && !empty($entity)) {
$result = Set::extract($this->data, join('.', $entity));
}
$habtmKey = $this->field();
if (empty($result) && isset($this->data[$habtmKey][$habtmKey])) {
$result = $this->data[$habtmKey][$habtmKey];
} elseif (empty($result) && isset($this->data[$habtmKey]) && is_array($this->data[$habtmKey])) {
if (ClassRegistry::isKeySet($habtmKey)) {
$model =& ClassRegistry::getObject($habtmKey);
$result = $this->__selectedArray($this->data[$habtmKey], $model->primaryKey);
}
}
if (is_array($result)) {
if (array_key_exists($view->fieldSuffix, $result)) {
$result = $result[$view->fieldSuffix];
}
}
if (is_array($options)) {
if ($result === null && isset($options['default'])) {
$result = $options['default'];
}
unset($options['default']);
}
if (is_array($options)) {
$options[$key] = $result;
return $options;
} else {
return $result;
}
}
/**
* Sets the defaults for an input tag. Will set the
* name, value, and id attributes for an array of html attributes. Will also
* add a 'form-error' class if the field contains validation errors.
*
* @param string $field The field name to initialize.
* @param array $options Array of options to use while initializing an input field.
* @return array Array options for the form input.
* @access protected
*/
function _initInputField($field, $options = array()) {
if ($field !== null) {
$this->setEntity($field);
}
$options = (array)$options;
$options = $this->_name($options);
$options = $this->value($options);
$options = $this->domId($options);
if ($this->tagIsInvalid()) {
$options = $this->addClass($options, 'form-error');
}
return $options;
}
/**
* Adds the given class to the element options
*
* @param array $options Array options/attributes to add a class to
* @param string $class The classname being added.
* @param string $key the key to use for class.
* @return array Array of options with $key set.
* @access public
*/
function addClass($options = array(), $class = null, $key = 'class') {
if (isset($options[$key]) && trim($options[$key]) != '') {
$options[$key] .= ' ' . $class;
} else {
$options[$key] = $class;
}
return $options;
}
/**
* Returns a string generated by a helper method
*
* This method can be overridden in subclasses to do generalized output post-processing
*
* @param string $str String to be output.
* @return string
* @deprecated This method will be removed in future versions.
*/
function output($str) {
return $str;
}
/**
* Before render callback. beforeRender is called before the view file is rendered.
*
* Overridden in subclasses.
*
* @return void
* @access public
*/
function beforeRender() {
}
/**
* After render callback. afterRender is called after the view file is rendered
* but before the layout has been rendered.
*
* Overridden in subclasses.
*
* @return void
* @access public
*/
function afterRender() {
}
/**
* Before layout callback. beforeLayout is called before the layout is rendered.
*
* Overridden in subclasses.
*
* @return void
* @access public
*/
function beforeLayout() {
}
/**
* After layout callback. afterLayout is called after the layout has rendered.
*
* Overridden in subclasses.
*
* @return void
* @access public
*/
function afterLayout() {
}
/**
* Transforms a recordset from a hasAndBelongsToMany association to a list of selected
* options for a multiple select element
*
* @param mixed $data
* @param string $key
* @return array
* @access private
*/
function __selectedArray($data, $key = 'id') {
if (!is_array($data)) {
$model = $data;
if (!empty($this->data[$model][$model])) {
return $this->data[$model][$model];
}
if (!empty($this->data[$model])) {
$data = $this->data[$model];
}
}
$array = array();
if (!empty($data)) {
foreach ($data as $var) {
$array[$var[$key]] = $var[$key];
}
}
return $array;
}
/**
* Resets the vars used by Helper::clean() to null
*
* @return void
* @access private
*/
function __reset() {
$this->__tainted = null;
$this->__cleaned = null;
}
/**
* Removes harmful content from output
*
* @return void
* @access private
*/
function __clean() {
if (get_magic_quotes_gpc()) {
$this->__cleaned = stripslashes($this->__tainted);
} else {
$this->__cleaned = $this->__tainted;
}
$this->__cleaned = str_replace(array("&amp;", "&lt;", "&gt;"), array("&amp;amp;", "&amp;lt;", "&amp;gt;"), $this->__cleaned);
$this->__cleaned = preg_replace('#(&\#*\w+)[\x00-\x20]+;#u', "$1;", $this->__cleaned);
$this->__cleaned = preg_replace('#(&\#x*)([0-9A-F]+);*#iu', "$1$2;", $this->__cleaned);
$this->__cleaned = html_entity_decode($this->__cleaned, ENT_COMPAT, "UTF-8");
$this->__cleaned = preg_replace('#(<[^>]+[\x00-\x20\"\'\/])(on|xmlns)[^>]*>#iUu', "$1>", $this->__cleaned);
$this->__cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*)[\\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iUu', '$1=$2nojavascript...', $this->__cleaned);
$this->__cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=([\'\"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iUu', '$1=$2novbscript...', $this->__cleaned);
$this->__cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=*([\'\"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#iUu','$1=$2nomozbinding...', $this->__cleaned);
$this->__cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=([\'\"]*)[\x00-\x20]*data[\x00-\x20]*:#Uu', '$1=$2nodata...', $this->__cleaned);
$this->__cleaned = preg_replace('#(<[^>]+)style[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*).*expression[\x00-\x20]*\([^>]*>#iU', "$1>", $this->__cleaned);
$this->__cleaned = preg_replace('#(<[^>]+)style[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*).*behaviour[\x00-\x20]*\([^>]*>#iU', "$1>", $this->__cleaned);
$this->__cleaned = preg_replace('#(<[^>]+)style[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*).*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*>#iUu', "$1>", $this->__cleaned);
$this->__cleaned = preg_replace('#</*\w+:\w[^>]*>#i', "", $this->__cleaned);
do {
$oldstring = $this->__cleaned;
$this->__cleaned = preg_replace('#</*(applet|meta|xml|blink|link|style|script|embed|object|iframe|frame|frameset|ilayer|layer|bgsound|title|base)[^>]*>#i', "", $this->__cleaned);
} while ($oldstring != $this->__cleaned);
$this->__cleaned = str_replace(array("&amp;", "&lt;", "&gt;"), array("&amp;amp;", "&amp;lt;", "&amp;gt;"), $this->__cleaned);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
<?php
/**
* Application level View Helper
*
* This file is application-wide helper file. You can put all
* application-wide helper-related methods here.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake
* @since CakePHP(tm) v 0.2.9
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('View', 'Helper', false);
/**
* This is a placeholder class.
* Create the same file in app/app_helper.php
*
* Add your application-wide methods in the class below, your helpers
* will inherit them.
*
* @package cake
* @subpackage cake.cake
*/
class AppHelper extends Helper {
}

View File

@@ -0,0 +1,261 @@
<?php
/**
* CacheHelper helps create full page view caching.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.helpers
* @since CakePHP(tm) v 1.0.0.2277
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* CacheHelper helps create full page view caching.
*
* When using CacheHelper you don't call any of its methods, they are all automatically
* called by View, and use the $cacheAction settings set in the controller.
*
* @package cake
* @subpackage cake.cake.libs.view.helpers
* @link http://book.cakephp.org/view/1376/Cache
*/
class CacheHelper extends AppHelper {
/**
* Array of strings replaced in cached views.
* The strings are found between <cake:nocache><cake:nocache> in views
*
* @var array
* @access private
*/
var $__replace = array();
/**
* Array of string that are replace with there var replace above.
* The strings are any content inside <cake:nocache><cake:nocache> and includes the tags in views
*
* @var array
* @access private
*/
var $__match = array();
/**
* cache action time
*
* @var object
* @access public
*/
var $cacheAction;
/**
* Main method used to cache a view
*
* @param string $file File to cache
* @param string $out output to cache
* @param boolean $cache Whether or not a cache file should be written.
* @return string view ouput
*/
function cache($file, $out, $cache = false) {
$cacheTime = 0;
$useCallbacks = false;
if (is_array($this->cacheAction)) {
$keys = array_keys($this->cacheAction);
$index = null;
foreach ($keys as $action) {
if ($action == $this->params['action']) {
$index = $action;
break;
}
}
if (!isset($index) && $this->action == 'index') {
$index = 'index';
}
$options = $this->cacheAction;
if (isset($this->cacheAction[$index])) {
if (is_array($this->cacheAction[$index])) {
$options = array_merge(array('duration' => 0, 'callbacks' => false), $this->cacheAction[$index]);
} else {
$cacheTime = $this->cacheAction[$index];
}
}
if (isset($options['duration'])) {
$cacheTime = $options['duration'];
}
if (isset($options['callbacks'])) {
$useCallbacks = $options['callbacks'];
}
} else {
$cacheTime = $this->cacheAction;
}
if ($cacheTime != '' && $cacheTime > 0) {
$this->__parseFile($file, $out);
if ($cache === true) {
$cached = $this->__parseOutput($out);
$this->__writeFile($cached, $cacheTime, $useCallbacks);
}
return $out;
} else {
return $out;
}
}
/**
* Parse file searching for no cache tags
*
* @param string $file The filename that needs to be parsed.
* @param string $cache The cached content
* @access private
*/
function __parseFile($file, $cache) {
if (is_file($file)) {
$file = file_get_contents($file);
} elseif ($file = fileExistsInPath($file)) {
$file = file_get_contents($file);
}
preg_match_all('/(<cake:nocache>(?<=<cake:nocache>)[\\s\\S]*?(?=<\/cake:nocache>)<\/cake:nocache>)/i', $cache, $outputResult, PREG_PATTERN_ORDER);
preg_match_all('/(?<=<cake:nocache>)([\\s\\S]*?)(?=<\/cake:nocache>)/i', $file, $fileResult, PREG_PATTERN_ORDER);
$fileResult = $fileResult[0];
$outputResult = $outputResult[0];
if (!empty($this->__replace)) {
foreach ($outputResult as $i => $element) {
$index = array_search($element, $this->__match);
if ($index !== false) {
unset($outputResult[$i]);
}
}
$outputResult = array_values($outputResult);
}
if (!empty($fileResult)) {
$i = 0;
foreach ($fileResult as $cacheBlock) {
if (isset($outputResult[$i])) {
$this->__replace[] = $cacheBlock;
$this->__match[] = $outputResult[$i];
}
$i++;
}
}
}
/**
* Parse the output and replace cache tags
*
* @param string $cache Output to replace content in.
* @return string with all replacements made to <cake:nocache><cake:nocache>
* @access private
*/
function __parseOutput($cache) {
$count = 0;
if (!empty($this->__match)) {
foreach ($this->__match as $found) {
$original = $cache;
$length = strlen($found);
$position = 0;
for ($i = 1; $i <= 1; $i++) {
$position = strpos($cache, $found, $position);
if ($position !== false) {
$cache = substr($original, 0, $position);
$cache .= $this->__replace[$count];
$cache .= substr($original, $position + $length);
} else {
break;
}
}
$count++;
}
return $cache;
}
return $cache;
}
/**
* Write a cached version of the file
*
* @param string $content view content to write to a cache file.
* @param sting $timestamp Duration to set for cache file.
* @return boolean success of caching view.
* @access private
*/
function __writeFile($content, $timestamp, $useCallbacks = false) {
$now = time();
if (is_numeric($timestamp)) {
$cacheTime = $now + $timestamp;
} else {
$cacheTime = strtotime($timestamp, $now);
}
$path = $this->here;
if ($this->here == '/') {
$path = 'home';
}
$cache = strtolower(Inflector::slug($path));
if (empty($cache)) {
return;
}
$cache = $cache . '.php';
$file = '<!--cachetime:' . $cacheTime . '--><?php';
if (empty($this->plugin)) {
$file .= '
App::import(\'Controller\', \'' . $this->controllerName. '\');
';
} else {
$file .= '
App::import(\'Controller\', \'' . $this->plugin . '.' . $this->controllerName. '\');
';
}
$file .= '$controller =& new ' . $this->controllerName . 'Controller();
$controller->plugin = $this->plugin = \''.$this->plugin.'\';
$controller->helpers = $this->helpers = unserialize(\'' . serialize($this->helpers) . '\');
$controller->base = $this->base = \'' . $this->base . '\';
$controller->layout = $this->layout = \'' . $this->layout. '\';
$controller->webroot = $this->webroot = \'' . $this->webroot . '\';
$controller->here = $this->here = \'' . $this->here . '\';
$controller->params = $this->params = unserialize(stripslashes(\'' . addslashes(serialize($this->params)) . '\'));
$controller->action = $this->action = unserialize(\'' . serialize($this->action) . '\');
$controller->data = $this->data = unserialize(stripslashes(\'' . addslashes(serialize($this->data)) . '\'));
$controller->theme = $this->theme = \'' . $this->theme . '\';
Router::setRequestInfo(array($this->params, array(\'base\' => $this->base, \'webroot\' => $this->webroot)));';
if ($useCallbacks == true) {
$file .= '
$controller->constructClasses();
$controller->Component->initialize($controller);
$controller->beforeFilter();
$controller->Component->startup($controller);';
}
$file .= '
$loadedHelpers = array();
$loadedHelpers = $this->_loadHelpers($loadedHelpers, $this->helpers);
foreach (array_keys($loadedHelpers) as $helper) {
$camelBackedHelper = Inflector::variable($helper);
${$camelBackedHelper} =& $loadedHelpers[$helper];
$this->loaded[$camelBackedHelper] =& ${$camelBackedHelper};
$this->{$helper} =& $loadedHelpers[$helper];
}
?>';
$content = preg_replace("/(<\\?xml)/", "<?php echo '$1';?>",$content);
$file .= $content;
return cache('views' . DS . $cache, $file, $timestamp);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,835 @@
<?php
/**
* Html Helper class file.
*
* Simplifies the construction of HTML elements.
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.helpers
* @since CakePHP(tm) v 0.9.1
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Html Helper class for easy use of HTML widgets.
*
* HtmlHelper encloses all methods needed while working with HTML pages.
*
* @package cake
* @subpackage cake.cake.libs.view.helpers
* @link http://book.cakephp.org/view/1434/HTML
*/
class HtmlHelper extends AppHelper {
/**
* html tags used by this helper.
*
* @var array
* @access public
*/
var $tags = array(
'meta' => '<meta%s/>',
'metalink' => '<link href="%s"%s/>',
'link' => '<a href="%s"%s>%s</a>',
'mailto' => '<a href="mailto:%s" %s>%s</a>',
'form' => '<form %s>',
'formend' => '</form>',
'input' => '<input name="%s" %s/>',
'textarea' => '<textarea name="%s" %s>%s</textarea>',
'hidden' => '<input type="hidden" name="%s" %s/>',
'checkbox' => '<input type="checkbox" name="%s" %s/>',
'checkboxmultiple' => '<input type="checkbox" name="%s[]"%s />',
'radio' => '<input type="radio" name="%s" id="%s" %s />%s',
'selectstart' => '<select name="%s"%s>',
'selectmultiplestart' => '<select name="%s[]"%s>',
'selectempty' => '<option value=""%s>&nbsp;</option>',
'selectoption' => '<option value="%s"%s>%s</option>',
'selectend' => '</select>',
'optiongroup' => '<optgroup label="%s"%s>',
'optiongroupend' => '</optgroup>',
'checkboxmultiplestart' => '',
'checkboxmultipleend' => '',
'password' => '<input type="password" name="%s" %s/>',
'file' => '<input type="file" name="%s" %s/>',
'file_no_model' => '<input type="file" name="%s" %s/>',
'submit' => '<input %s/>',
'submitimage' => '<input type="image" src="%s" %s/>',
'button' => '<button type="%s"%s>%s</button>',
'image' => '<img src="%s" %s/>',
'tableheader' => '<th%s>%s</th>',
'tableheaderrow' => '<tr%s>%s</tr>',
'tablecell' => '<td%s>%s</td>',
'tablerow' => '<tr%s>%s</tr>',
'block' => '<div%s>%s</div>',
'blockstart' => '<div%s>',
'blockend' => '</div>',
'tag' => '<%s%s>%s</%s>',
'tagstart' => '<%s%s>',
'tagend' => '</%s>',
'para' => '<p%s>%s</p>',
'parastart' => '<p%s>',
'label' => '<label for="%s"%s>%s</label>',
'fieldset' => '<fieldset%s>%s</fieldset>',
'fieldsetstart' => '<fieldset><legend>%s</legend>',
'fieldsetend' => '</fieldset>',
'legend' => '<legend>%s</legend>',
'css' => '<link rel="%s" type="text/css" href="%s" %s/>',
'style' => '<style type="text/css"%s>%s</style>',
'charset' => '<meta http-equiv="Content-Type" content="text/html; charset=%s" />',
'ul' => '<ul%s>%s</ul>',
'ol' => '<ol%s>%s</ol>',
'li' => '<li%s>%s</li>',
'error' => '<div%s>%s</div>',
'javascriptblock' => '<script type="text/javascript"%s>%s</script>',
'javascriptstart' => '<script type="text/javascript">',
'javascriptlink' => '<script type="text/javascript" src="%s"%s></script>',
'javascriptend' => '</script>'
);
/**
* Breadcrumbs.
*
* @var array
* @access protected
*/
var $_crumbs = array();
/**
* Names of script files that have been included once
*
* @var array
* @access private
*/
var $__includedScripts = array();
/**
* Options for the currently opened script block buffer if any.
*
* @var array
* @access protected
*/
var $_scriptBlockOptions = array();
/**
* Document type definitions
*
* @var array
* @access private
*/
var $__docTypes = array(
'html4-strict' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">',
'html4-trans' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
'html4-frame' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">',
'xhtml-strict' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
'xhtml-trans' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
'xhtml-frame' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">',
'xhtml11' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'
);
/**
* Adds a link to the breadcrumbs array.
*
* @param string $name Text for link
* @param string $link URL for link (if empty it won't be a link)
* @param mixed $options Link attributes e.g. array('id'=>'selected')
* @return void
* @see HtmlHelper::link() for details on $options that can be used.
* @access public
*/
function addCrumb($name, $link = null, $options = null) {
$this->_crumbs[] = array($name, $link, $options);
}
/**
* Returns a doctype string.
*
* Possible doctypes:
*
* - html4-strict: HTML4 Strict.
* - html4-trans: HTML4 Transitional.
* - html4-frame: HTML4 Frameset.
* - xhtml-strict: XHTML1 Strict.
* - xhtml-trans: XHTML1 Transitional.
* - xhtml-frame: XHTML1 Frameset.
* - xhtml11: XHTML1.1.
*
* @param string $type Doctype to use.
* @return string Doctype string
* @access public
* @link http://book.cakephp.org/view/1439/docType
*/
function docType($type = 'xhtml-strict') {
if (isset($this->__docTypes[$type])) {
return $this->__docTypes[$type];
}
return null;
}
/**
* Creates a link to an external resource and handles basic meta tags
*
* ### Options
*
* - `inline` Whether or not the link element should be output inline, or in scripts_for_layout.
*
* @param string $type The title of the external resource
* @param mixed $url The address of the external resource or string for content attribute
* @param array $options Other attributes for the generated tag. If the type attribute is html,
* rss, atom, or icon, the mime-type is returned.
* @return string A completed `<link />` element.
* @access public
* @link http://book.cakephp.org/view/1438/meta
*/
function meta($type, $url = null, $options = array()) {
$inline = isset($options['inline']) ? $options['inline'] : true;
unset($options['inline']);
if (!is_array($type)) {
$types = array(
'rss' => array('type' => 'application/rss+xml', 'rel' => 'alternate', 'title' => $type, 'link' => $url),
'atom' => array('type' => 'application/atom+xml', 'title' => $type, 'link' => $url),
'icon' => array('type' => 'image/x-icon', 'rel' => 'icon', 'link' => $url),
'keywords' => array('name' => 'keywords', 'content' => $url),
'description' => array('name' => 'description', 'content' => $url),
);
if ($type === 'icon' && $url === null) {
$types['icon']['link'] = $this->webroot('favicon.ico');
}
if (isset($types[$type])) {
$type = $types[$type];
} elseif (!isset($options['type']) && $url !== null) {
if (is_array($url) && isset($url['ext'])) {
$type = $types[$url['ext']];
} else {
$type = $types['rss'];
}
} elseif (isset($options['type']) && isset($types[$options['type']])) {
$type = $types[$options['type']];
unset($options['type']);
} else {
$type = array();
}
} elseif ($url !== null) {
$inline = $url;
}
$options = array_merge($type, $options);
$out = null;
if (isset($options['link'])) {
if (isset($options['rel']) && $options['rel'] === 'icon') {
$out = sprintf($this->tags['metalink'], $options['link'], $this->_parseAttributes($options, array('link'), ' ', ' '));
$options['rel'] = 'shortcut icon';
} else {
$options['link'] = $this->url($options['link'], true);
}
$out .= sprintf($this->tags['metalink'], $options['link'], $this->_parseAttributes($options, array('link'), ' ', ' '));
} else {
$out = sprintf($this->tags['meta'], $this->_parseAttributes($options, array('type'), ' ', ' '));
}
if ($inline) {
return $out;
} else {
$view =& ClassRegistry::getObject('view');
$view->addScript($out);
}
}
/**
* Returns a charset META-tag.
*
* @param string $charset The character set to be used in the meta tag. If empty,
* The App.encoding value will be used. Example: "utf-8".
* @return string A meta tag containing the specified character set.
* @access public
* @link http://book.cakephp.org/view/1436/charset
*/
function charset($charset = null) {
if (empty($charset)) {
$charset = strtolower(Configure::read('App.encoding'));
}
return sprintf($this->tags['charset'], (!empty($charset) ? $charset : 'utf-8'));
}
/**
* Creates an HTML link.
*
* If $url starts with "http://" this is treated as an external link. Else,
* it is treated as a path to controller/action and parsed with the
* HtmlHelper::url() method.
*
* If the $url is empty, $title is used instead.
*
* ### Options
*
* - `escape` Set to false to disable escaping of title and attributes.
*
* @param string $title The content to be wrapped by <a> tags.
* @param mixed $url Cake-relative URL or array of URL parameters, or external URL (starts with http://)
* @param array $options Array of HTML attributes.
* @param string $confirmMessage JavaScript confirmation message.
* @return string An `<a />` element.
* @access public
* @link http://book.cakephp.org/view/1442/link
*/
function link($title, $url = null, $options = array(), $confirmMessage = false) {
$escapeTitle = true;
if ($url !== null) {
$url = $this->url($url);
} else {
$url = $this->url($title);
$title = $url;
$escapeTitle = false;
}
if (isset($options['escape'])) {
$escapeTitle = $options['escape'];
}
if ($escapeTitle === true) {
$title = h($title);
} elseif (is_string($escapeTitle)) {
$title = htmlentities($title, ENT_QUOTES, $escapeTitle);
}
if (!empty($options['confirm'])) {
$confirmMessage = $options['confirm'];
unset($options['confirm']);
}
if ($confirmMessage) {
$confirmMessage = str_replace("'", "\'", $confirmMessage);
$confirmMessage = str_replace('"', '\"', $confirmMessage);
$options['onclick'] = "return confirm('{$confirmMessage}');";
} elseif (isset($options['default']) && $options['default'] == false) {
if (isset($options['onclick'])) {
$options['onclick'] .= ' event.returnValue = false; return false;';
} else {
$options['onclick'] = 'event.returnValue = false; return false;';
}
unset($options['default']);
}
return sprintf($this->tags['link'], $url, $this->_parseAttributes($options), $title);
}
/**
* Creates a link element for CSS stylesheets.
*
* ### Options
*
* - `inline` If set to false, the generated tag appears in the head tag of the layout. Defaults to true
*
* @param mixed $path The name of a CSS style sheet or an array containing names of
* CSS stylesheets. If `$path` is prefixed with '/', the path will be relative to the webroot
* of your application. Otherwise, the path will be relative to your CSS path, usually webroot/css.
* @param string $rel Rel attribute. Defaults to "stylesheet". If equal to 'import' the stylesheet will be imported.
* @param array $options Array of HTML attributes.
* @return string CSS <link /> or <style /> tag, depending on the type of link.
* @access public
* @link http://book.cakephp.org/view/1437/css
*/
function css($path, $rel = null, $options = array()) {
$options += array('inline' => true);
if (is_array($path)) {
$out = '';
foreach ($path as $i) {
$out .= "\n\t" . $this->css($i, $rel, $options);
}
if ($options['inline']) {
return $out . "\n";
}
return;
}
if (strpos($path, '://') !== false) {
$url = $path;
} else {
if ($path[0] !== '/') {
$path = CSS_URL . $path;
}
if (strpos($path, '?') === false) {
if (substr($path, -4) !== '.css') {
$path .= '.css';
}
}
$url = $this->assetTimestamp($this->webroot($path));
if (Configure::read('Asset.filter.css')) {
$pos = strpos($url, CSS_URL);
if ($pos !== false) {
$url = substr($url, 0, $pos) . 'ccss/' . substr($url, $pos + strlen(CSS_URL));
}
}
}
if ($rel == 'import') {
$out = sprintf($this->tags['style'], $this->_parseAttributes($options, array('inline'), '', ' '), '@import url(' . $url . ');');
} else {
if ($rel == null) {
$rel = 'stylesheet';
}
$out = sprintf($this->tags['css'], $rel, $url, $this->_parseAttributes($options, array('inline'), '', ' '));
}
if ($options['inline']) {
return $out;
} else {
$view =& ClassRegistry::getObject('view');
$view->addScript($out);
}
}
/**
* Returns one or many `<script>` tags depending on the number of scripts given.
*
* If the filename is prefixed with "/", the path will be relative to the base path of your
* application. Otherwise, the path will be relative to your JavaScript path, usually webroot/js.
*
* Can include one or many Javascript files.
*
* ### Options
*
* - `inline` - Whether script should be output inline or into scripts_for_layout.
* - `once` - Whether or not the script should be checked for uniqueness. If true scripts will only be
* included once, use false to allow the same script to be included more than once per request.
*
* @param mixed $url String or array of javascript files to include
* @param mixed $options Array of options, and html attributes see above. If boolean sets $options['inline'] = value
* @return mixed String of `<script />` tags or null if $inline is false or if $once is true and the file has been
* included before.
* @access public
* @link http://book.cakephp.org/view/1589/script
*/
function script($url, $options = array()) {
if (is_bool($options)) {
list($inline, $options) = array($options, array());
$options['inline'] = $inline;
}
$options = array_merge(array('inline' => true, 'once' => true), $options);
if (is_array($url)) {
$out = '';
foreach ($url as $i) {
$out .= "\n\t" . $this->script($i, $options);
}
if ($options['inline']) {
return $out . "\n";
}
return null;
}
if ($options['once'] && isset($this->__includedScripts[$url])) {
return null;
}
$this->__includedScripts[$url] = true;
if (strpos($url, '://') === false) {
if ($url[0] !== '/') {
$url = JS_URL . $url;
}
if (strpos($url, '?') === false && substr($url, -3) !== '.js') {
$url .= '.js';
}
$url = $this->assetTimestamp($this->webroot($url));
if (Configure::read('Asset.filter.js')) {
$url = str_replace(JS_URL, 'cjs/', $url);
}
}
$attributes = $this->_parseAttributes($options, array('inline', 'once'), ' ');
$out = sprintf($this->tags['javascriptlink'], $url, $attributes);
if ($options['inline']) {
return $out;
} else {
$view =& ClassRegistry::getObject('view');
$view->addScript($out);
}
}
/**
* Wrap $script in a script tag.
*
* ### Options
*
* - `safe` (boolean) Whether or not the $script should be wrapped in <![CDATA[ ]]>
* - `inline` (boolean) Whether or not the $script should be added to $scripts_for_layout or output inline
*
* @param string $script The script to wrap
* @param array $options The options to use.
* @return mixed string or null depending on the value of `$options['inline']`
* @access public
* @link http://book.cakephp.org/view/1604/scriptBlock
*/
function scriptBlock($script, $options = array()) {
$options += array('safe' => true, 'inline' => true);
if ($options['safe']) {
$script = "\n" . '//<![CDATA[' . "\n" . $script . "\n" . '//]]>' . "\n";
}
$inline = $options['inline'];
unset($options['inline'], $options['safe']);
$attributes = $this->_parseAttributes($options, ' ', ' ');
if ($inline) {
return sprintf($this->tags['javascriptblock'], $attributes, $script);
} else {
$view =& ClassRegistry::getObject('view');
$view->addScript(sprintf($this->tags['javascriptblock'], $attributes, $script));
return null;
}
}
/**
* Begin a script block that captures output until HtmlHelper::scriptEnd()
* is called. This capturing block will capture all output between the methods
* and create a scriptBlock from it.
*
* ### Options
*
* - `safe` Whether the code block should contain a CDATA
* - `inline` Should the generated script tag be output inline or in `$scripts_for_layout`
*
* @param array $options Options for the code block.
* @return void
* @access public
* @link http://book.cakephp.org/view/1605/scriptStart
*/
function scriptStart($options = array()) {
$options += array('safe' => true, 'inline' => true);
$this->_scriptBlockOptions = $options;
ob_start();
return null;
}
/**
* End a Buffered section of Javascript capturing.
* Generates a script tag inline or in `$scripts_for_layout` depending on the settings
* used when the scriptBlock was started
*
* @return mixed depending on the settings of scriptStart() either a script tag or null
* @access public
* @link http://book.cakephp.org/view/1606/scriptEnd
*/
function scriptEnd() {
$buffer = ob_get_clean();
$options = $this->_scriptBlockOptions;
$this->_scriptBlockOptions = array();
return $this->scriptBlock($buffer, $options);
}
/**
* Builds CSS style data from an array of CSS properties
*
* ### Usage:
*
* {{{
* echo $html->style(array('margin' => '10px', 'padding' => '10px'), true);
*
* // creates
* 'margin:10px;padding:10px;'
* }}}
*
* @param array $data Style data array, keys will be used as property names, values as property values.
* @param boolean $oneline Whether or not the style block should be displayed on one line.
* @return string CSS styling data
* @access public
* @link http://book.cakephp.org/view/1440/style
*/
function style($data, $oneline = true) {
if (!is_array($data)) {
return $data;
}
$out = array();
foreach ($data as $key=> $value) {
$out[] = $key.':'.$value.';';
}
if ($oneline) {
return join(' ', $out);
}
return implode("\n", $out);
}
/**
* Returns the breadcrumb trail as a sequence of &raquo;-separated links.
*
* @param string $separator Text to separate crumbs.
* @param string $startText This will be the first crumb, if false it defaults to first crumb in array
* @return string Composed bread crumbs
* @access public
*/
function getCrumbs($separator = '&raquo;', $startText = false) {
if (!empty($this->_crumbs)) {
$out = array();
if ($startText) {
$out[] = $this->link($startText, '/');
}
foreach ($this->_crumbs as $crumb) {
if (!empty($crumb[1])) {
$out[] = $this->link($crumb[0], $crumb[1], $crumb[2]);
} else {
$out[] = $crumb[0];
}
}
return join($separator, $out);
} else {
return null;
}
}
/**
* Creates a formatted IMG element. If `$options['url']` is provided, an image link will be
* generated with the link pointed at `$options['url']`. This method will set an empty
* alt attribute if one is not supplied.
*
* ### Usage
*
* Create a regular image:
*
* `echo $html->image('cake_icon.png', array('alt' => 'CakePHP'));`
*
* Create an image link:
*
* `echo $html->image('cake_icon.png', array('alt' => 'CakePHP', 'url' => 'http://cakephp.org'));`
*
* @param string $path Path to the image file, relative to the app/webroot/img/ directory.
* @param array $options Array of HTML attributes.
* @return string completed img tag
* @access public
* @link http://book.cakephp.org/view/1441/image
*/
function image($path, $options = array()) {
if (is_array($path)) {
$path = $this->url($path);
} elseif (strpos($path, '://') === false) {
if ($path[0] !== '/') {
$path = IMAGES_URL . $path;
}
$path = $this->assetTimestamp($this->webroot($path));
}
if (!isset($options['alt'])) {
$options['alt'] = '';
}
$url = false;
if (!empty($options['url'])) {
$url = $options['url'];
unset($options['url']);
}
$image = sprintf($this->tags['image'], $path, $this->_parseAttributes($options, null, '', ' '));
if ($url) {
return sprintf($this->tags['link'], $this->url($url), null, $image);
}
return $image;
}
/**
* Returns a row of formatted and named TABLE headers.
*
* @param array $names Array of tablenames.
* @param array $trOptions HTML options for TR elements.
* @param array $thOptions HTML options for TH elements.
* @return string Completed table headers
* @access public
* @link http://book.cakephp.org/view/1446/tableHeaders
*/
function tableHeaders($names, $trOptions = null, $thOptions = null) {
$out = array();
foreach ($names as $arg) {
$out[] = sprintf($this->tags['tableheader'], $this->_parseAttributes($thOptions), $arg);
}
return sprintf($this->tags['tablerow'], $this->_parseAttributes($trOptions), join(' ', $out));
}
/**
* Returns a formatted string of table rows (TR's with TD's in them).
*
* @param array $data Array of table data
* @param array $oddTrOptions HTML options for odd TR elements if true useCount is used
* @param array $evenTrOptions HTML options for even TR elements
* @param bool $useCount adds class "column-$i"
* @param bool $continueOddEven If false, will use a non-static $count variable,
* so that the odd/even count is reset to zero just for that call.
* @return string Formatted HTML
* @access public
* @link http://book.cakephp.org/view/1447/tableCells
*/
function tableCells($data, $oddTrOptions = null, $evenTrOptions = null, $useCount = false, $continueOddEven = true) {
if (empty($data[0]) || !is_array($data[0])) {
$data = array($data);
}
if ($oddTrOptions === true) {
$useCount = true;
$oddTrOptions = null;
}
if ($evenTrOptions === false) {
$continueOddEven = false;
$evenTrOptions = null;
}
if ($continueOddEven) {
static $count = 0;
} else {
$count = 0;
}
foreach ($data as $line) {
$count++;
$cellsOut = array();
$i = 0;
foreach ($line as $cell) {
$cellOptions = array();
if (is_array($cell)) {
$cellOptions = $cell[1];
$cell = $cell[0];
} elseif ($useCount) {
$cellOptions['class'] = 'column-' . ++$i;
}
$cellsOut[] = sprintf($this->tags['tablecell'], $this->_parseAttributes($cellOptions), $cell);
}
$options = $this->_parseAttributes($count % 2 ? $oddTrOptions : $evenTrOptions);
$out[] = sprintf($this->tags['tablerow'], $options, implode(' ', $cellsOut));
}
return implode("\n", $out);
}
/**
* Returns a formatted block tag, i.e DIV, SPAN, P.
*
* ### Options
*
* - `escape` Whether or not the contents should be html_entity escaped.
*
* @param string $name Tag name.
* @param string $text String content that will appear inside the div element.
* If null, only a start tag will be printed
* @param array $options Additional HTML attributes of the DIV tag, see above.
* @return string The formatted tag element
* @access public
* @link http://book.cakephp.org/view/1443/tag
*/
function tag($name, $text = null, $options = array()) {
if (is_array($options) && isset($options['escape']) && $options['escape']) {
$text = h($text);
unset($options['escape']);
}
if (!is_array($options)) {
$options = array('class' => $options);
}
if ($text === null) {
$tag = 'tagstart';
} else {
$tag = 'tag';
}
return sprintf($this->tags[$tag], $name, $this->_parseAttributes($options, null, ' ', ''), $text, $name);
}
/**
* Returns a formatted DIV tag for HTML FORMs.
*
* ### Options
*
* - `escape` Whether or not the contents should be html_entity escaped.
*
* @param string $class CSS class name of the div element.
* @param string $text String content that will appear inside the div element.
* If null, only a start tag will be printed
* @param array $options Additional HTML attributes of the DIV tag
* @return string The formatted DIV element
* @access public
* @link http://book.cakephp.org/view/1444/div
*/
function div($class = null, $text = null, $options = array()) {
if (!empty($class)) {
$options['class'] = $class;
}
return $this->tag('div', $text, $options);
}
/**
* Returns a formatted P tag.
*
* ### Options
*
* - `escape` Whether or not the contents should be html_entity escaped.
*
* @param string $class CSS class name of the p element.
* @param string $text String content that will appear inside the p element.
* @param array $options Additional HTML attributes of the P tag
* @return string The formatted P element
* @access public
* @link http://book.cakephp.org/view/1445/para
*/
function para($class, $text, $options = array()) {
if (isset($options['escape'])) {
$text = h($text);
}
if ($class != null && !empty($class)) {
$options['class'] = $class;
}
if ($text === null) {
$tag = 'parastart';
} else {
$tag = 'para';
}
return sprintf($this->tags[$tag], $this->_parseAttributes($options, null, ' ', ''), $text);
}
/**
* Build a nested list (UL/OL) out of an associative array.
*
* @param array $list Set of elements to list
* @param array $options Additional HTML attributes of the list (ol/ul) tag or if ul/ol use that as tag
* @param array $itemOptions Additional HTML attributes of the list item (LI) tag
* @param string $tag Type of list tag to use (ol/ul)
* @return string The nested list
* @access public
*/
function nestedList($list, $options = array(), $itemOptions = array(), $tag = 'ul') {
if (is_string($options)) {
$tag = $options;
$options = array();
}
$items = $this->__nestedListItem($list, $options, $itemOptions, $tag);
return sprintf($this->tags[$tag], $this->_parseAttributes($options, null, ' ', ''), $items);
}
/**
* Internal function to build a nested list (UL/OL) out of an associative array.
*
* @param array $list Set of elements to list
* @param array $options Additional HTML attributes of the list (ol/ul) tag
* @param array $itemOptions Additional HTML attributes of the list item (LI) tag
* @param string $tag Type of list tag to use (ol/ul)
* @return string The nested list element
* @access private
* @see HtmlHelper::nestedList()
*/
function __nestedListItem($items, $options, $itemOptions, $tag) {
$out = '';
$index = 1;
foreach ($items as $key => $item) {
if (is_array($item)) {
$item = $key . $this->nestedList($item, $options, $itemOptions, $tag);
}
if (isset($itemOptions['even']) && $index % 2 == 0) {
$itemOptions['class'] = $itemOptions['even'];
} else if (isset($itemOptions['odd']) && $index % 2 != 0) {
$itemOptions['class'] = $itemOptions['odd'];
}
$out .= sprintf($this->tags['li'], $this->_parseAttributes($itemOptions, array('even', 'odd'), ' ', ''), $item);
$index++;
}
return $out;
}
}

View File

@@ -0,0 +1,721 @@
<?php
/**
* Javascript Helper class file.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.helpers
* @since CakePHP(tm) v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Javascript Helper class for easy use of JavaScript.
*
* JavascriptHelper encloses all methods needed while working with JavaScript.
*
* @package cake
* @subpackage cake.cake.libs.view.helpers
* @link http://book.cakephp.org/view/1450/Javascript
*/
class JavascriptHelper extends AppHelper {
/**
* Determines whether native JSON extension is used for encoding. Set by object constructor.
*
* @var boolean
* @access public
*/
var $useNative = false;
/**
* If true, automatically writes events to the end of a script or to an external JavaScript file
* at the end of page execution
*
* @var boolean
* @access public
*/
var $enabled = true;
/**
* Indicates whether <script /> blocks should be written 'safely,' i.e. wrapped in CDATA blocks
*
* @var boolean
* @access public
*/
var $safe = false;
/**
* HTML tags used by this helper.
*
* @var array
* @access public
*/
var $tags = array(
'javascriptstart' => '<script type="text/javascript">',
'javascriptend' => '</script>',
'javascriptblock' => '<script type="text/javascript">%s</script>',
'javascriptlink' => '<script type="text/javascript" src="%s"></script>'
);
/**
* Holds options passed to codeBlock(), saved for when block is dumped to output
*
* @var array
* @access protected
* @see JavascriptHelper::codeBlock()
*/
var $_blockOptions = array();
/**
* Caches events written by event() for output at the end of page execution
*
* @var array
* @access protected
* @see JavascriptHelper::event()
*/
var $_cachedEvents = array();
/**
* Indicates whether generated events should be cached for later output (can be written at the
* end of the page, in the <head />, or to an external file).
*
* @var boolean
* @access protected
* @see JavascriptHelper::event()
* @see JavascriptHelper::writeEvents()
*/
var $_cacheEvents = false;
/**
* Indicates whether cached events should be written to an external file
*
* @var boolean
* @access protected
* @see JavascriptHelper::event()
* @see JavascriptHelper::writeEvents()
*/
var $_cacheToFile = false;
/**
* Indicates whether *all* generated JavaScript should be cached for later output
*
* @var boolean
* @access protected
* @see JavascriptHelper::codeBlock()
* @see JavascriptHelper::blockEnd()
*/
var $_cacheAll = false;
/**
* Contains event rules attached with CSS selectors. Used with the event:Selectors JavaScript
* library.
*
* @var array
* @access protected
* @see JavascriptHelper::event()
* @link http://alternateidea.com/event-selectors/
*/
var $_rules = array();
/**
* @var string
* @access private
*/
var $__scriptBuffer = null;
/**
* Constructor. Checks for presence of native PHP JSON extension to use for object encoding
*
* @access public
*/
function __construct($options = array()) {
if (!empty($options)) {
foreach ($options as $key => $val) {
if (is_numeric($key)) {
$key = $val;
$val = true;
}
switch ($key) {
case 'cache':
break;
case 'safe':
$this->safe = $val;
break;
}
}
}
$this->useNative = function_exists('json_encode');
return parent::__construct($options);
}
/**
* Returns a JavaScript script tag.
*
* Options:
*
* - allowCache: boolean, designates whether this block is cacheable using the
* current cache settings.
* - safe: boolean, whether this block should be wrapped in CDATA tags. Defaults
* to helper's object configuration.
* - inline: whether the block should be printed inline, or written
* to cached for later output (i.e. $scripts_for_layout).
*
* @param string $script The JavaScript to be wrapped in SCRIPT tags.
* @param array $options Set of options:
* @return string The full SCRIPT element, with the JavaScript inside it, or null,
* if 'inline' is set to false.
*/
function codeBlock($script = null, $options = array()) {
if (!empty($options) && !is_array($options)) {
$options = array('allowCache' => $options);
} elseif (empty($options)) {
$options = array();
}
$defaultOptions = array('allowCache' => true, 'safe' => true, 'inline' => true);
$options = array_merge($defaultOptions, $options);
if (empty($script)) {
$this->__scriptBuffer = @ob_get_contents();
$this->_blockOptions = $options;
$this->inBlock = true;
@ob_end_clean();
ob_start();
return null;
}
if ($this->_cacheEvents && $this->_cacheAll && $options['allowCache']) {
$this->_cachedEvents[] = $script;
return null;
}
if ($options['safe'] || $this->safe) {
$script = "\n" . '//<![CDATA[' . "\n" . $script . "\n" . '//]]>' . "\n";
}
if ($options['inline']) {
return sprintf($this->tags['javascriptblock'], $script);
} else {
$view =& ClassRegistry::getObject('view');
$view->addScript(sprintf($this->tags['javascriptblock'], $script));
}
}
/**
* Ends a block of cached JavaScript code
*
* @return mixed
*/
function blockEnd() {
if (!isset($this->inBlock) || !$this->inBlock) {
return;
}
$script = @ob_get_contents();
@ob_end_clean();
ob_start();
echo $this->__scriptBuffer;
$this->__scriptBuffer = null;
$options = $this->_blockOptions;
$this->_blockOptions = array();
$this->inBlock = false;
if (empty($script)) {
return null;
}
return $this->codeBlock($script, $options);
}
/**
* Returns a JavaScript include tag (SCRIPT element). If the filename is prefixed with "/",
* the path will be relative to the base path of your application. Otherwise, the path will
* be relative to your JavaScript path, usually webroot/js.
*
* @param mixed $url String URL to JavaScript file, or an array of URLs.
* @param boolean $inline If true, the <script /> tag will be printed inline,
* otherwise it will be printed in the <head />, using $scripts_for_layout
* @see JS_URL
* @return string
*/
function link($url, $inline = true) {
if (is_array($url)) {
$out = '';
foreach ($url as $i) {
$out .= "\n\t" . $this->link($i, $inline);
}
if ($inline) {
return $out . "\n";
}
return;
}
if (strpos($url, '://') === false) {
if ($url[0] !== '/') {
$url = JS_URL . $url;
}
if (strpos($url, '?') === false) {
if (substr($url, -3) !== '.js') {
$url .= '.js';
}
}
$url = $this->assetTimestamp($this->webroot($url));
if (Configure::read('Asset.filter.js')) {
$pos = strpos($url, JS_URL);
if ($pos !== false) {
$url = substr($url, 0, $pos) . 'cjs/' . substr($url, $pos + strlen(JS_URL));
}
}
}
$out = sprintf($this->tags['javascriptlink'], $url);
if ($inline) {
return $out;
} else {
$view =& ClassRegistry::getObject('view');
$view->addScript($out);
}
}
/**
* Escape carriage returns and single and double quotes for JavaScript segments.
*
* @param string $script string that might have javascript elements
* @return string escaped string
*/
function escapeScript($script) {
$script = str_replace(array("\r\n", "\n", "\r"), '\n', $script);
$script = str_replace(array('"', "'"), array('\"', "\\'"), $script);
return $script;
}
/**
* Escape a string to be JavaScript friendly.
*
* List of escaped ellements:
* + "\r\n" => '\n'
* + "\r" => '\n'
* + "\n" => '\n'
* + '"' => '\"'
* + "'" => "\\'"
*
* @param string $script String that needs to get escaped.
* @return string Escaped string.
*/
function escapeString($string) {
App::import('Core', 'Multibyte');
$escape = array("\r\n" => "\n", "\r" => "\n");
$string = str_replace(array_keys($escape), array_values($escape), $string);
return $this->_utf8ToHex($string);
}
/**
* Encode a string into JSON. Converts and escapes necessary characters.
*
* @return void
*/
function _utf8ToHex($string) {
$length = strlen($string);
$return = '';
for ($i = 0; $i < $length; ++$i) {
$ord = ord($string{$i});
switch (true) {
case $ord == 0x08:
$return .= '\b';
break;
case $ord == 0x09:
$return .= '\t';
break;
case $ord == 0x0A:
$return .= '\n';
break;
case $ord == 0x0C:
$return .= '\f';
break;
case $ord == 0x0D:
$return .= '\r';
break;
case $ord == 0x22:
case $ord == 0x2F:
case $ord == 0x5C:
case $ord == 0x27:
$return .= '\\' . $string{$i};
break;
case (($ord >= 0x20) && ($ord <= 0x7F)):
$return .= $string{$i};
break;
case (($ord & 0xE0) == 0xC0):
if ($i + 1 >= $length) {
$i += 1;
$return .= '?';
break;
}
$charbits = $string{$i} . $string{$i + 1};
$char = Multibyte::utf8($charbits);
$return .= sprintf('\u%04s', dechex($char[0]));
$i += 1;
break;
case (($ord & 0xF0) == 0xE0):
if ($i + 2 >= $length) {
$i += 2;
$return .= '?';
break;
}
$charbits = $string{$i} . $string{$i + 1} . $string{$i + 2};
$char = Multibyte::utf8($charbits);
$return .= sprintf('\u%04s', dechex($char[0]));
$i += 2;
break;
case (($ord & 0xF8) == 0xF0):
if ($i + 3 >= $length) {
$i += 3;
$return .= '?';
break;
}
$charbits = $string{$i} . $string{$i + 1} . $string{$i + 2} . $string{$i + 3};
$char = Multibyte::utf8($charbits);
$return .= sprintf('\u%04s', dechex($char[0]));
$i += 3;
break;
case (($ord & 0xFC) == 0xF8):
if ($i + 4 >= $length) {
$i += 4;
$return .= '?';
break;
}
$charbits = $string{$i} . $string{$i + 1} . $string{$i + 2} . $string{$i + 3} . $string{$i + 4};
$char = Multibyte::utf8($charbits);
$return .= sprintf('\u%04s', dechex($char[0]));
$i += 4;
break;
case (($ord & 0xFE) == 0xFC):
if ($i + 5 >= $length) {
$i += 5;
$return .= '?';
break;
}
$charbits = $string{$i} . $string{$i + 1} . $string{$i + 2} . $string{$i + 3} . $string{$i + 4} . $string{$i + 5};
$char = Multibyte::utf8($charbits);
$return .= sprintf('\u%04s', dechex($char[0]));
$i += 5;
break;
}
}
return $return;
}
/**
* Attach an event to an element. Used with the Prototype library.
*
* @param string $object Object to be observed
* @param string $event event to observe
* @param string $observer function to call
* @param array $options Set options: useCapture, allowCache, safe
* @return boolean true on success
*/
function event($object, $event, $observer = null, $options = array()) {
if (!empty($options) && !is_array($options)) {
$options = array('useCapture' => $options);
} else if (empty($options)) {
$options = array();
}
$defaultOptions = array('useCapture' => false);
$options = array_merge($defaultOptions, $options);
if ($options['useCapture'] == true) {
$options['useCapture'] = 'true';
} else {
$options['useCapture'] = 'false';
}
$isObject = (
strpos($object, 'window') !== false || strpos($object, 'document') !== false ||
strpos($object, '$(') !== false || strpos($object, '"') !== false ||
strpos($object, '\'') !== false
);
if ($isObject) {
$b = "Event.observe({$object}, '{$event}', function(event) { {$observer} }, ";
$b .= "{$options['useCapture']});";
} elseif ($object[0] == '\'') {
$b = "Event.observe(" . substr($object, 1) . ", '{$event}', function(event) { ";
$b .= "{$observer} }, {$options['useCapture']});";
} else {
$chars = array('#', ' ', ', ', '.', ':');
$found = false;
foreach ($chars as $char) {
if (strpos($object, $char) !== false) {
$found = true;
break;
}
}
if ($found) {
$this->_rules[$object] = $event;
} else {
$b = "Event.observe(\$('{$object}'), '{$event}', function(event) { ";
$b .= "{$observer} }, {$options['useCapture']});";
}
}
if (isset($b) && !empty($b)) {
if ($this->_cacheEvents === true) {
$this->_cachedEvents[] = $b;
return;
} else {
return $this->codeBlock($b, array_diff_key($options, $defaultOptions));
}
}
}
/**
* Cache JavaScript events created with event()
*
* @param boolean $file If true, code will be written to a file
* @param boolean $all If true, all code written with JavascriptHelper will be sent to a file
* @return null
*/
function cacheEvents($file = false, $all = false) {
$this->_cacheEvents = true;
$this->_cacheToFile = $file;
$this->_cacheAll = $all;
}
/**
* Gets (and clears) the current JavaScript event cache
*
* @param boolean $clear
* @return string
*/
function getCache($clear = true) {
$out = '';
$rules = array();
if (!empty($this->_rules)) {
foreach ($this->_rules as $sel => $event) {
$rules[] = "\t'{$sel}': function(element, event) {\n\t\t{$event}\n\t}";
}
}
$data = implode("\n", $this->_cachedEvents);
if (!empty($rules)) {
$data .= "\nvar Rules = {\n" . implode(",\n\n", $rules) . "\n}";
$data .= "\nEventSelectors.start(Rules);\n";
}
if ($clear) {
$this->_rules = array();
$this->_cacheEvents = false;
$this->_cachedEvents = array();
}
return $data;
}
/**
* Write cached JavaScript events
*
* @param boolean $inline If true, returns JavaScript event code. Otherwise it is added to the
* output of $scripts_for_layout in the layout.
* @param array $options Set options for codeBlock
* @return string
*/
function writeEvents($inline = true, $options = array()) {
$out = '';
$rules = array();
if (!$this->_cacheEvents) {
return;
}
$data = $this->getCache();
if (empty($data)) {
return;
}
if ($this->_cacheToFile) {
$filename = md5($data);
if (!file_exists(JS . $filename . '.js')) {
cache(str_replace(WWW_ROOT, '', JS) . $filename . '.js', $data, '+999 days', 'public');
}
$out = $this->link($filename);
} else {
$out = $this->codeBlock("\n" . $data . "\n", $options);
}
if ($inline) {
return $out;
} else {
$view =& ClassRegistry::getObject('view');
$view->addScript($out);
}
}
/**
* Includes the Prototype Javascript library (and anything else) inside a single script tag.
*
* Note: The recommended approach is to copy the contents of
* javascripts into your application's
* public/javascripts/ directory, and use @see javascriptIncludeTag() to
* create remote script links.
*
* @param string $script Script file to include
* @param array $options Set options for codeBlock
* @return string script with all javascript in/javascripts folder
*/
function includeScript($script = "", $options = array()) {
if ($script == "") {
$files = scandir(JS);
$javascript = '';
foreach ($files as $file) {
if (substr($file, -3) == '.js') {
$javascript .= file_get_contents(JS . "{$file}") . "\n\n";
}
}
} else {
$javascript = file_get_contents(JS . "$script.js") . "\n\n";
}
return $this->codeBlock("\n\n" . $javascript, $options);
}
/**
* Generates a JavaScript object in JavaScript Object Notation (JSON)
* from an array
*
* ### Options
*
* - block - Wraps the return value in a script tag if true. Default is false
* - prefix - Prepends the string to the returned data. Default is ''
* - postfix - Appends the string to the returned data. Default is ''
* - stringKeys - A list of array keys to be treated as a string.
* - quoteKeys - If false treats $stringKeys as a list of keys **not** to be quoted. Default is true.
* - q - The type of quote to use. Default is '"'. This option only affects the keys, not the values.
*
* @param array $data Data to be converted
* @param array $options Set of options: block, prefix, postfix, stringKeys, quoteKeys, q
* @return string A JSON code block
*/
function object($data = array(), $options = array()) {
if (!empty($options) && !is_array($options)) {
$options = array('block' => $options);
} else if (empty($options)) {
$options = array();
}
$defaultOptions = array(
'block' => false, 'prefix' => '', 'postfix' => '',
'stringKeys' => array(), 'quoteKeys' => true, 'q' => '"'
);
$options = array_merge($defaultOptions, $options, array_filter(compact(array_keys($defaultOptions))));
if (is_object($data)) {
$data = get_object_vars($data);
}
$out = $keys = array();
$numeric = true;
if ($this->useNative) {
$rt = json_encode($data);
} else {
if (is_null($data)) {
return 'null';
}
if (is_bool($data)) {
return $data ? 'true' : 'false';
}
if (is_array($data)) {
$keys = array_keys($data);
}
if (!empty($keys)) {
$numeric = (array_values($keys) === array_keys(array_values($keys)));
}
foreach ($data as $key => $val) {
if (is_array($val) || is_object($val)) {
$val = $this->object(
$val,
array_merge($options, array('block' => false, 'prefix' => '', 'postfix' => ''))
);
} else {
$quoteStrings = (
!count($options['stringKeys']) ||
($options['quoteKeys'] && in_array($key, $options['stringKeys'], true)) ||
(!$options['quoteKeys'] && !in_array($key, $options['stringKeys'], true))
);
$val = $this->value($val, $quoteStrings);
}
if (!$numeric) {
$val = $options['q'] . $this->value($key, false) . $options['q'] . ':' . $val;
}
$out[] = $val;
}
if (!$numeric) {
$rt = '{' . implode(',', $out) . '}';
} else {
$rt = '[' . implode(',', $out) . ']';
}
}
$rt = $options['prefix'] . $rt . $options['postfix'];
if ($options['block']) {
$rt = $this->codeBlock($rt, array_diff_key($options, $defaultOptions));
}
return $rt;
}
/**
* Converts a PHP-native variable of any type to a JSON-equivalent representation
*
* @param mixed $val A PHP variable to be converted to JSON
* @param boolean $quoteStrings If false, leaves string values unquoted
* @return string a JavaScript-safe/JSON representation of $val
*/
function value($val, $quoteStrings = true) {
switch (true) {
case (is_array($val) || is_object($val)):
$val = $this->object($val);
break;
case ($val === null):
$val = 'null';
break;
case (is_bool($val)):
$val = !empty($val) ? 'true' : 'false';
break;
case (is_int($val)):
$val = $val;
break;
case (is_float($val)):
$val = sprintf("%.11f", $val);
break;
default:
$val = $this->escapeString($val);
if ($quoteStrings) {
$val = '"' . $val . '"';
}
break;
}
return $val;
}
/**
* AfterRender callback. Writes any cached events to the view, or to a temp file.
*
* @return null
*/
function afterRender() {
if (!$this->enabled) {
return;
}
echo $this->writeEvents(true);
}
}

View File

@@ -0,0 +1,363 @@
<?php
/**
* jQuery Engine Helper for JsHelper
*
* Provides jQuery specific Javascript for JsHelper.
*
* Implements the JsHelper interface for jQuery. All $options arrays
* support all options found in the JsHelper, as well as those in the jQuery
* documentation.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2006-2010, Cake Software Foundation, Inc.
* @link http://cakephp.org CakePHP Project
* @package cake
* @subpackage cake.view.helpers
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Helper', 'Js');
class JqueryEngineHelper extends JsBaseEngineHelper {
/**
* Option mappings for jQuery
*
* @var array
* @access protected
*/
var $_optionMap = array(
'request' => array(
'type' => 'dataType',
'before' => 'beforeSend',
'method' => 'type',
),
'sortable' => array(
'complete' => 'stop',
),
'drag' => array(
'snapGrid' => 'grid',
'container' => 'containment',
),
'drop' => array(
'leave' => 'out',
'hover' => 'over'
),
'slider' => array(
'complete' => 'stop',
'direction' => 'orientation'
)
);
/**
* Callback arguments lists
*
* @var string
* @access protected
*/
var $_callbackArguments = array(
'slider' => array(
'start' => 'event, ui',
'slide' => 'event, ui',
'change' => 'event, ui',
'stop' => 'event, ui'
),
'sortable' => array(
'start' => 'event, ui',
'sort' => 'event, ui',
'change' => 'event, ui',
'beforeStop' => 'event, ui',
'stop' => 'event, ui',
'update' => 'event, ui',
'receive' => 'event, ui',
'remove' => 'event, ui',
'over' => 'event, ui',
'out' => 'event, ui',
'activate' => 'event, ui',
'deactivate' => 'event, ui'
),
'drag' => array(
'start' => 'event, ui',
'drag' => 'event, ui',
'stop' => 'event, ui',
),
'drop' => array(
'activate' => 'event, ui',
'deactivate' => 'event, ui',
'over' => 'event, ui',
'out' => 'event, ui',
'drop' => 'event, ui'
),
'request' => array(
'beforeSend' => 'XMLHttpRequest',
'error' => 'XMLHttpRequest, textStatus, errorThrown',
'success' => 'data, textStatus',
'complete' => 'XMLHttpRequest, textStatus',
'xhr' => ''
)
);
/**
* The variable name of the jQuery Object, useful
* when jQuery is put into noConflict() mode.
*
* @var string
* @access public
*/
var $jQueryObject = '$';
/**
* Helper function to wrap repetitive simple method templating.
*
* @param string $method The method name being generated.
* @param string $template The method template
* @param string $selection the selection to apply
* @param string $options Array of options for method
* @param string $callbacks Array of callback / special options.
* @return string Composed method string
* @access public
*/
function _methodTemplate($method, $template, $options, $extraSafeKeys = array()) {
$options = $this->_mapOptions($method, $options);
$options = $this->_prepareCallbacks($method, $options);
$callbacks = array_keys($this->_callbackArguments[$method]);
if (!empty($extraSafeKeys)) {
$callbacks = array_merge($callbacks, $extraSafeKeys);
}
$options = $this->_parseOptions($options, $callbacks);
return sprintf($template, $this->selection, $options);
}
/**
* Create javascript selector for a CSS rule
*
* @param string $selector The selector that is targeted
* @return object instance of $this. Allows chained methods.
* @access public
*/
function get($selector) {
if ($selector == 'window' || $selector == 'document') {
$this->selection = $this->jQueryObject . '(' . $selector .')';
} else {
$this->selection = $this->jQueryObject . '("' . $selector . '")';
}
return $this;
}
/**
* Add an event to the script cache. Operates on the currently selected elements.
*
* ### Options
*
* - 'wrap' - Whether you want the callback wrapped in an anonymous function. (defaults true)
* - 'stop' - Whether you want the event to stopped. (defaults true)
*
* @param string $type Type of event to bind to the current dom id
* @param string $callback The Javascript function you wish to trigger or the function literal
* @param array $options Options for the event.
* @return string completed event handler
* @access public
*/
function event($type, $callback, $options = array()) {
$defaults = array('wrap' => true, 'stop' => true);
$options = array_merge($defaults, $options);
$function = 'function (event) {%s}';
if ($options['wrap'] && $options['stop']) {
$callback .= "\nreturn false;";
}
if ($options['wrap']) {
$callback = sprintf($function, $callback);
}
return sprintf('%s.bind("%s", %s);', $this->selection, $type, $callback);
}
/**
* Create a domReady event. For jQuery. This method does not
* bind a 'traditional event' as `$(document).bind('ready', fn)`
* Works in an entirely different fashion than `$(document).ready()`
* The first will not run the function when eval()'d as part of a response
* The second will. Because of the way that ajax pagination is done
* `$().ready()` is used.
*
* @param string $functionBody The code to run on domReady
* @return string completed domReady method
* @access public
*/
function domReady($functionBody) {
return $this->jQueryObject . '(document).ready(function () {' . $functionBody . '});';
}
/**
* Create an iteration over the current selection result.
*
* @param string $method The method you want to apply to the selection
* @param string $callback The function body you wish to apply during the iteration.
* @return string completed iteration
* @access public
*/
function each($callback) {
return $this->selection . '.each(function () {' . $callback . '});';
}
/**
* Trigger an Effect.
*
* @param string $name The name of the effect to trigger.
* @param array $options Array of options for the effect.
* @return string completed string with effect.
* @access public
* @see JsBaseEngineHelper::effect()
*/
function effect($name, $options = array()) {
$speed = null;
if (isset($options['speed']) && in_array($options['speed'], array('fast', 'slow'))) {
$speed = $this->value($options['speed']);
}
$effect = '';
switch ($name) {
case 'slideIn':
case 'slideOut':
$name = ($name == 'slideIn') ? 'slideDown' : 'slideUp';
case 'hide':
case 'show':
case 'fadeIn':
case 'fadeOut':
case 'slideDown':
case 'slideUp':
$effect = ".$name($speed);";
break;
}
return $this->selection . $effect;
}
/**
* Create an $.ajax() call.
*
* If the 'update' key is set, success callback will be overridden.
*
* @param mixed $url
* @param array $options See JsHelper::request() for options.
* @return string The completed ajax call.
* @access public
* @see JsBaseEngineHelper::request() for options list.
*/
function request($url, $options = array()) {
$url = $this->url($url);
$options = $this->_mapOptions('request', $options);
if (isset($options['data']) && is_array($options['data'])) {
$options['data'] = $this->_toQuerystring($options['data']);
}
$options['url'] = $url;
if (isset($options['update'])) {
$wrapCallbacks = isset($options['wrapCallbacks']) ? $options['wrapCallbacks'] : true;
$success = '';
if(isset($options['success']) AND !empty($options['success'])) {
$success .= $options['success'];
}
$success .= $this->jQueryObject . '("' . $options['update'] . '").html(data);';
if (!$wrapCallbacks) {
$success = 'function (data, textStatus) {' . $success . '}';
}
$options['dataType'] = 'html';
$options['success'] = $success;
unset($options['update']);
}
$callbacks = array('success', 'error', 'beforeSend', 'complete');
if (isset($options['dataExpression'])) {
$callbacks[] = 'data';
unset($options['dataExpression']);
}
$options = $this->_prepareCallbacks('request', $options);
$options = $this->_parseOptions($options, $callbacks);
return $this->jQueryObject . '.ajax({' . $options .'});';
}
/**
* Create a sortable element.
*
* Requires both Ui.Core and Ui.Sortables to be loaded.
*
* @param array $options Array of options for the sortable.
* @return string Completed sortable script.
* @access public
* @see JsBaseEngineHelper::sortable() for options list.
*/
function sortable($options = array()) {
$template = '%s.sortable({%s});';
return $this->_methodTemplate('sortable', $template, $options);
}
/**
* Create a Draggable element
*
* Requires both Ui.Core and Ui.Draggable to be loaded.
*
* @param array $options Array of options for the draggable element.
* @return string Completed Draggable script.
* @access public
* @see JsBaseEngineHelper::drag() for options list.
*/
function drag($options = array()) {
$template = '%s.draggable({%s});';
return $this->_methodTemplate('drag', $template, $options);
}
/**
* Create a Droppable element
*
* Requires both Ui.Core and Ui.Droppable to be loaded.
*
* @param array $options Array of options for the droppable element.
* @return string Completed Droppable script.
* @access public
* @see JsBaseEngineHelper::drop() for options list.
*/
function drop($options = array()) {
$template = '%s.droppable({%s});';
return $this->_methodTemplate('drop', $template, $options);
}
/**
* Create a Slider element
*
* Requires both Ui.Core and Ui.Slider to be loaded.
*
* @param array $options Array of options for the droppable element.
* @return string Completed Slider script.
* @access public
* @see JsBaseEngineHelper::slider() for options list.
*/
function slider($options = array()) {
$callbacks = array('start', 'change', 'slide', 'stop');
$template = '%s.slider({%s});';
return $this->_methodTemplate('slider', $template, $options, $callbacks);
}
/**
* Serialize a form attached to $selector. If the current selection is not an input or
* form, errors will be created in the Javascript.
*
* @param array $options Options for the serialization
* @return string completed form serialization script.
* @access public
* @see JsBaseEngineHelper::serializeForm() for option list.
*/
function serializeForm($options = array()) {
$options = array_merge(array('isForm' => false, 'inline' => false), $options);
$selector = $this->selection;
if (!$options['isForm']) {
$selector = $this->selection . '.closest("form")';
}
$method = '.serialize()';
if (!$options['inline']) {
$method .= ';';
}
return $selector . $method;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,374 @@
<?php
/**
* MooTools Engine Helper for JsHelper
*
* Provides MooTools specific Javascript for JsHelper.
* Assumes that you have the following MooTools packages
*
* - Remote, Remote.HTML, Remote.JSON
* - Fx, Fx.Tween, Fx.Morph
* - Selectors, DomReady,
* - Drag, Drag.Move
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.libs.view.helpers
* @since CakePHP(tm) v 1.3
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Helper', 'Js');
class MootoolsEngineHelper extends JsBaseEngineHelper {
/**
* Option mappings for MooTools
*
* @var array
*/
var $_optionMap = array(
'request' => array(
'complete' => 'onComplete',
'success' => 'onSuccess',
'before' => 'onRequest',
'error' => 'onFailure'
),
'sortable' => array(
'distance' => 'snap',
'containment' => 'constrain',
'sort' => 'onSort',
'complete' => 'onComplete',
'start' => 'onStart',
),
'drag' => array(
'snapGrid' => 'snap',
'start' => 'onStart',
'drag' => 'onDrag',
'stop' => 'onComplete',
),
'drop' => array(
'drop' => 'onDrop',
'hover' => 'onEnter',
'leave' => 'onLeave',
),
'slider' => array(
'complete' => 'onComplete',
'change' => 'onChange',
'direction' => 'mode',
'step' => 'steps'
)
);
/**
* Contains a list of callback names -> default arguments.
*
* @var array
*/
var $_callbackArguments = array(
'slider' => array(
'onTick' => 'position',
'onChange' => 'step',
'onComplete' => 'event'
),
'request' => array(
'onRequest' => '',
'onComplete' => '',
'onCancel' => '',
'onSuccess' => 'responseText, responseXML',
'onFailure' => 'xhr',
'onException' => 'headerName, value',
),
'drag' => array(
'onBeforeStart' => 'element',
'onStart' => 'element',
'onSnap' => 'element',
'onDrag' => 'element, event',
'onComplete' => 'element, event',
'onCancel' => 'element',
),
'drop' => array(
'onBeforeStart' => 'element',
'onStart' => 'element',
'onSnap' => 'element',
'onDrag' => 'element, event',
'onComplete' => 'element, event',
'onCancel' => 'element',
'onDrop' => 'element, droppable, event',
'onLeave' => 'element, droppable',
'onEnter' => 'element, droppable',
),
'sortable' => array(
'onStart' => 'element, clone',
'onSort' => 'element, clone',
'onComplete' => 'element',
)
);
/**
* Create javascript selector for a CSS rule
*
* @param string $selector The selector that is targeted
* @return object instance of $this. Allows chained methods.
*/
function get($selector) {
$this->_multipleSelection = false;
if ($selector == 'window' || $selector == 'document') {
$this->selection = "$(" . $selector .")";
return $this;
}
if (preg_match('/^#[^\s.]+$/', $selector)) {
$this->selection = '$("' . substr($selector, 1) . '")';
return $this;
}
$this->_multipleSelection = true;
$this->selection = '$$("' . $selector . '")';
return $this;
}
/**
* Add an event to the script cache. Operates on the currently selected elements.
*
* ### Options
*
* - 'wrap' - Whether you want the callback wrapped in an anonymous function. (defaults true)
* - 'stop' - Whether you want the event to stopped. (defaults true)
*
* @param string $type Type of event to bind to the current dom id
* @param string $callback The Javascript function you wish to trigger or the function literal
* @param array $options Options for the event.
* @return string completed event handler
*/
function event($type, $callback, $options = array()) {
$defaults = array('wrap' => true, 'stop' => true);
$options = array_merge($defaults, $options);
$function = 'function (event) {%s}';
if ($options['wrap'] && $options['stop']) {
$callback = "event.stop();\n" . $callback;
}
if ($options['wrap']) {
$callback = sprintf($function, $callback);
}
$out = $this->selection . ".addEvent(\"{$type}\", $callback);";
return $out;
}
/**
* Create a domReady event. This is a special event in many libraries
*
* @param string $functionBody The code to run on domReady
* @return string completed domReady method
*/
function domReady($functionBody) {
$this->selection = 'window';
return $this->event('domready', $functionBody, array('stop' => false));
}
/**
* Create an iteration over the current selection result.
*
* @param string $method The method you want to apply to the selection
* @param string $callback The function body you wish to apply during the iteration.
* @return string completed iteration
*/
function each($callback) {
return $this->selection . '.each(function (item, index) {' . $callback . '});';
}
/**
* Trigger an Effect.
*
* @param string $name The name of the effect to trigger.
* @param array $options Array of options for the effect.
* @return string completed string with effect.
* @see JsBaseEngineHelper::effect()
*/
function effect($name, $options = array()) {
$speed = null;
if (isset($options['speed']) && in_array($options['speed'], array('fast', 'slow'))) {
if ($options['speed'] == 'fast') {
$speed = '"short"';
} elseif ($options['speed'] == 'slow') {
$speed = '"long"';
}
}
$effect = '';
switch ($name) {
case 'hide':
$effect = 'setStyle("display", "none")';
break;
case 'show':
$effect = 'setStyle("display", "")';
break;
case 'fadeIn':
case 'fadeOut':
case 'slideIn':
case 'slideOut':
list($effectName, $direction) = preg_split('/([A-Z][a-z]+)/', $name, -1, PREG_SPLIT_DELIM_CAPTURE);
$direction = strtolower($direction);
if ($speed) {
$effect .= "set(\"$effectName\", {duration:$speed}).";
}
$effect .= "$effectName(\"$direction\")";
break;
}
return $this->selection . '.' . $effect . ';';
}
/**
* Create an new Request.
*
* Requires `Request`. If you wish to use 'update' key you must have ```Request.HTML```
* if you wish to do Json requests you will need ```JSON``` and ```Request.JSON```.
*
* @param mixed $url
* @param array $options
* @return string The completed ajax call.
*/
function request($url, $options = array()) {
$url = $this->url($url);
$options = $this->_mapOptions('request', $options);
$type = $data = null;
if (isset($options['type']) || isset($options['update'])) {
if (isset($options['type']) && strtolower($options['type']) == 'json') {
$type = '.JSON';
}
if (isset($options['update'])) {
$options['update'] = str_replace('#', '', $options['update']);
$type = '.HTML';
}
unset($options['type']);
}
if (!empty($options['data'])) {
$data = $options['data'];
unset($options['data']);
}
$options['url'] = $url;
$options = $this->_prepareCallbacks('request', $options);
if (isset($options['dataExpression'])) {
$callbacks[] = 'data';
unset($options['dataExpression']);
} elseif (!empty($data)) {
$data = $this->object($data);
}
$options = $this->_parseOptions($options, array_keys($this->_callbackArguments['request']));
return "var jsRequest = new Request$type({{$options}}).send($data);";
}
/**
* Create a sortable element.
*
* Requires the `Sortables` plugin from MootoolsMore
*
* @param array $options Array of options for the sortable.
* @return string Completed sortable script.
* @see JsBaseEngineHelper::sortable() for options list.
*/
function sortable($options = array()) {
$options = $this->_processOptions('sortable', $options);
return 'var jsSortable = new Sortables(' . $this->selection . ', {' . $options . '});';
}
/**
* Create a Draggable element.
*
* Requires the `Drag` plugin from MootoolsMore
*
* @param array $options Array of options for the draggable.
* @return string Completed draggable script.
* @see JsHelper::drag() for options list.
*/
function drag($options = array()) {
$options = $this->_processOptions('drag', $options);
return $this->selection . '.makeDraggable({' . $options . '});';
}
/**
* Create a Droppable element.
*
* Requires the `Drag` and `Drag.Move` plugins from MootoolsMore
*
* Droppables in Mootools function differently from other libraries. Droppables
* are implemented as an extension of Drag. So in addtion to making a get() selection for
* the droppable element. You must also provide a selector rule to the draggable element. Furthermore,
* Mootools droppables inherit all options from Drag.
*
* @param array $options Array of options for the droppable.
* @return string Completed droppable script.
* @see JsBaseEngineHelper::drop() for options list.
*/
function drop($options = array()) {
if (empty($options['drag'])) {
trigger_error(
__('MootoolsEngine::drop() requires a "drag" option to properly function', true), E_USER_WARNING
);
return false;
}
$options['droppables'] = $this->selection;
$this->get($options['drag']);
unset($options['drag']);
$options = $this->_mapOptions('drag', $this->_mapOptions('drop', $options));
$options = $this->_prepareCallbacks('drop', $options);
$safe = array_merge(array_keys($this->_callbackArguments['drop']), array('droppables'));
$optionString = $this->_parseOptions($options, $safe);
$out = $this->selection . '.makeDraggable({' . $optionString . '});';
$this->selection = $options['droppables'];
return $out;
}
/**
* Create a slider control
*
* Requires `Slider` from MootoolsMore
*
* @param array $options Array of options for the slider.
* @return string Completed slider script.
* @see JsBaseEngineHelper::slider() for options list.
*/
function slider($options = array()) {
$slider = $this->selection;
$this->get($options['handle']);
unset($options['handle']);
if (isset($options['min']) && isset($options['max'])) {
$options['range'] = array($options['min'], $options['max']);
unset($options['min'], $options['max']);
}
$optionString = $this->_processOptions('slider', $options);
if (!empty($optionString)) {
$optionString = ', {' . $optionString . '}';
}
$out = 'var jsSlider = new Slider(' . $slider . ', ' . $this->selection . $optionString . ');';
$this->selection = $slider;
return $out;
}
/**
* Serialize the form attached to $selector.
*
* @param array $options Array of options.
* @return string Completed serializeForm() snippet
* @see JsBaseEngineHelper::serializeForm()
*/
function serializeForm($options = array()) {
$options = array_merge(array('isForm' => false, 'inline' => false), $options);
$selection = $this->selection;
if (!$options['isForm']) {
$selection = '$(' . $this->selection . '.form)';
}
$method = '.toQueryString()';
if (!$options['inline']) {
$method .= ';';
}
return $selection . $method;
}
}

View File

@@ -0,0 +1,257 @@
<?php
/**
* Number Helper.
*
* Methods to make numbers more readable.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.helpers
* @since CakePHP(tm) v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Number helper library.
*
* Methods to make numbers more readable.
*
* @package cake
* @subpackage cake.cake.libs.view.helpers
* @link http://book.cakephp.org/view/1452/Number
*/
class NumberHelper extends AppHelper {
/**
* Currencies supported by the helper. You can add additional currency formats
* with NumberHelper::addFormat
*
* @var array
* @access protected
*/
var $_currencies = array(
'USD' => array(
'before' => '$', 'after' => 'c', 'zero' => 0, 'places' => 2, 'thousands' => ',',
'decimals' => '.', 'negative' => '()', 'escape' => true
),
'GBP' => array(
'before'=>'&#163;', 'after' => 'p', 'zero' => 0, 'places' => 2, 'thousands' => ',',
'decimals' => '.', 'negative' => '()','escape' => false
),
'EUR' => array(
'before'=>'&#8364;', 'after' => false, 'zero' => 0, 'places' => 2, 'thousands' => '.',
'decimals' => ',', 'negative' => '()', 'escape' => false
)
);
/**
* Default options for currency formats
*
* @var array
* @access protected
*/
var $_currencyDefaults = array(
'before'=>'', 'after' => '', 'zero' => '0', 'places' => 2, 'thousands' => ',',
'decimals' => '.','negative' => '()', 'escape' => true
);
/**
* Formats a number with a level of precision.
*
* @param float $number A floating point number.
* @param integer $precision The precision of the returned number.
* @return float Formatted float.
* @access public
* @link http://book.cakephp.org/view/1454/precision
*/
function precision($number, $precision = 3) {
return sprintf("%01.{$precision}f", $number);
}
/**
* Returns a formatted-for-humans file size.
*
* @param integer $length Size in bytes
* @return string Human readable size
* @access public
* @link http://book.cakephp.org/view/1456/toReadableSize
*/
function toReadableSize($size) {
switch (true) {
case $size < 1024:
return sprintf(__n('%d Byte', '%d Bytes', $size, true), $size);
case round($size / 1024) < 1024:
return sprintf(__('%d KB', true), $this->precision($size / 1024, 0));
case round($size / 1024 / 1024, 2) < 1024:
return sprintf(__('%.2f MB', true), $this->precision($size / 1024 / 1024, 2));
case round($size / 1024 / 1024 / 1024, 2) < 1024:
return sprintf(__('%.2f GB', true), $this->precision($size / 1024 / 1024 / 1024, 2));
default:
return sprintf(__('%.2f TB', true), $this->precision($size / 1024 / 1024 / 1024 / 1024, 2));
}
}
/**
* Formats a number into a percentage string.
*
* @param float $number A floating point number
* @param integer $precision The precision of the returned number
* @return string Percentage string
* @access public
* @link http://book.cakephp.org/view/1455/toPercentage
*/
function toPercentage($number, $precision = 2) {
return $this->precision($number, $precision) . '%';
}
/**
* Formats a number into a currency format.
*
* @param float $number A floating point number
* @param integer $options if int then places, if string then before, if (,.-) then use it
* or array with places and before keys
* @return string formatted number
* @access public
* @link http://book.cakephp.org/view/1457/format
*/
function format($number, $options = false) {
$places = 0;
if (is_int($options)) {
$places = $options;
}
$separators = array(',', '.', '-', ':');
$before = $after = null;
if (is_string($options) && !in_array($options, $separators)) {
$before = $options;
}
$thousands = ',';
if (!is_array($options) && in_array($options, $separators)) {
$thousands = $options;
}
$decimals = '.';
if (!is_array($options) && in_array($options, $separators)) {
$decimals = $options;
}
$escape = true;
if (is_array($options)) {
$options = array_merge(array('before'=>'$', 'places' => 2, 'thousands' => ',', 'decimals' => '.'), $options);
extract($options);
}
$out = $before . number_format($number, $places, $decimals, $thousands) . $after;
if ($escape) {
return h($out);
}
return $out;
}
/**
* Formats a number into a currency format.
*
* ### Options
*
* - `before` - The currency symbol to place before whole numbers ie. '$'
* - `after` - The currency symbol to place after decimal numbers ie. 'c'. Set to boolean false to
* use no decimal symbol. eg. 0.35 => $0.35.
* - `zero` - The text to use for zero values, can be a string or a number. ie. 0, 'Free!'
* - `places` - Number of decimal places to use. ie. 2
* - `thousands` - Thousands separator ie. ','
* - `decimals` - Decimal separator symbol ie. '.'
* - `negative` - Symbol for negative numbers. If equal to '()', the number will be wrapped with ( and )
* - `escape` - Should the output be htmlentity escaped? Defaults to true
*
* @param float $number
* @param string $currency Shortcut to default options. Valid values are 'USD', 'EUR', 'GBP', otherwise
* set at least 'before' and 'after' options.
* @param array $options
* @return string Number formatted as a currency.
* @access public
* @link http://book.cakephp.org/view/1453/currency
*/
function currency($number, $currency = 'USD', $options = array()) {
$default = $this->_currencyDefaults;
if (isset($this->_currencies[$currency])) {
$default = $this->_currencies[$currency];
} elseif (is_string($currency)) {
$options['before'] = $currency;
}
$options = array_merge($default, $options);
$result = null;
if ($number == 0 ) {
if ($options['zero'] !== 0 ) {
return $options['zero'];
}
$options['after'] = null;
} elseif ($number < 1 && $number > -1 ) {
if ($options['after'] !== false) {
$multiply = intval('1' . str_pad('', $options['places'], '0'));
$number = $number * $multiply;
$options['before'] = null;
$options['places'] = null;
}
} elseif (empty($options['before'])) {
$options['before'] = null;
} else {
$options['after'] = null;
}
$abs = abs($number);
$result = $this->format($abs, $options);
if ($number < 0 ) {
if ($options['negative'] == '()') {
$result = '(' . $result .')';
} else {
$result = $options['negative'] . $result;
}
}
return $result;
}
/**
* Add a currency format to the Number helper. Makes reusing
* currency formats easier.
*
* {{{ $number->addFormat('NOK', array('before' => 'Kr. ')); }}}
*
* You can now use `NOK` as a shortform when formatting currency amounts.
*
* {{{ $number->currency($value, 'NOK'); }}}
*
* Added formats are merged with the following defaults.
*
* {{{
* array(
* 'before' => '$', 'after' => 'c', 'zero' => 0, 'places' => 2, 'thousands' => ',',
* 'decimals' => '.', 'negative' => '()', 'escape' => true
* )
* }}}
*
* @param string $formatName The format name to be used in the future.
* @param array $options The array of options for this format.
* @return void
* @see NumberHelper::currency()
* @access public
*/
function addFormat($formatName, $options) {
$this->_currencies[$formatName] = $options + $this->_currencyDefaults;
}
}

View File

@@ -0,0 +1,813 @@
<?php
/**
* Pagination Helper class file.
*
* Generates pagination links
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.helpers
* @since CakePHP(tm) v 1.2.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Pagination Helper class for easy generation of pagination links.
*
* PaginationHelper encloses all methods needed when working with pagination.
*
* @package cake
* @subpackage cake.cake.libs.view.helpers
* @link http://book.cakephp.org/view/1458/Paginator
*/
class PaginatorHelper extends AppHelper {
/**
* Helper dependencies
*
* @var array
*/
var $helpers = array('Html');
/**
* Holds the default model for paged recordsets
*
* @var string
*/
var $__defaultModel = null;
/**
* The class used for 'Ajax' pagination links.
*
* @var string
*/
var $_ajaxHelperClass = 'Js';
/**
* Holds the default options for pagination links
*
* The values that may be specified are:
*
* - `$options['format']` Format of the counter. Supported formats are 'range' and 'pages'
* and custom (default). In the default mode the supplied string is parsed and constants are replaced
* by their actual values.
* Constants: %page%, %pages%, %current%, %count%, %start%, %end% .
* - `$options['separator']` The separator of the actual page and number of pages (default: ' of ').
* - `$options['url']` Url of the action. See Router::url()
* - `$options['url']['sort']` the key that the recordset is sorted.
* - `$options['url']['direction']` Direction of the sorting (default: 'asc').
* - `$options['url']['page']` Page # to display.
* - `$options['model']` The name of the model.
* - `$options['escape']` Defines if the title field for the link should be escaped (default: true).
* - `$options['update']` DOM id of the element updated with the results of the AJAX call.
* If this key isn't specified Paginator will use plain HTML links.
* - `$options['indicator']` DOM id of the element that will be shown when doing AJAX requests. **Only supported by
* AjaxHelper**
*
* @var array
* @access public
*/
var $options = array();
/**
* Constructor for the helper. Sets up the helper that is used for creating 'AJAX' links.
*
* Use `var $helpers = array('Paginator' => array('ajax' => 'CustomHelper'));` to set a custom Helper
* or choose a non JsHelper Helper. If you want to use a specific library with JsHelper declare JsHelper and its
* adapter before including PaginatorHelper in your helpers array.
*
* The chosen custom helper must implement a `link()` method.
*
* @return void
*/
function __construct($config = array()) {
$ajaxProvider = isset($config['ajax']) ? $config['ajax'] : 'Js';
$this->helpers[] = $ajaxProvider;
$this->_ajaxHelperClass = $ajaxProvider;
App::import('Helper', $ajaxProvider);
$classname = $ajaxProvider . 'Helper';
if (!is_callable(array($classname, 'link'))) {
trigger_error(sprintf(__('%s does not implement a link() method, it is incompatible with PaginatorHelper', true), $classname), E_USER_WARNING);
}
}
/**
* Before render callback. Overridden to merge passed args with url options.
*
* @return void
* @access public
*/
function beforeRender() {
$this->options['url'] = array_merge($this->params['pass'], $this->params['named']);
parent::beforeRender();
}
/**
* Gets the current paging parameters from the resultset for the given model
*
* @param string $model Optional model name. Uses the default if none is specified.
* @return array The array of paging parameters for the paginated resultset.
* @access public
*/
function params($model = null) {
if (empty($model)) {
$model = $this->defaultModel();
}
if (!isset($this->params['paging']) || empty($this->params['paging'][$model])) {
return null;
}
return $this->params['paging'][$model];
}
/**
* Sets default options for all pagination links
*
* @param mixed $options Default options for pagination links. If a string is supplied - it
* is used as the DOM id element to update. See PaginatorHelper::$options for list of keys.
* @return void
* @access public
*/
function options($options = array()) {
if (is_string($options)) {
$options = array('update' => $options);
}
if (!empty($options['paging'])) {
if (!isset($this->params['paging'])) {
$this->params['paging'] = array();
}
$this->params['paging'] = array_merge($this->params['paging'], $options['paging']);
unset($options['paging']);
}
$model = $this->defaultModel();
if (!empty($options[$model])) {
if (!isset($this->params['paging'][$model])) {
$this->params['paging'][$model] = array();
}
$this->params['paging'][$model] = array_merge(
$this->params['paging'][$model], $options[$model]
);
unset($options[$model]);
}
$this->options = array_filter(array_merge($this->options, $options));
}
/**
* Gets the current page of the recordset for the given model
*
* @param string $model Optional model name. Uses the default if none is specified.
* @return string The current page number of the recordset.
* @access public
*/
function current($model = null) {
$params = $this->params($model);
if (isset($params['page'])) {
return $params['page'];
}
return 1;
}
/**
* Gets the current key by which the recordset is sorted
*
* @param string $model Optional model name. Uses the default if none is specified.
* @param mixed $options Options for pagination links. See #options for list of keys.
* @return string The name of the key by which the recordset is being sorted, or
* null if the results are not currently sorted.
* @access public
*/
function sortKey($model = null, $options = array()) {
if (empty($options)) {
$params = $this->params($model);
$options = array_merge($params['defaults'], $params['options']);
}
if (isset($options['sort']) && !empty($options['sort'])) {
return $options['sort'];
} elseif (isset($options['order']) && is_array($options['order'])) {
return key($options['order']);
} elseif (isset($options['order']) && is_string($options['order'])) {
return $options['order'];
}
return null;
}
/**
* Gets the current direction the recordset is sorted
*
* @param string $model Optional model name. Uses the default if none is specified.
* @param mixed $options Options for pagination links. See #options for list of keys.
* @return string The direction by which the recordset is being sorted, or
* null if the results are not currently sorted.
* @access public
*/
function sortDir($model = null, $options = array()) {
$dir = null;
if (empty($options)) {
$params = $this->params($model);
$options = array_merge($params['defaults'], $params['options']);
}
if (isset($options['direction'])) {
$dir = strtolower($options['direction']);
} elseif (isset($options['order']) && is_array($options['order'])) {
$dir = strtolower(current($options['order']));
}
if ($dir == 'desc') {
return 'desc';
}
return 'asc';
}
/**
* Generates a "previous" link for a set of paged records
*
* ### Options:
*
* - `tag` The tag wrapping tag you want to use, defaults to 'span'
* - `escape` Whether you want the contents html entity encoded, defaults to true
* - `model` The model to use, defaults to PaginatorHelper::defaultModel()
*
* @param string $title Title for the link. Defaults to '<< Previous'.
* @param mixed $options Options for pagination link. See #options for list of keys.
* @param string $disabledTitle Title when the link is disabled.
* @param mixed $disabledOptions Options for the disabled pagination link. See #options for list of keys.
* @return string A "previous" link or $disabledTitle text if the link is disabled.
* @access public
*/
function prev($title = '<< Previous', $options = array(), $disabledTitle = null, $disabledOptions = array()) {
return $this->__pagingLink('Prev', $title, $options, $disabledTitle, $disabledOptions);
}
/**
* Generates a "next" link for a set of paged records
*
* ### Options:
*
* - `tag` The tag wrapping tag you want to use, defaults to 'span'
* - `escape` Whether you want the contents html entity encoded, defaults to true
* - `model` The model to use, defaults to PaginatorHelper::defaultModel()
*
* @param string $title Title for the link. Defaults to 'Next >>'.
* @param mixed $options Options for pagination link. See above for list of keys.
* @param string $disabledTitle Title when the link is disabled.
* @param mixed $disabledOptions Options for the disabled pagination link. See above for list of keys.
* @return string A "next" link or or $disabledTitle text if the link is disabled.
* @access public
*/
function next($title = 'Next >>', $options = array(), $disabledTitle = null, $disabledOptions = array()) {
return $this->__pagingLink('Next', $title, $options, $disabledTitle, $disabledOptions);
}
/**
* Generates a sorting link. Sets named parameters for the sort and direction. Handles
* direction switching automatically.
*
* ### Options:
*
* - `escape` Whether you want the contents html entity encoded, defaults to true
* - `model` The model to use, defaults to PaginatorHelper::defaultModel()
*
* @param string $title Title for the link.
* @param string $key The name of the key that the recordset should be sorted. If $key is null
* $title will be used for the key, and a title will be generated by inflection.
* @param array $options Options for sorting link. See above for list of keys.
* @return string A link sorting default by 'asc'. If the resultset is sorted 'asc' by the specified
* key the returned link will sort by 'desc'.
* @access public
*/
function sort($title, $key = null, $options = array()) {
$options = array_merge(array('url' => array(), 'model' => null), $options);
$url = $options['url'];
unset($options['url']);
if (empty($key)) {
$key = $title;
$title = __(Inflector::humanize(preg_replace('/_id$/', '', $title)), true);
}
$dir = isset($options['direction']) ? $options['direction'] : 'asc';
unset($options['direction']);
$sortKey = $this->sortKey($options['model']);
$defaultModel = $this->defaultModel();
$isSorted = (
$sortKey === $key ||
$sortKey === $defaultModel . '.' . $key ||
$key === $defaultModel . '.' . $sortKey
);
if ($isSorted) {
$dir = $this->sortDir($options['model']) === 'asc' ? 'desc' : 'asc';
$class = $dir === 'asc' ? 'desc' : 'asc';
if (!empty($options['class'])) {
$options['class'] .= ' ' . $class;
} else {
$options['class'] = $class;
}
}
if (is_array($title) && array_key_exists($dir, $title)) {
$title = $title[$dir];
}
$url = array_merge(array('sort' => $key, 'direction' => $dir), $url, array('order' => null));
return $this->link($title, $url, $options);
}
/**
* Generates a plain or Ajax link with pagination parameters
*
* ### Options
*
* - `update` The Id of the DOM element you wish to update. Creates Ajax enabled links
* with the AjaxHelper.
* - `escape` Whether you want the contents html entity encoded, defaults to true
* - `model` The model to use, defaults to PaginatorHelper::defaultModel()
*
* @param string $title Title for the link.
* @param mixed $url Url for the action. See Router::url()
* @param array $options Options for the link. See #options for list of keys.
* @return string A link with pagination parameters.
* @access public
*/
function link($title, $url = array(), $options = array()) {
$options = array_merge(array('model' => null, 'escape' => true), $options);
$model = $options['model'];
unset($options['model']);
if (!empty($this->options)) {
$options = array_merge($this->options, $options);
}
if (isset($options['url'])) {
$url = array_merge((array)$options['url'], (array)$url);
unset($options['url']);
}
$url = $this->url($url, true, $model);
$obj = isset($options['update']) ? $this->_ajaxHelperClass : 'Html';
$url = array_merge(array('page' => $this->current($model)), $url);
$url = array_merge(Set::filter($url, true), array_intersect_key($url, array('plugin' => true)));
return $this->{$obj}->link($title, $url, $options);
}
/**
* Merges passed URL options with current pagination state to generate a pagination URL.
*
* @param array $options Pagination/URL options array
* @param boolean $asArray Return the url as an array, or a URI string
* @param string $model Which model to paginate on
* @return mixed By default, returns a full pagination URL string for use in non-standard contexts (i.e. JavaScript)
* @access public
*/
function url($options = array(), $asArray = false, $model = null) {
$paging = $this->params($model);
$url = array_merge(array_filter(Set::diff(array_merge(
$paging['defaults'], $paging['options']), $paging['defaults'])), $options
);
if (isset($url['order'])) {
$sort = $direction = null;
if (is_array($url['order'])) {
list($sort, $direction) = array($this->sortKey($model, $url), current($url['order']));
}
unset($url['order']);
$url = array_merge($url, compact('sort', 'direction'));
}
if ($asArray) {
return $url;
}
return parent::url($url);
}
/**
* Protected method for generating prev/next links
*
* @access protected
*/
function __pagingLink($which, $title = null, $options = array(), $disabledTitle = null, $disabledOptions = array()) {
$check = 'has' . $which;
$_defaults = array(
'url' => array(), 'step' => 1, 'escape' => true,
'model' => null, 'tag' => 'span', 'class' => strtolower($which)
);
$options = array_merge($_defaults, (array)$options);
$paging = $this->params($options['model']);
if (empty($disabledOptions)) {
$disabledOptions = $options;
}
if (!$this->{$check}($options['model']) && (!empty($disabledTitle) || !empty($disabledOptions))) {
if (!empty($disabledTitle) && $disabledTitle !== true) {
$title = $disabledTitle;
}
$options = array_merge($_defaults, (array)$disabledOptions);
} elseif (!$this->{$check}($options['model'])) {
return null;
}
foreach (array_keys($_defaults) as $key) {
${$key} = $options[$key];
unset($options[$key]);
}
$url = array_merge(array('page' => $paging['page'] + ($which == 'Prev' ? $step * -1 : $step)), $url);
if ($this->{$check}($model)) {
return $this->Html->tag($tag, $this->link($title, $url, array_merge($options, compact('escape', 'class'))));
} else {
return $this->Html->tag($tag, $title, array_merge($options, compact('escape', 'class')));
}
}
/**
* Returns true if the given result set is not at the first page
*
* @param string $model Optional model name. Uses the default if none is specified.
* @return boolean True if the result set is not at the first page.
* @access public
*/
function hasPrev($model = null) {
return $this->__hasPage($model, 'prev');
}
/**
* Returns true if the given result set is not at the last page
*
* @param string $model Optional model name. Uses the default if none is specified.
* @return boolean True if the result set is not at the last page.
* @access public
*/
function hasNext($model = null) {
return $this->__hasPage($model, 'next');
}
/**
* Returns true if the given result set has the page number given by $page
*
* @param string $model Optional model name. Uses the default if none is specified.
* @param int $page The page number - if not set defaults to 1.
* @return boolean True if the given result set has the specified page number.
* @access public
*/
function hasPage($model = null, $page = 1) {
if (is_numeric($model)) {
$page = $model;
$model = null;
}
$paging = $this->params($model);
return $page <= $paging['pageCount'];
}
/**
* Does $model have $page in its range?
*
* @param string $model Model name to get parameters for.
* @param integer $page Page number you are checking.
* @return boolean Whether model has $page
* @access protected
*/
function __hasPage($model, $page) {
$params = $this->params($model);
if (!empty($params)) {
if ($params["{$page}Page"] == true) {
return true;
}
}
return false;
}
/**
* Gets the default model of the paged sets
*
* @return string Model name or null if the pagination isn't initialized.
* @access public
*/
function defaultModel() {
if ($this->__defaultModel != null) {
return $this->__defaultModel;
}
if (empty($this->params['paging'])) {
return null;
}
list($this->__defaultModel) = array_keys($this->params['paging']);
return $this->__defaultModel;
}
/**
* Returns a counter string for the paged result set
*
* ### Options
*
* - `model` The model to use, defaults to PaginatorHelper::defaultModel();
* - `format` The format string you want to use, defaults to 'pages' Which generates output like '1 of 5'
* set to 'range' to generate output like '1 - 3 of 13'. Can also be set to a custom string, containing
* the following placeholders `%page%`, `%pages%`, `%current%`, `%count%`, `%start%`, `%end%` and any
* custom content you would like.
* - `separator` The separator string to use, default to ' of '
*
* @param mixed $options Options for the counter string. See #options for list of keys.
* @return string Counter string.
* @access public
*/
function counter($options = array()) {
if (is_string($options)) {
$options = array('format' => $options);
}
$options = array_merge(
array(
'model' => $this->defaultModel(),
'format' => 'pages',
'separator' => __(' of ', true)
),
$options);
$paging = $this->params($options['model']);
if ($paging['pageCount'] == 0) {
$paging['pageCount'] = 1;
}
$start = 0;
if ($paging['count'] >= 1) {
$start = (($paging['page'] - 1) * $paging['options']['limit']) + 1;
}
$end = $start + $paging['options']['limit'] - 1;
if ($paging['count'] < $end) {
$end = $paging['count'];
}
switch ($options['format']) {
case 'range':
if (!is_array($options['separator'])) {
$options['separator'] = array(' - ', $options['separator']);
}
$out = $start . $options['separator'][0] . $end . $options['separator'][1];
$out .= $paging['count'];
break;
case 'pages':
$out = $paging['page'] . $options['separator'] . $paging['pageCount'];
break;
default:
$map = array(
'%page%' => $paging['page'],
'%pages%' => $paging['pageCount'],
'%current%' => $paging['current'],
'%count%' => $paging['count'],
'%start%' => $start,
'%end%' => $end
);
$out = str_replace(array_keys($map), array_values($map), $options['format']);
$newKeys = array(
'{:page}', '{:pages}', '{:current}', '{:count}', '{:start}', '{:end}'
);
$out = str_replace($newKeys, array_values($map), $out);
break;
}
return $out;
}
/**
* Returns a set of numbers for the paged result set
* uses a modulus to decide how many numbers to show on each side of the current page (default: 8)
*
* ### Options
*
* - `before` Content to be inserted before the numbers
* - `after` Content to be inserted after the numbers
* - `model` Model to create numbers for, defaults to PaginatorHelper::defaultModel()
* - `modulus` how many numbers to include on either side of the current page, defaults to 8.
* - `separator` Separator content defaults to ' | '
* - `tag` The tag to wrap links in, defaults to 'span'
* - `first` Whether you want first links generated, set to an integer to define the number of 'first'
* links to generate
* - `last` Whether you want last links generated, set to an integer to define the number of 'last'
* links to generate
*
* @param mixed $options Options for the numbers, (before, after, model, modulus, separator)
* @return string numbers string.
* @access public
*/
function numbers($options = array()) {
if ($options === true) {
$options = array(
'before' => ' | ', 'after' => ' | ', 'first' => 'first', 'last' => 'last'
);
}
$defaults = array(
'tag' => 'span', 'before' => null, 'after' => null, 'model' => $this->defaultModel(),
'modulus' => '8', 'separator' => ' | ', 'first' => null, 'last' => null,
);
$options += $defaults;
$params = (array)$this->params($options['model']) + array('page'=> 1);
unset($options['model']);
if ($params['pageCount'] <= 1) {
return false;
}
extract($options);
unset($options['tag'], $options['before'], $options['after'], $options['model'],
$options['modulus'], $options['separator'], $options['first'], $options['last']);
$out = '';
if ($modulus && $params['pageCount'] > $modulus) {
$half = intval($modulus / 2);
$end = $params['page'] + $half;
if ($end > $params['pageCount']) {
$end = $params['pageCount'];
}
$start = $params['page'] - ($modulus - ($end - $params['page']));
if ($start <= 1) {
$start = 1;
$end = $params['page'] + ($modulus - $params['page']) + 1;
}
if ($first && $start > 1) {
$offset = ($start <= (int)$first) ? $start - 1 : $first;
if ($offset < $start - 1) {
$out .= $this->first($offset, array('tag' => $tag, 'separator' => $separator));
} else {
$out .= $this->first($offset, array('tag' => $tag, 'after' => $separator, 'separator' => $separator));
}
}
$out .= $before;
for ($i = $start; $i < $params['page']; $i++) {
$out .= $this->Html->tag($tag, $this->link($i, array('page' => $i), $options))
. $separator;
}
$out .= $this->Html->tag($tag, $params['page'], array('class' => 'current'));
if ($i != $params['pageCount']) {
$out .= $separator;
}
$start = $params['page'] + 1;
for ($i = $start; $i < $end; $i++) {
$out .= $this->Html->tag($tag, $this->link($i, array('page' => $i), $options))
. $separator;
}
if ($end != $params['page']) {
$out .= $this->Html->tag($tag, $this->link($i, array('page' => $end), $options));
}
$out .= $after;
if ($last && $end < $params['pageCount']) {
$offset = ($params['pageCount'] < $end + (int)$last) ? $params['pageCount'] - $end : $last;
if ($offset <= $last && $params['pageCount'] - $end > $offset) {
$out .= $this->last($offset, array('tag' => $tag, 'separator' => $separator));
} else {
$out .= $this->last($offset, array('tag' => $tag, 'before' => $separator, 'separator' => $separator));
}
}
} else {
$out .= $before;
for ($i = 1; $i <= $params['pageCount']; $i++) {
if ($i == $params['page']) {
$out .= $this->Html->tag($tag, $i, array('class' => 'current'));
} else {
$out .= $this->Html->tag($tag, $this->link($i, array('page' => $i), $options));
}
if ($i != $params['pageCount']) {
$out .= $separator;
}
}
$out .= $after;
}
return $out;
}
/**
* Returns a first or set of numbers for the first pages
*
* ### Options:
*
* - `tag` The tag wrapping tag you want to use, defaults to 'span'
* - `before` Content to insert before the link/tag
* - `model` The model to use defaults to PaginatorHelper::defaultModel()
* - `separator` Content between the generated links, defaults to ' | '
*
* @param mixed $first if string use as label for the link, if numeric print page numbers
* @param mixed $options
* @return string numbers string.
* @access public
*/
function first($first = '<< first', $options = array()) {
$options = array_merge(
array(
'tag' => 'span',
'after'=> null,
'model' => $this->defaultModel(),
'separator' => ' | ',
),
(array)$options);
$params = array_merge(array('page'=> 1), (array)$this->params($options['model']));
unset($options['model']);
if ($params['pageCount'] <= 1) {
return false;
}
extract($options);
unset($options['tag'], $options['after'], $options['model'], $options['separator']);
$out = '';
if (is_int($first) && $params['page'] > $first) {
if ($after === null) {
$after = '...';
}
for ($i = 1; $i <= $first; $i++) {
$out .= $this->Html->tag($tag, $this->link($i, array('page' => $i), $options));
if ($i != $first) {
$out .= $separator;
}
}
$out .= $after;
} elseif ($params['page'] > 1) {
$out = $this->Html->tag($tag, $this->link($first, array('page' => 1), $options))
. $after;
}
return $out;
}
/**
* Returns a last or set of numbers for the last pages
*
* ### Options:
*
* - `tag` The tag wrapping tag you want to use, defaults to 'span'
* - `before` Content to insert before the link/tag
* - `model` The model to use defaults to PaginatorHelper::defaultModel()
* - `separator` Content between the generated links, defaults to ' | '
*
* @param mixed $last if string use as label for the link, if numeric print page numbers
* @param mixed $options Array of options
* @return string numbers string.
* @access public
*/
function last($last = 'last >>', $options = array()) {
$options = array_merge(
array(
'tag' => 'span',
'before'=> null,
'model' => $this->defaultModel(),
'separator' => ' | ',
),
(array)$options);
$params = array_merge(array('page'=> 1), (array)$this->params($options['model']));
unset($options['model']);
if ($params['pageCount'] <= 1) {
return false;
}
extract($options);
unset($options['tag'], $options['before'], $options['model'], $options['separator']);
$out = '';
$lower = $params['pageCount'] - $last + 1;
if (is_int($last) && $params['page'] < $lower) {
if ($before === null) {
$before = '...';
}
for ($i = $lower; $i <= $params['pageCount']; $i++) {
$out .= $this->Html->tag($tag, $this->link($i, array('page' => $i), $options));
if ($i != $params['pageCount']) {
$out .= $separator;
}
}
$out = $before . $out;
} elseif ($params['page'] < $params['pageCount']) {
$out = $before . $this->Html->tag(
$tag, $this->link($last, array('page' => $params['pageCount']), $options
));
}
return $out;
}
}

View File

@@ -0,0 +1,365 @@
<?php
/**
* Prototype Engine Helper for JsHelper
*
* Provides Prototype specific Javascript for JsHelper. Requires at least
* Prototype 1.6
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.libs.view.helpers
* @since CakePHP(tm) v 1.3
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Helper', 'Js');
class PrototypeEngineHelper extends JsBaseEngineHelper {
/**
* Is the current selection a multiple selection? or is it just a single element.
*
* @var boolean
*/
var $_multiple = false;
/**
* Option mappings for Prototype
*
* @var array
*/
var $_optionMap = array(
'request' => array(
'async' => 'asynchronous',
'data' => 'parameters',
'before' => 'onCreate',
'success' => 'onSuccess',
'complete' => 'onComplete',
'error' => 'onFailure'
),
'sortable' => array(
'start' => 'onStart',
'sort' => 'onChange',
'complete' => 'onUpdate',
'distance' => 'snap',
),
'drag' => array(
'snapGrid' => 'snap',
'container' => 'constraint',
'stop' => 'onEnd',
'start' => 'onStart',
'drag' => 'onDrag',
),
'drop' => array(
'hover' => 'onHover',
'drop' => 'onDrop',
'hoverClass' => 'hoverclass',
),
'slider' => array(
'direction' => 'axis',
'change' => 'onSlide',
'complete' => 'onChange',
'value' => 'sliderValue',
)
);
/**
* Contains a list of callback names -> default arguments.
*
* @var array
*/
var $_callbackArguments = array(
'slider' => array(
'onSlide' => 'value',
'onChange' => 'value',
),
'drag' => array(
'onStart' => 'event',
'onDrag' => 'event',
'change' => 'draggable',
'onEnd' => 'event',
),
'drop' => array(
'onHover' => 'draggable, droppable, event',
'onDrop' => 'draggable, droppable, event',
),
'request' => array(
'onCreate' => 'transport',
'onComplete' => 'transport',
'onFailure' => 'response, jsonHeader',
'onRequest' => 'transport',
'onSuccess' => 'response, jsonHeader'
),
'sortable' => array(
'onStart' => 'element',
'onChange' => 'element',
'onUpdate' => 'element',
),
);
/**
* Create javascript selector for a CSS rule
*
* @param string $selector The selector that is targeted
* @return object instance of $this. Allows chained methods.
*/
function get($selector) {
$this->_multiple = false;
if ($selector == 'window' || $selector == 'document') {
$this->selection = "$(" . $selector .")";
return $this;
}
if (preg_match('/^#[^\s.]+$/', $selector)) {
$this->selection = '$("' . substr($selector, 1) . '")';
return $this;
}
$this->_multiple = true;
$this->selection = '$$("' . $selector . '")';
return $this;
}
/**
* Add an event to the script cache. Operates on the currently selected elements.
*
* ### Options
*
* - `wrap` - Whether you want the callback wrapped in an anonymous function. (defaults true)
* - `stop` - Whether you want the event to stopped. (defaults true)
*
* @param string $type Type of event to bind to the current 946 id
* @param string $callback The Javascript function you wish to trigger or the function literal
* @param array $options Options for the event.
* @return string completed event handler
*/
function event($type, $callback, $options = array()) {
$defaults = array('wrap' => true, 'stop' => true);
$options = array_merge($defaults, $options);
$function = 'function (event) {%s}';
if ($options['wrap'] && $options['stop']) {
$callback = "event.stop();\n" . $callback;
}
if ($options['wrap']) {
$callback = sprintf($function, $callback);
}
$out = $this->selection . ".observe(\"{$type}\", $callback);";
return $out;
}
/**
* Create a domReady event. This is a special event in many libraries
*
* @param string $functionBody The code to run on domReady
* @return string completed domReady method
* @access public
*/
function domReady($functionBody) {
$this->selection = 'document';
return $this->event('dom:loaded', $functionBody, array('stop' => false));
}
/**
* Create an iteration over the current selection result.
*
* @param string $method The method you want to apply to the selection
* @param string $callback The function body you wish to apply during the iteration.
* @return string completed iteration
* @access public
*/
function each($callback) {
return $this->selection . '.each(function (item, index) {' . $callback . '});';
}
/**
* Trigger an Effect.
*
* ### Note: Effects require Scriptaculous to be loaded.
*
* @param string $name The name of the effect to trigger.
* @param array $options Array of options for the effect.
* @return string completed string with effect.
* @access public
* @see JsBaseEngineHelper::effect()
*/
function effect($name, $options = array()) {
$effect = '';
$optionString = null;
if (isset($options['speed'])) {
if ($options['speed'] == 'fast') {
$options['duration'] = 0.5;
} elseif ($options['speed'] == 'slow') {
$options['duration'] = 2;
} else {
$options['duration'] = 1;
}
unset($options['speed']);
}
if (!empty($options)) {
$optionString = ', {' . $this->_parseOptions($options) . '}';
}
switch ($name) {
case 'hide':
case 'show':
$effect = $this->selection . '.' . $name . '();';
break;
case 'slideIn':
case 'slideOut':
$name = ($name == 'slideIn') ? 'slideDown' : 'slideUp';
$effect = 'Effect.' . $name . '(' . $this->selection . $optionString . ');';
break;
case 'fadeIn':
case 'fadeOut':
$name = ($name == 'fadeIn') ? 'appear' : 'fade';
$effect = $this->selection . '.' . $name .'(' . substr($optionString, 2) . ');';
break;
}
return $effect;
}
/**
* Create an Ajax or Ajax.Updater call.
*
* @param mixed $url
* @param array $options
* @return string The completed ajax call.
* @access public
*/
function request($url, $options = array()) {
$url = '"'. $this->url($url) . '"';
$options = $this->_mapOptions('request', $options);
$type = '.Request';
$data = null;
if (isset($options['type']) && strtolower($options['type']) == 'json') {
unset($options['type']);
}
if (isset($options['update'])) {
$url = '"' . str_replace('#', '', $options['update']) . '", ' . $url;
$type = '.Updater';
unset($options['update'], $options['type']);
}
$safe = array_keys($this->_callbackArguments['request']);
$options = $this->_prepareCallbacks('request', $options, $safe);
if (isset($options['dataExpression'])) {
$safe[] = 'parameters';
unset($options['dataExpression']);
}
$options = $this->_parseOptions($options, $safe);
if (!empty($options)) {
$options = ', {' . $options . '}';
}
return "var jsRequest = new Ajax$type($url$options);";
}
/**
* Create a sortable element.
*
* #### Note: Requires scriptaculous to be loaded.
*
* @param array $options Array of options for the sortable.
* @return string Completed sortable script.
* @access public
* @see JsBaseEngineHelper::sortable() for options list.
*/
function sortable($options = array()) {
$options = $this->_processOptions('sortable', $options);
if (!empty($options)) {
$options = ', {' . $options . '}';
}
return 'var jsSortable = Sortable.create(' . $this->selection . $options . ');';
}
/**
* Create a Draggable element.
*
* #### Note: Requires scriptaculous to be loaded.
*
* @param array $options Array of options for the draggable.
* @return string Completed draggable script.
* @access public
* @see JsBaseEngineHelper::draggable() for options list.
*/
function drag($options = array()) {
$options = $this->_processOptions('drag', $options);
if (!empty($options)) {
$options = ', {' . $options . '}';
}
if ($this->_multiple) {
return $this->each('new Draggable(item' . $options . ');');
}
return 'var jsDrag = new Draggable(' . $this->selection . $options . ');';
}
/**
* Create a Droppable element.
*
* #### Note: Requires scriptaculous to be loaded.
*
* @param array $options Array of options for the droppable.
* @return string Completed droppable script.
* @access public
* @see JsBaseEngineHelper::droppable() for options list.
*/
function drop($options = array()) {
$options = $this->_processOptions('drop', $options);
if (!empty($options)) {
$options = ', {' . $options . '}';
}
return 'Droppables.add(' . $this->selection . $options . ');';
}
/**
* Creates a slider control widget.
*
* ### Note: Requires scriptaculous to be loaded.
*
* @param array $options Array of options for the slider.
* @return string Completed slider script.
* @access public
* @see JsBaseEngineHelper::slider() for options list.
*/
function slider($options = array()) {
$slider = $this->selection;
$this->get($options['handle']);
unset($options['handle']);
if (isset($options['min']) && isset($options['max'])) {
$options['range'] = array($options['min'], $options['max']);
unset($options['min'], $options['max']);
}
$optionString = $this->_processOptions('slider', $options);
if (!empty($optionString)) {
$optionString = ', {' . $optionString . '}';
}
$out = 'var jsSlider = new Control.Slider(' . $this->selection . ', ' . $slider . $optionString . ');';
$this->selection = $slider;
return $out;
}
/**
* Serialize the form attached to $selector.
*
* @param array $options Array of options.
* @return string Completed serializeForm() snippet
* @access public
* @see JsBaseEngineHelper::serializeForm()
*/
function serializeForm($options = array()) {
$options = array_merge(array('isForm' => false, 'inline' => false), $options);
$selection = $this->selection;
if (!$options['isForm']) {
$selection = '$(' . $this->selection . '.form)';
}
$method = '.serialize()';
if (!$options['inline']) {
$method .= ';';
}
return $selection . $method;
}
}

View File

@@ -0,0 +1,292 @@
<?php
/**
* RSS Helper class file.
*
* Simplifies the output of RSS feeds.
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.helpers
* @since CakePHP(tm) v 1.2
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Helper', 'Xml');
/**
* XML Helper class for easy output of XML structures.
*
* XmlHelper encloses all methods needed while working with XML documents.
*
* @package cake
* @subpackage cake.cake.libs.view.helpers
* @link http://book.cakephp.org/view/1460/RSS
*/
class RssHelper extends XmlHelper {
/**
* Helpers used by RSS Helper
*
* @var array
* @access public
*/
var $helpers = array('Time');
/**
* Base URL
*
* @access public
* @var string
*/
var $base = null;
/**
* URL to current action.
*
* @access public
* @var string
*/
var $here = null;
/**
* Parameter array.
*
* @access public
* @var array
*/
var $params = array();
/**
* Current action.
*
* @access public
* @var string
*/
var $action = null;
/**
* POSTed model data
*
* @access public
* @var array
*/
var $data = null;
/**
* Name of the current model
*
* @access public
* @var string
*/
var $model = null;
/**
* Name of the current field
*
* @access public
* @var string
*/
var $field = null;
/**
* Default spec version of generated RSS
*
* @access public
* @var string
*/
var $version = '2.0';
/**
* Returns an RSS document wrapped in `<rss />` tags
*
* @param array $attrib `<rss />` tag attributes
* @return string An RSS document
* @access public
*/
function document($attrib = array(), $content = null) {
if ($content === null) {
$content = $attrib;
$attrib = array();
}
if (!isset($attrib['version']) || empty($attrib['version'])) {
$attrib['version'] = $this->version;
}
return $this->elem('rss', $attrib, $content);
}
/**
* Returns an RSS `<channel />` element
*
* @param array $attrib `<channel />` tag attributes
* @param mixed $elements Named array elements which are converted to tags
* @param mixed $content Content (`<item />`'s belonging to this channel
* @return string An RSS `<channel />`
* @access public
*/
function channel($attrib = array(), $elements = array(), $content = null) {
$view =& ClassRegistry::getObject('view');
if (!isset($elements['title']) && !empty($view->pageTitle)) {
$elements['title'] = $view->pageTitle;
}
if (!isset($elements['link'])) {
$elements['link'] = '/';
}
if (!isset($elements['description'])) {
$elements['description'] = '';
}
$elements['link'] = $this->url($elements['link'], true);
$elems = '';
foreach ($elements as $elem => $data) {
$attributes = array();
if (is_array($data)) {
if (strtolower($elem) == 'cloud') {
$attributes = $data;
$data = array();
} elseif (isset($data['attrib']) && is_array($data['attrib'])) {
$attributes = $data['attrib'];
unset($data['attrib']);
} else {
$innerElements = '';
foreach ($data as $subElement => $value) {
$innerElements .= $this->elem($subElement, array(), $value);
}
$data = $innerElements;
}
}
$elems .= $this->elem($elem, $attributes, $data);
}
return $this->elem('channel', $attrib, $elems . $content, !($content === null));
}
/**
* Transforms an array of data using an optional callback, and maps it to a set
* of `<item />` tags
*
* @param array $items The list of items to be mapped
* @param mixed $callback A string function name, or array containing an object
* and a string method name
* @return string A set of RSS `<item />` elements
* @access public
*/
function items($items, $callback = null) {
if ($callback != null) {
$items = array_map($callback, $items);
}
$out = '';
$c = count($items);
for ($i = 0; $i < $c; $i++) {
$out .= $this->item(array(), $items[$i]);
}
return $out;
}
/**
* Converts an array into an `<item />` element and its contents
*
* @param array $attrib The attributes of the `<item />` element
* @param array $elements The list of elements contained in this `<item />`
* @return string An RSS `<item />` element
* @access public
*/
function item($att = array(), $elements = array()) {
$content = null;
if (isset($elements['link']) && !isset($elements['guid'])) {
$elements['guid'] = $elements['link'];
}
foreach ($elements as $key => $val) {
$attrib = array();
$escape = true;
if (is_array($val) && isset($val['convertEntities'])) {
$escape = $val['convertEntities'];
unset($val['convertEntities']);
}
switch ($key) {
case 'pubDate' :
$val = $this->time($val);
break;
case 'category' :
if (is_array($val) && !empty($val[0])) {
foreach ($val as $category) {
$attrib = array();
if (isset($category['domain'])) {
$attrib['domain'] = $category['domain'];
unset($category['domain']);
}
$categories[] = $this->elem($key, $attrib, $category);
}
$elements[$key] = implode('', $categories);
continue 2;
} else if (is_array($val) && isset($val['domain'])) {
$attrib['domain'] = $val['domain'];
}
break;
case 'link':
case 'guid':
case 'comments':
if (is_array($val) && isset($val['url'])) {
$attrib = $val;
unset($attrib['url']);
$val = $val['url'];
}
$val = $this->url($val, true);
break;
case 'source':
if (is_array($val) && isset($val['url'])) {
$attrib['url'] = $this->url($val['url'], true);
$val = $val['title'];
} elseif (is_array($val)) {
$attrib['url'] = $this->url($val[0], true);
$val = $val[1];
}
break;
case 'enclosure':
if (is_string($val['url']) && is_file(WWW_ROOT . $val['url']) && file_exists(WWW_ROOT . $val['url'])) {
if (!isset($val['length']) && strpos($val['url'], '://') === false) {
$val['length'] = sprintf("%u", filesize(WWW_ROOT . $val['url']));
}
if (!isset($val['type']) && function_exists('mime_content_type')) {
$val['type'] = mime_content_type(WWW_ROOT . $val['url']);
}
}
$val['url'] = $this->url($val['url'], true);
$attrib = $val;
$val = null;
break;
}
if (!is_null($val) && $escape) {
$val = h($val);
}
$elements[$key] = $this->elem($key, $attrib, $val);
}
if (!empty($elements)) {
$content = implode('', $elements);
}
return $this->elem('item', $att, $content, !($content === null));
}
/**
* Converts a time in any format to an RSS time
*
* @param mixed $time
* @return string An RSS-formatted timestamp
* @see TimeHelper::toRSS
*/
function time($time) {
return $this->Time->toRSS($time);
}
}

View File

@@ -0,0 +1,201 @@
<?php
/**
* Session Helper provides access to the Session in the Views.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.helpers
* @since CakePHP(tm) v 1.1.7.3328
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
if (!class_exists('cakesession')) {
require LIBS . 'cake_session.php';
}
/**
* Session Helper.
*
* Session reading from the view.
*
* @package cake
* @subpackage cake.cake.libs.view.helpers
* @link http://book.cakephp.org/view/1465/Session
*/
class SessionHelper extends CakeSession {
/**
* List of helpers used by this helper
*
* @var array
*/
var $helpers = array();
/**
* Used to determine if methods implementation is used, or bypassed
*
* @var boolean
*/
var $__active = true;
/**
* Class constructor
*
* @param string $base
*/
function __construct($base = null) {
if (Configure::read('Session.start') === true) {
parent::__construct($base, false);
$this->start();
$this->__active = true;
} else {
$this->__active = false;
}
}
/**
* Turn sessions on if 'Session.start' is set to false in core.php
*
* @param string $base
* @access public
*/
function activate($base = null) {
$this->__active = true;
}
/**
* Used to read a session values set in a controller for a key or return values for all keys.
*
* In your view: `$session->read('Controller.sessKey');`
* Calling the method without a param will return all session vars
*
* @param string $name the name of the session key you want to read
* @return values from the session vars
* @access public
* @link http://book.cakephp.org/view/1466/Methods
*/
function read($name = null) {
if ($this->__active === true && $this->__start()) {
return parent::read($name);
}
return false;
}
/**
* Used to check is a session key has been set
*
* In your view: `$session->check('Controller.sessKey');`
*
* @param string $name
* @return boolean
* @access public
* @link http://book.cakephp.org/view/1466/Methods
*/
function check($name) {
if ($this->__active === true && $this->__start()) {
return parent::check($name);
}
return false;
}
/**
* Returns last error encountered in a session
*
* In your view: `$session->error();`
*
* @return string last error
* @access public
* @link http://book.cakephp.org/view/1466/Methods
*/
function error() {
if ($this->__active === true && $this->__start()) {
return parent::error();
}
return false;
}
/**
* Used to render the message set in Controller::Session::setFlash()
*
* In your view: $session->flash('somekey');
* Will default to flash if no param is passed
*
* @param string $key The [Message.]key you are rendering in the view.
* @return boolean|string Will return the value if $key is set, or false if not set.
* @access public
* @link http://book.cakephp.org/view/1466/Methods
* @link http://book.cakephp.org/view/1467/flash
*/
function flash($key = 'flash') {
$out = false;
if ($this->__active === true && $this->__start()) {
if (parent::check('Message.' . $key)) {
$flash = parent::read('Message.' . $key);
if ($flash['element'] == 'default') {
if (!empty($flash['params']['class'])) {
$class = $flash['params']['class'];
} else {
$class = 'message';
}
$out = '<div id="' . $key . 'Message" class="' . $class . '">' . $flash['message'] . '</div>';
} elseif ($flash['element'] == '' || $flash['element'] == null) {
$out = $flash['message'];
} else {
$view =& ClassRegistry::getObject('view');
$tmpVars = $flash['params'];
$tmpVars['message'] = $flash['message'];
$out = $view->element($flash['element'], $tmpVars);
}
parent::delete('Message.' . $key);
}
}
return $out;
}
/**
* Used to check is a session is valid in a view
*
* @return boolean
* @access public
*/
function valid() {
if ($this->__active === true && $this->__start()) {
return parent::valid();
}
}
/**
* Override CakeSession::write().
* This method should not be used in a view
*
* @return boolean
* @access public
*/
function write() {
trigger_error(__('You can not write to a Session from the view', true), E_USER_WARNING);
}
/**
* Determine if Session has been started
* and attempt to start it if not
*
* @return boolean true if Session is already started, false if
* Session could not be started
* @access private
*/
function __start() {
if (!$this->started()) {
return $this->start();
}
return true;
}
}

View File

@@ -0,0 +1,336 @@
<?php
/**
* Text Helper
*
* Text manipulations: Highlight, excerpt, truncate, strip of links, convert email addresses to mailto: links...
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.helpers
* @since CakePHP(tm) v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Included libraries.
*
*/
if (!class_exists('HtmlHelper')) {
App::import('Helper', 'Html');
}
if (!class_exists('Multibyte')) {
App::import('Core', 'Multibyte');
}
/**
* Text helper library.
*
* Text manipulations: Highlight, excerpt, truncate, strip of links, convert email addresses to mailto: links...
*
* @package cake
* @subpackage cake.cake.libs.view.helpers
* @link http://book.cakephp.org/view/1469/Text
*/
class TextHelper extends AppHelper {
/**
* Highlights a given phrase in a text. You can specify any expression in highlighter that
* may include the \1 expression to include the $phrase found.
*
* ### Options:
*
* - `format` The piece of html with that the phrase will be highlighted
* - `html` If true, will ignore any HTML tags, ensuring that only the correct text is highlighted
*
* @param string $text Text to search the phrase in
* @param string $phrase The phrase that will be searched
* @param array $options An array of html attributes and options.
* @return string The highlighted text
* @access public
* @link http://book.cakephp.org/view/1469/Text#highlight-1622
*/
function highlight($text, $phrase, $options = array()) {
if (empty($phrase)) {
return $text;
}
$default = array(
'format' => '<span class="highlight">\1</span>',
'html' => false
);
$options = array_merge($default, $options);
extract($options);
if (is_array($phrase)) {
$replace = array();
$with = array();
foreach ($phrase as $key => $segment) {
$segment = "($segment)";
if ($html) {
$segment = "(?![^<]+>)$segment(?![^<]+>)";
}
$with[] = (is_array($format)) ? $format[$key] : $format;
$replace[] = "|$segment|iu";
}
return preg_replace($replace, $with, $text);
} else {
$phrase = "($phrase)";
if ($html) {
$phrase = "(?![^<]+>)$phrase(?![^<]+>)";
}
return preg_replace("|$phrase|iu", $format, $text);
}
}
/**
* Strips given text of all links (<a href=....)
*
* @param string $text Text
* @return string The text without links
* @access public
* @link http://book.cakephp.org/view/1469/Text#stripLinks-1623
*/
function stripLinks($text) {
return preg_replace('|<a\s+[^>]+>|im', '', preg_replace('|<\/a>|im', '', $text));
}
/**
* Adds links (<a href=....) to a given text, by finding text that begins with
* strings like http:// and ftp://.
*
* @param string $text Text to add links to
* @param array $options Array of HTML options.
* @return string The text with links
* @access public
* @link http://book.cakephp.org/view/1469/Text#autoLinkUrls-1619
*/
function autoLinkUrls($text, $htmlOptions = array()) {
$options = var_export($htmlOptions, true);
$text = preg_replace_callback('#(?<!href="|">)((?:https?|ftp|nntp)://[^\s<>()]+)#i', create_function('$matches',
'$Html = new HtmlHelper(); $Html->tags = $Html->loadConfig(); return $Html->link($matches[0], $matches[0],' . $options . ');'), $text);
return preg_replace_callback('#(?<!href="|">)(?<!http://|https://|ftp://|nntp://)(www\.[^\n\%\ <]+[^<\n\%\,\.\ <])(?<!\))#i',
create_function('$matches', '$Html = new HtmlHelper(); $Html->tags = $Html->loadConfig(); return $Html->link($matches[0], "http://" . $matches[0],' . $options . ');'), $text);
}
/**
* Adds email links (<a href="mailto:....) to a given text.
*
* @param string $text Text
* @param array $options Array of HTML options.
* @return string The text with links
* @access public
* @link http://book.cakephp.org/view/1469/Text#autoLinkEmails-1618
*/
function autoLinkEmails($text, $options = array()) {
$linkOptions = 'array(';
foreach ($options as $option => $value) {
$value = var_export($value, true);
$linkOptions .= "'$option' => $value, ";
}
$linkOptions .= ')';
return preg_replace_callback('#([_A-Za-z0-9+-]+(?:\.[_A-Za-z0-9+-]+)*@[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)*)#',
create_function('$matches', '$Html = new HtmlHelper(); $Html->tags = $Html->loadConfig(); return $Html->link($matches[0], "mailto:" . $matches[0],' . $linkOptions . ');'), $text);
}
/**
* Convert all links and email adresses to HTML links.
*
* @param string $text Text
* @param array $options Array of HTML options.
* @return string The text with links
* @access public
* @link http://book.cakephp.org/view/1469/Text#autoLink-1620
*/
function autoLink($text, $options = array()) {
return $this->autoLinkEmails($this->autoLinkUrls($text, $options), $options);
}
/**
* Truncates text.
*
* Cuts a string to the length of $length and replaces the last characters
* with the ending if the text is longer than length.
*
* ### Options:
*
* - `ending` Will be used as Ending and appended to the trimmed string
* - `exact` If false, $text will not be cut mid-word
* - `html` If true, HTML tags would be handled correctly
*
* @param string $text String to truncate.
* @param integer $length Length of returned string, including ellipsis.
* @param array $options An array of html attributes and options.
* @return string Trimmed string.
* @access public
* @link http://book.cakephp.org/view/1469/Text#truncate-1625
*/
function truncate($text, $length = 100, $options = array()) {
$default = array(
'ending' => '...', 'exact' => true, 'html' => false
);
$options = array_merge($default, $options);
extract($options);
if ($html) {
if (mb_strlen(preg_replace('/<.*?>/', '', $text)) <= $length) {
return $text;
}
$totalLength = mb_strlen(strip_tags($ending));
$openTags = array();
$truncate = '';
preg_match_all('/(<\/?([\w+]+)[^>]*>)?([^<>]*)/', $text, $tags, PREG_SET_ORDER);
foreach ($tags as $tag) {
if (!preg_match('/img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param/s', $tag[2])) {
if (preg_match('/<[\w]+[^>]*>/s', $tag[0])) {
array_unshift($openTags, $tag[2]);
} else if (preg_match('/<\/([\w]+)[^>]*>/s', $tag[0], $closeTag)) {
$pos = array_search($closeTag[1], $openTags);
if ($pos !== false) {
array_splice($openTags, $pos, 1);
}
}
}
$truncate .= $tag[1];
$contentLength = mb_strlen(preg_replace('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', ' ', $tag[3]));
if ($contentLength + $totalLength > $length) {
$left = $length - $totalLength;
$entitiesLength = 0;
if (preg_match_all('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', $tag[3], $entities, PREG_OFFSET_CAPTURE)) {
foreach ($entities[0] as $entity) {
if ($entity[1] + 1 - $entitiesLength <= $left) {
$left--;
$entitiesLength += mb_strlen($entity[0]);
} else {
break;
}
}
}
$truncate .= mb_substr($tag[3], 0 , $left + $entitiesLength);
break;
} else {
$truncate .= $tag[3];
$totalLength += $contentLength;
}
if ($totalLength >= $length) {
break;
}
}
} else {
if (mb_strlen($text) <= $length) {
return $text;
} else {
$truncate = mb_substr($text, 0, $length - mb_strlen($ending));
}
}
if (!$exact) {
$spacepos = mb_strrpos($truncate, ' ');
if (isset($spacepos)) {
if ($html) {
$bits = mb_substr($truncate, $spacepos);
preg_match_all('/<\/([a-z]+)>/', $bits, $droppedTags, PREG_SET_ORDER);
if (!empty($droppedTags)) {
foreach ($droppedTags as $closingTag) {
if (!in_array($closingTag[1], $openTags)) {
array_unshift($openTags, $closingTag[1]);
}
}
}
}
$truncate = mb_substr($truncate, 0, $spacepos);
}
}
$truncate .= $ending;
if ($html) {
foreach ($openTags as $tag) {
$truncate .= '</'.$tag.'>';
}
}
return $truncate;
}
/**
* Extracts an excerpt from the text surrounding the phrase with a number of characters on each side
* determined by radius.
*
* @param string $text String to search the phrase in
* @param string $phrase Phrase that will be searched for
* @param integer $radius The amount of characters that will be returned on each side of the founded phrase
* @param string $ending Ending that will be appended
* @return string Modified string
* @access public
* @link http://book.cakephp.org/view/1469/Text#excerpt-1621
*/
function excerpt($text, $phrase, $radius = 100, $ending = '...') {
if (empty($text) or empty($phrase)) {
return $this->truncate($text, $radius * 2, array('ending' => $ending));
}
$phraseLen = mb_strlen($phrase);
if ($radius < $phraseLen) {
$radius = $phraseLen;
}
$pos = mb_strpos(mb_strtolower($text), mb_strtolower($phrase));
$startPos = 0;
if ($pos > $radius) {
$startPos = $pos - $radius;
}
$textLen = mb_strlen($text);
$endPos = $pos + $phraseLen + $radius;
if ($endPos >= $textLen) {
$endPos = $textLen;
}
$excerpt = mb_substr($text, $startPos, $endPos - $startPos);
if ($startPos != 0) {
$excerpt = substr_replace($excerpt, $ending, 0, $phraseLen);
}
if ($endPos != $textLen) {
$excerpt = substr_replace($excerpt, $ending, -$phraseLen);
}
return $excerpt;
}
/**
* Creates a comma separated list where the last two items are joined with 'and', forming natural English
*
* @param array $list The list to be joined
* @param string $and The word used to join the last and second last items together with. Defaults to 'and'
* @param string $separator The separator used to join all othe other items together. Defaults to ', '
* @return string The glued together string.
* @access public
* @link http://book.cakephp.org/view/1469/Text#toList-1624
*/
function toList($list, $and = 'and', $separator = ', ') {
if (count($list) > 1) {
return implode($separator, array_slice($list, null, -1)) . ' ' . $and . ' ' . array_pop($list);
} else {
return array_pop($list);
}
}
}

View File

@@ -0,0 +1,735 @@
<?php
/**
* Time Helper class file.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.helpers
* @since CakePHP(tm) v 0.10.0.1076
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Time Helper class for easy use of time data.
*
* Manipulation of time data.
*
* @package cake
* @subpackage cake.cake.libs.view.helpers
* @link http://book.cakephp.org/view/1470/Time
*/
class TimeHelper extends AppHelper {
/**
* Converts a string representing the format for the function strftime and returns a
* windows safe and i18n aware format.
*
* @param string $format Format with specifiers for strftime function.
* Accepts the special specifier %S which mimics th modifier S for date()
* @param string UNIX timestamp
* @return string windows safe and date() function compatible format for strftime
* @access public
*/
function convertSpecifiers($format, $time = null) {
if (!$time) {
$time = time();
}
$this->__time = $time;
return preg_replace_callback('/\%(\w+)/', array($this, '__translateSpecifier'), $format);
}
/**
* Auxiliary function to translate a matched specifier element from a regular expresion into
* a windows safe and i18n aware specifier
*
* @param array $specifier match from regular expression
* @return string converted element
* @access private
*/
function __translateSpecifier($specifier) {
switch ($specifier[1]) {
case 'a':
$abday = __c('abday', 5, true);
if (is_array($abday)) {
return $abday[date('w', $this->__time)];
}
break;
case 'A':
$day = __c('day',5,true);
if (is_array($day)) {
return $day[date('w', $this->__time)];
}
break;
case 'c':
$format = __c('d_t_fmt',5,true);
if ($format != 'd_t_fmt') {
return $this->convertSpecifiers($format, $this->__time);
}
break;
case 'C':
return sprintf("%02d", date('Y', $this->__time) / 100);
case 'D':
return '%m/%d/%y';
case 'eS' :
return date('jS', $this->__time);
case 'b':
case 'h':
$months = __c('abmon', 5, true);
if (is_array($months)) {
return $months[date('n', $this->__time) -1];
}
return '%b';
case 'B':
$months = __c('mon',5,true);
if (is_array($months)) {
return $months[date('n', $this->__time) -1];
}
break;
case 'n':
return "\n";
case 'p':
case 'P':
$default = array('am' => 0, 'pm' => 1);
$meridiem = $default[date('a',$this->__time)];
$format = __c('am_pm', 5, true);
if (is_array($format)) {
$meridiem = $format[$meridiem];
return ($specifier[1] == 'P') ? strtolower($meridiem) : strtoupper($meridiem);
}
break;
case 'r':
$complete = __c('t_fmt_ampm', 5, true);
if ($complete != 't_fmt_ampm') {
return str_replace('%p',$this->__translateSpecifier(array('%p', 'p')),$complete);
}
break;
case 'R':
return date('H:i', $this->__time);
case 't':
return "\t";
case 'T':
return '%H:%M:%S';
case 'u':
return ($weekDay = date('w', $this->__time)) ? $weekDay : 7;
case 'x':
$format = __c('d_fmt', 5, true);
if ($format != 'd_fmt') {
return $this->convertSpecifiers($format, $this->__time);
}
break;
case 'X':
$format = __c('t_fmt',5,true);
if ($format != 't_fmt') {
return $this->convertSpecifiers($format, $this->__time);
}
break;
}
return $specifier[0];
}
/**
* Converts given time (in server's time zone) to user's local time, given his/her offset from GMT.
*
* @param string $serverTime UNIX timestamp
* @param int $userOffset User's offset from GMT (in hours)
* @return string UNIX timestamp
* @access public
*/
function convert($serverTime, $userOffset) {
$serverOffset = $this->serverOffset();
$gmtTime = $serverTime - $serverOffset;
$userTime = $gmtTime + $userOffset * (60*60);
return $userTime;
}
/**
* Returns server's offset from GMT in seconds.
*
* @return int Offset
* @access public
*/
function serverOffset() {
return date('Z', time());
}
/**
* Returns a UNIX timestamp, given either a UNIX timestamp or a valid strtotime() date string.
*
* @param string $dateString Datetime string
* @param int $userOffset User's offset from GMT (in hours)
* @return string Parsed timestamp
* @access public
* @link http://book.cakephp.org/view/1471/Formatting
*/
function fromString($dateString, $userOffset = null) {
if (empty($dateString)) {
return false;
}
if (is_integer($dateString) || is_numeric($dateString)) {
$date = intval($dateString);
} else {
$date = strtotime($dateString);
}
if ($userOffset !== null) {
return $this->convert($date, $userOffset);
}
if ($date === -1) {
return false;
}
return $date;
}
/**
* Returns a nicely formatted date string for given Datetime string.
*
* @param string $dateString Datetime string or Unix timestamp
* @param int $userOffset User's offset from GMT (in hours)
* @return string Formatted date string
* @access public
* @link http://book.cakephp.org/view/1471/Formatting
*/
function nice($dateString = null, $userOffset = null) {
if ($dateString != null) {
$date = $this->fromString($dateString, $userOffset);
} else {
$date = time();
}
$format = $this->convertSpecifiers('%a, %b %eS %Y, %H:%M', $date);
return strftime($format, $date);
}
/**
* Returns a formatted descriptive date string for given datetime string.
*
* If the given date is today, the returned string could be "Today, 16:54".
* If the given date was yesterday, the returned string could be "Yesterday, 16:54".
* If $dateString's year is the current year, the returned string does not
* include mention of the year.
*
* @param string $dateString Datetime string or Unix timestamp
* @param int $userOffset User's offset from GMT (in hours)
* @return string Described, relative date string
* @access public
* @link http://book.cakephp.org/view/1471/Formatting
*/
function niceShort($dateString = null, $userOffset = null) {
$date = $dateString ? $this->fromString($dateString, $userOffset) : time();
$y = $this->isThisYear($date) ? '' : ' %Y';
if ($this->isToday($date)) {
$ret = sprintf(__('Today, %s',true), strftime("%H:%M", $date));
} elseif ($this->wasYesterday($date)) {
$ret = sprintf(__('Yesterday, %s',true), strftime("%H:%M", $date));
} else {
$format = $this->convertSpecifiers("%b %eS{$y}, %H:%M", $date);
$ret = strftime($format, $date);
}
return $ret;
}
/**
* Returns a partial SQL string to search for all records between two dates.
*
* @param string $dateString Datetime string or Unix timestamp
* @param string $end Datetime string or Unix timestamp
* @param string $fieldName Name of database field to compare with
* @param int $userOffset User's offset from GMT (in hours)
* @return string Partial SQL string.
* @access public
* @link http://book.cakephp.org/view/1471/Formatting
*/
function daysAsSql($begin, $end, $fieldName, $userOffset = null) {
$begin = $this->fromString($begin, $userOffset);
$end = $this->fromString($end, $userOffset);
$begin = date('Y-m-d', $begin) . ' 00:00:00';
$end = date('Y-m-d', $end) . ' 23:59:59';
return "($fieldName >= '$begin') AND ($fieldName <= '$end')";
}
/**
* Returns a partial SQL string to search for all records between two times
* occurring on the same day.
*
* @param string $dateString Datetime string or Unix timestamp
* @param string $fieldName Name of database field to compare with
* @param int $userOffset User's offset from GMT (in hours)
* @return string Partial SQL string.
* @access public
* @link http://book.cakephp.org/view/1471/Formatting
*/
function dayAsSql($dateString, $fieldName, $userOffset = null) {
$date = $this->fromString($dateString, $userOffset);
return $this->daysAsSql($dateString, $dateString, $fieldName);
}
/**
* Returns true if given datetime string is today.
*
* @param string $dateString Datetime string or Unix timestamp
* @param int $userOffset User's offset from GMT (in hours)
* @return boolean True if datetime string is today
* @access public
*/
function isToday($dateString, $userOffset = null) {
$date = $this->fromString($dateString, $userOffset);
return date('Y-m-d', $date) == date('Y-m-d', time());
}
/**
* Returns true if given datetime string is within this week
* @param string $dateString
* @param int $userOffset User's offset from GMT (in hours)
* @return boolean True if datetime string is within current week
* @access public
* @link http://book.cakephp.org/view/1472/Testing-Time
*/
function isThisWeek($dateString, $userOffset = null) {
$date = $this->fromString($dateString, $userOffset);
return date('W Y', $date) == date('W Y', time());
}
/**
* Returns true if given datetime string is within this month
* @param string $dateString
* @param int $userOffset User's offset from GMT (in hours)
* @return boolean True if datetime string is within current month
* @access public
* @link http://book.cakephp.org/view/1472/Testing-Time
*/
function isThisMonth($dateString, $userOffset = null) {
$date = $this->fromString($dateString);
return date('m Y',$date) == date('m Y', time());
}
/**
* Returns true if given datetime string is within current year.
*
* @param string $dateString Datetime string or Unix timestamp
* @return boolean True if datetime string is within current year
* @access public
* @link http://book.cakephp.org/view/1472/Testing-Time
*/
function isThisYear($dateString, $userOffset = null) {
$date = $this->fromString($dateString, $userOffset);
return date('Y', $date) == date('Y', time());
}
/**
* Returns true if given datetime string was yesterday.
*
* @param string $dateString Datetime string or Unix timestamp
* @param int $userOffset User's offset from GMT (in hours)
* @return boolean True if datetime string was yesterday
* @access public
* @link http://book.cakephp.org/view/1472/Testing-Time
*
*/
function wasYesterday($dateString, $userOffset = null) {
$date = $this->fromString($dateString, $userOffset);
return date('Y-m-d', $date) == date('Y-m-d', strtotime('yesterday'));
}
/**
* Returns true if given datetime string is tomorrow.
*
* @param string $dateString Datetime string or Unix timestamp
* @param int $userOffset User's offset from GMT (in hours)
* @return boolean True if datetime string was yesterday
* @access public
* @link http://book.cakephp.org/view/1472/Testing-Time
*/
function isTomorrow($dateString, $userOffset = null) {
$date = $this->fromString($dateString, $userOffset);
return date('Y-m-d', $date) == date('Y-m-d', strtotime('tomorrow'));
}
/**
* Returns the quarter
*
* @param string $dateString
* @param boolean $range if true returns a range in Y-m-d format
* @return boolean True if datetime string is within current week
* @access public
* @link http://book.cakephp.org/view/1471/Formatting
*/
function toQuarter($dateString, $range = false) {
$time = $this->fromString($dateString);
$date = ceil(date('m', $time) / 3);
if ($range === true) {
$range = 'Y-m-d';
}
if ($range !== false) {
$year = date('Y', $time);
switch ($date) {
case 1:
$date = array($year.'-01-01', $year.'-03-31');
break;
case 2:
$date = array($year.'-04-01', $year.'-06-30');
break;
case 3:
$date = array($year.'-07-01', $year.'-09-30');
break;
case 4:
$date = array($year.'-10-01', $year.'-12-31');
break;
}
}
return $date;
}
/**
* Returns a UNIX timestamp from a textual datetime description. Wrapper for PHP function strtotime().
*
* @param string $dateString Datetime string to be represented as a Unix timestamp
* @param int $userOffset User's offset from GMT (in hours)
* @return integer Unix timestamp
* @access public
* @link http://book.cakephp.org/view/1471/Formatting
*/
function toUnix($dateString, $userOffset = null) {
return $this->fromString($dateString, $userOffset);
}
/**
* Returns a date formatted for Atom RSS feeds.
*
* @param string $dateString Datetime string or Unix timestamp
* @param int $userOffset User's offset from GMT (in hours)
* @return string Formatted date string
* @access public
* @link http://book.cakephp.org/view/1471/Formatting
*/
function toAtom($dateString, $userOffset = null) {
$date = $this->fromString($dateString, $userOffset);
return date('Y-m-d\TH:i:s\Z', $date);
}
/**
* Formats date for RSS feeds
*
* @param string $dateString Datetime string or Unix timestamp
* @param int $userOffset User's offset from GMT (in hours)
* @return string Formatted date string
* @access public
* @link http://book.cakephp.org/view/1471/Formatting
*/
function toRSS($dateString, $userOffset = null) {
$date = $this->fromString($dateString, $userOffset);
return date("r", $date);
}
/**
* Returns either a relative date or a formatted date depending
* on the difference between the current time and given datetime.
* $datetime should be in a <i>strtotime</i> - parsable format, like MySQL's datetime datatype.
*
* ### Options:
*
* - `format` => a fall back format if the relative time is longer than the duration specified by end
* - `end` => The end of relative time telling
* - `userOffset` => Users offset from GMT (in hours)
*
* Relative dates look something like this:
* 3 weeks, 4 days ago
* 15 seconds ago
*
* Default date formatting is d/m/yy e.g: on 18/2/09
*
* The returned string includes 'ago' or 'on' and assumes you'll properly add a word
* like 'Posted ' before the function output.
*
* @param string $dateString Datetime string or Unix timestamp
* @param array $options Default format if timestamp is used in $dateString
* @return string Relative time string.
* @access public
* @link http://book.cakephp.org/view/1471/Formatting
*/
function timeAgoInWords($dateTime, $options = array()) {
$userOffset = null;
if (is_array($options) && isset($options['userOffset'])) {
$userOffset = $options['userOffset'];
}
$now = time();
if (!is_null($userOffset)) {
$now = $this->convert(time(), $userOffset);
}
$inSeconds = $this->fromString($dateTime, $userOffset);
$backwards = ($inSeconds > $now);
$format = 'j/n/y';
$end = '+1 month';
if (is_array($options)) {
if (isset($options['format'])) {
$format = $options['format'];
unset($options['format']);
}
if (isset($options['end'])) {
$end = $options['end'];
unset($options['end']);
}
} else {
$format = $options;
}
if ($backwards) {
$futureTime = $inSeconds;
$pastTime = $now;
} else {
$futureTime = $now;
$pastTime = $inSeconds;
}
$diff = $futureTime - $pastTime;
// If more than a week, then take into account the length of months
if ($diff >= 604800) {
$current = array();
$date = array();
list($future['H'], $future['i'], $future['s'], $future['d'], $future['m'], $future['Y']) = explode('/', date('H/i/s/d/m/Y', $futureTime));
list($past['H'], $past['i'], $past['s'], $past['d'], $past['m'], $past['Y']) = explode('/', date('H/i/s/d/m/Y', $pastTime));
$years = $months = $weeks = $days = $hours = $minutes = $seconds = 0;
if ($future['Y'] == $past['Y'] && $future['m'] == $past['m']) {
$months = 0;
$years = 0;
} else {
if ($future['Y'] == $past['Y']) {
$months = $future['m'] - $past['m'];
} else {
$years = $future['Y'] - $past['Y'];
$months = $future['m'] + ((12 * $years) - $past['m']);
if ($months >= 12) {
$years = floor($months / 12);
$months = $months - ($years * 12);
}
if ($future['m'] < $past['m'] && $future['Y'] - $past['Y'] == 1) {
$years --;
}
}
}
if ($future['d'] >= $past['d']) {
$days = $future['d'] - $past['d'];
} else {
$daysInPastMonth = date('t', $pastTime);
$daysInFutureMonth = date('t', mktime(0, 0, 0, $future['m'] - 1, 1, $future['Y']));
if (!$backwards) {
$days = ($daysInPastMonth - $past['d']) + $future['d'];
} else {
$days = ($daysInFutureMonth - $past['d']) + $future['d'];
}
if ($future['m'] != $past['m']) {
$months --;
}
}
if ($months == 0 && $years >= 1 && $diff < ($years * 31536000)) {
$months = 11;
$years --;
}
if ($months >= 12) {
$years = $years + 1;
$months = $months - 12;
}
if ($days >= 7) {
$weeks = floor($days / 7);
$days = $days - ($weeks * 7);
}
} else {
$years = $months = $weeks = 0;
$days = floor($diff / 86400);
$diff = $diff - ($days * 86400);
$hours = floor($diff / 3600);
$diff = $diff - ($hours * 3600);
$minutes = floor($diff / 60);
$diff = $diff - ($minutes * 60);
$seconds = $diff;
}
$relativeDate = '';
$diff = $futureTime - $pastTime;
if ($diff > abs($now - $this->fromString($end))) {
$relativeDate = sprintf(__('on %s',true), date($format, $inSeconds));
} else {
if ($years > 0) {
// years and months and days
$relativeDate .= ($relativeDate ? ', ' : '') . $years . ' ' . __n('year', 'years', $years, true);
$relativeDate .= $months > 0 ? ($relativeDate ? ', ' : '') . $months . ' ' . __n('month', 'months', $months, true) : '';
$relativeDate .= $weeks > 0 ? ($relativeDate ? ', ' : '') . $weeks . ' ' . __n('week', 'weeks', $weeks, true) : '';
$relativeDate .= $days > 0 ? ($relativeDate ? ', ' : '') . $days . ' ' . __n('day', 'days', $days, true) : '';
} elseif (abs($months) > 0) {
// months, weeks and days
$relativeDate .= ($relativeDate ? ', ' : '') . $months . ' ' . __n('month', 'months', $months, true);
$relativeDate .= $weeks > 0 ? ($relativeDate ? ', ' : '') . $weeks . ' ' . __n('week', 'weeks', $weeks, true) : '';
$relativeDate .= $days > 0 ? ($relativeDate ? ', ' : '') . $days . ' ' . __n('day', 'days', $days, true) : '';
} elseif (abs($weeks) > 0) {
// weeks and days
$relativeDate .= ($relativeDate ? ', ' : '') . $weeks . ' ' . __n('week', 'weeks', $weeks, true);
$relativeDate .= $days > 0 ? ($relativeDate ? ', ' : '') . $days . ' ' . __n('day', 'days', $days, true) : '';
} elseif (abs($days) > 0) {
// days and hours
$relativeDate .= ($relativeDate ? ', ' : '') . $days . ' ' . __n('day', 'days', $days, true);
$relativeDate .= $hours > 0 ? ($relativeDate ? ', ' : '') . $hours . ' ' . __n('hour', 'hours', $hours, true) : '';
} elseif (abs($hours) > 0) {
// hours and minutes
$relativeDate .= ($relativeDate ? ', ' : '') . $hours . ' ' . __n('hour', 'hours', $hours, true);
$relativeDate .= $minutes > 0 ? ($relativeDate ? ', ' : '') . $minutes . ' ' . __n('minute', 'minutes', $minutes, true) : '';
} elseif (abs($minutes) > 0) {
// minutes only
$relativeDate .= ($relativeDate ? ', ' : '') . $minutes . ' ' . __n('minute', 'minutes', $minutes, true);
} else {
// seconds only
$relativeDate .= ($relativeDate ? ', ' : '') . $seconds . ' ' . __n('second', 'seconds', $seconds, true);
}
if (!$backwards) {
$relativeDate = sprintf(__('%s ago', true), $relativeDate);
}
}
return $relativeDate;
}
/**
* Alias for timeAgoInWords
*
* @param mixed $dateTime Datetime string (strtotime-compatible) or Unix timestamp
* @param mixed $options Default format string, if timestamp is used in $dateTime, or an array of options to be passed
* on to timeAgoInWords().
* @return string Relative time string.
* @see TimeHelper::timeAgoInWords
* @access public
* @deprecated This method alias will be removed in future versions.
* @link http://book.cakephp.org/view/1471/Formatting
*/
function relativeTime($dateTime, $options = array()) {
return $this->timeAgoInWords($dateTime, $options);
}
/**
* Returns true if specified datetime was within the interval specified, else false.
*
* @param mixed $timeInterval the numeric value with space then time type.
* Example of valid types: 6 hours, 2 days, 1 minute.
* @param mixed $dateString the datestring or unix timestamp to compare
* @param int $userOffset User's offset from GMT (in hours)
* @return bool
* @access public
* @link http://book.cakephp.org/view/1472/Testing-Time
*/
function wasWithinLast($timeInterval, $dateString, $userOffset = null) {
$tmp = str_replace(' ', '', $timeInterval);
if (is_numeric($tmp)) {
$timeInterval = $tmp . ' ' . __('days', true);
}
$date = $this->fromString($dateString, $userOffset);
$interval = $this->fromString('-'.$timeInterval);
if ($date >= $interval && $date <= time()) {
return true;
}
return false;
}
/**
* Returns gmt, given either a UNIX timestamp or a valid strtotime() date string.
*
* @param string $dateString Datetime string
* @return string Formatted date string
* @access public
* @link http://book.cakephp.org/view/1471/Formatting
*/
function gmt($string = null) {
if ($string != null) {
$string = $this->fromString($string);
} else {
$string = time();
}
$string = $this->fromString($string);
$hour = intval(date("G", $string));
$minute = intval(date("i", $string));
$second = intval(date("s", $string));
$month = intval(date("n", $string));
$day = intval(date("j", $string));
$year = intval(date("Y", $string));
return gmmktime($hour, $minute, $second, $month, $day, $year);
}
/**
* Returns a formatted date string, given either a UNIX timestamp or a valid strtotime() date string.
* This function also accepts a time string and a format string as first and second parameters.
* In that case this function behaves as a wrapper for TimeHelper::i18nFormat()
*
* @param string $format date format string (or a DateTime string)
* @param string $dateString Datetime string (or a date format string)
* @param boolean $invalid flag to ignore results of fromString == false
* @param int $userOffset User's offset from GMT (in hours)
* @return string Formatted date string
* @access public
*/
function format($format, $date = null, $invalid = false, $userOffset = null) {
$time = $this->fromString($date, $userOffset);
$_time = $this->fromString($format, $userOffset);
if (is_numeric($_time) && $time === false) {
$format = $date;
return $this->i18nFormat($_time, $format, $invalid, $userOffset);
}
if ($time === false && $invalid !== false) {
return $invalid;
}
return date($format, $time);
}
/**
* Returns a formatted date string, given either a UNIX timestamp or a valid strtotime() date string.
* It take in account the default date format for the current language if a LC_TIME file is used.
*
* @param string $dateString Datetime string
* @param string $format strftime format string.
* @param boolean $invalid flag to ignore results of fromString == false
* @param int $userOffset User's offset from GMT (in hours)
* @return string Formatted and translated date string @access public
* @access public
*/
function i18nFormat($date, $format = null, $invalid = false, $userOffset = null) {
$date = $this->fromString($date, $userOffset);
if ($date === false && $invalid !== false) {
return $invalid;
}
if (empty($format)) {
$format = '%x';
}
$format = $this->convertSpecifiers($format, $date);
return strftime($format, $date);
}
}

View File

@@ -0,0 +1,174 @@
<?php
/**
* XML Helper class file.
*
* Simplifies the output of XML documents.
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.libs.view.helpers
* @since CakePHP(tm) v 1.2
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Core', array('Xml', 'Set'));
/**
* XML Helper class for easy output of XML structures.
*
* XmlHelper encloses all methods needed while working with XML documents.
*
* @package cake
* @subpackage cake.cake.libs.view.helpers
* @link http://book.cakephp.org/view/1473/XML
*/
class XmlHelper extends AppHelper {
/**
* Default document encoding
*
* @access public
* @var string
*/
var $encoding = 'UTF-8';
/**
* Constructor
*
* @return void
*/
function __construct() {
parent::__construct();
$this->Xml =& new Xml();
$this->Xml->options(array('verifyNs' => false));
}
/**
* Returns an XML document header
*
* @param array $attrib Header tag attributes
* @return string XML header
* @access public
* @link http://book.cakephp.org/view/1476/header
*/
function header($attrib = array()) {
if (Configure::read('App.encoding') !== null) {
$this->encoding = Configure::read('App.encoding');
}
if (is_array($attrib)) {
$attrib = array_merge(array('encoding' => $this->encoding), $attrib);
}
if (is_string($attrib) && strpos($attrib, 'xml') !== 0) {
$attrib = 'xml ' . $attrib;
}
return $this->Xml->header($attrib);
}
/**
* Adds a namespace to any documents generated
*
* @param string $name The namespace name
* @param string $url The namespace URI; can be empty if in the default namespace map
* @return boolean False if no URL is specified, and the namespace does not exist
* default namespace map, otherwise true
* @deprecated
* @see Xml::addNs()
*/
function addNs($name, $url = null) {
return $this->Xml->addNamespace($name, $url);
}
/**
* Removes a namespace added in addNs()
*
* @param string $name The namespace name or URI
* @deprecated
* @see Xml::removeNs()
* @access public
*/
function removeNs($name) {
return $this->Xml->removeGlobalNamespace($name);
}
/**
* Generates an XML element
*
* @param string $name The name of the XML element
* @param array $attrib The attributes of the XML element
* @param mixed $content XML element content
* @param boolean $endTag Whether the end tag of the element should be printed
* @return string XML
* @access public
* @link http://book.cakephp.org/view/1475/elem
*/
function elem($name, $attrib = array(), $content = null, $endTag = true) {
$namespace = null;
if (isset($attrib['namespace'])) {
$namespace = $attrib['namespace'];
unset($attrib['namespace']);
}
$cdata = false;
if (is_array($content) && isset($content['cdata'])) {
$cdata = true;
unset($content['cdata']);
}
if (is_array($content) && array_key_exists('value', $content)) {
$content = $content['value'];
}
$children = array();
if (is_array($content)) {
$children = $content;
$content = null;
}
$elem =& $this->Xml->createElement($name, $content, $attrib, $namespace);
foreach ($children as $child) {
$elem->createElement($child);
}
$out = $elem->toString(array('cdata' => $cdata, 'leaveOpen' => !$endTag));
if (!$endTag) {
$this->Xml =& $elem;
}
return $out;
}
/**
* Create closing tag for current element
*
* @return string
* @access public
*/
function closeElem() {
$name = $this->Xml->name();
if ($parent =& $this->Xml->parent()) {
$this->Xml =& $parent;
}
return '</' . $name . '>';
}
/**
* Serializes a model resultset into XML
*
* @param mixed $data The content to be converted to XML
* @param array $options The data formatting options. For a list of valid options, see
* Xml::__construct().
* @return string A copy of $data in XML format
* @see Xml::__construct()
* @access public
* @link http://book.cakephp.org/view/1474/serialize
*/
function serialize($data, $options = array()) {
$options += array('attributes' => false, 'format' => 'attributes');
$data =& new Xml($data, $options);
return $data->toString($options + array('header' => false));
}
}

Some files were not shown because too many files have changed in this diff Show More