991843601e332606d86be1e1a7b3896f3a835f86

Author: yandod

Date: 2009-12-12 11:25:31 -0500

import cakephp.

diff --git a/cake/LICENSE.txt b/cake/LICENSE.txt new file mode 100755 index 0000000..e54a557 --- /dev/null +++ b/cake/LICENSE.txt @@ -0,0 +1,22 @@ +The MIT License + +CakePHP(tm) : The Rapid Development PHP Framework (http://www.cakephp.org) +Copyright 2005-2007, Cake Software Foundation, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/cake/VERSION.txt b/cake/VERSION.txt new file mode 100755 index 0000000..3a1f10e --- /dev/null +++ b/cake/VERSION.txt @@ -0,0 +1 @@ +1.2.5 \ No newline at end of file diff --git a/cake/basics.php b/cake/basics.php new file mode 100755 index 0000000..5e387c5 --- /dev/null +++ b/cake/basics.php @@ -0,0 +1,940 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Basic Cake functionality. + * + * Core functions for including other source files, loading models and so forth. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Basic defines for timing functions. + */ + define('SECOND', 1); + define('MINUTE', 60 * SECOND); + define('HOUR', 60 * MINUTE); + define('DAY', 24 * HOUR); + define('WEEK', 7 * DAY); + define('MONTH', 30 * DAY); + define('YEAR', 365 * DAY); +/** + * Patch for PHP < 5.0 + */ +if (!function_exists('clone')) { + if (version_compare(PHP_VERSION, '5.0') < 0) { + eval (' + function clone($object) + { + return $object; + }'); + } +} +/** + * Loads configuration files. Receives a set of configuration files + * to load. + * Example: + * + * `config('config1', 'config2');` + * + * @return boolean Success + */ + function config() { + $args = func_get_args(); + foreach ($args as $arg) { + if ($arg === 'database' && file_exists(CONFIGS . 'database.php')) { + include_once(CONFIGS . $arg . '.php'); + } elseif (file_exists(CONFIGS . $arg . '.php')) { + include_once(CONFIGS . $arg . '.php'); + + if (count($args) == 1) { + return true; + } + } else { + if (count($args) == 1) { + return false; + } + } + } + return true; + } +/** + * Loads component/components from LIBS. Takes optional number of parameters. + * + * Example: + * + * `uses('flay', 'time');` + * + * @param string $name Filename without the .php part + */ + function uses() { + $args = func_get_args(); + foreach ($args as $file) { + require_once(LIBS . strtolower($file) . '.php'); + } + } +/** + * Prints out debug information about given variable. + * + * Only runs if debug level is greater than zero. + * + * @param boolean $var Variable to show debug information for. + * @param boolean $showHtml If set to true, the method prints the debug data in a screen-friendly way. + * @param boolean $showFrom If set to true, the method prints from where the function was called. + * @link http://book.cakephp.org/view/458/Basic-Debugging + */ + function debug($var = false, $showHtml = false, $showFrom = true) { + if (Configure::read() > 0) { + if ($showFrom) { + $calledFrom = debug_backtrace(); + echo '<strong>' . substr(str_replace(ROOT, '', $calledFrom[0]['file']), 1) . '</strong>'; + echo ' (line <strong>' . $calledFrom[0]['line'] . '</strong>)'; + } + echo "\n<pre class=\"cake-debug\">\n"; + + $var = print_r($var, true); + if ($showHtml) { + $var = str_replace('<', '&lt;', str_replace('>', '&gt;', $var)); + } + echo $var . "\n</pre>\n"; + } + } +if (!function_exists('getMicrotime')) { +/** + * Returns microtime for execution time checking + * + * @return float Microtime + */ + function getMicrotime() { + list($usec, $sec) = explode(' ', microtime()); + return ((float)$usec + (float)$sec); + } +} +if (!function_exists('sortByKey')) { +/** + * Sorts given $array by key $sortby. + * + * @param array $array Array to sort + * @param string $sortby Sort by this key + * @param string $order Sort order asc/desc (ascending or descending). + * @param integer $type Type of sorting to perform + * @return mixed Sorted array + */ + function sortByKey(&$array, $sortby, $order = 'asc', $type = SORT_NUMERIC) { + if (!is_array($array)) { + return null; + } + + foreach ($array as $key => $val) { + $sa[$key] = $val[$sortby]; + } + + if ($order == 'asc') { + asort($sa, $type); + } else { + arsort($sa, $type); + } + + foreach ($sa as $key => $val) { + $out[] = $array[$key]; + } + return $out; + } +} +if (!function_exists('array_combine')) { +/** + * Combines given identical arrays by using the first array's values as keys, + * and the second one's values as values. (Implemented for backwards compatibility with PHP4) + * + * @param array $a1 Array to use for keys + * @param array $a2 Array to use for values + * @return mixed Outputs either combined array or false. + */ + function array_combine($a1, $a2) { + $a1 = array_values($a1); + $a2 = array_values($a2); + $c1 = count($a1); + $c2 = count($a2); + + if ($c1 != $c2) { + return false; + } + if ($c1 <= 0) { + return false; + } + $output = array(); + + for ($i = 0; $i < $c1; $i++) { + $output[$a1[$i]] = $a2[$i]; + } + return $output; + } +} +/** + * Convenience method for htmlspecialchars. + * + * @param string $text Text to wrap through htmlspecialchars + * @param string $charset Character set to use when escaping. Defaults to config value in 'App.encoding' or 'UTF-8' + * @return string Wrapped text + * @link http://book.cakephp.org/view/703/h + */ + function h($text, $charset = null) { + if (is_array($text)) { + return array_map('h', $text); + } + if (empty($charset)) { + $charset = Configure::read('App.encoding'); + } + if (empty($charset)) { + $charset = 'UTF-8'; + } + return htmlspecialchars($text, ENT_QUOTES, $charset); + } +/** + * Returns an array of all the given parameters. + * + * Example: + * + * `a('a', 'b')` + * + * Would return: + * + * `array('a', 'b')` + * + * @return array Array of given parameters + * @link http://book.cakephp.org/view/694/a + */ + function a() { + $args = func_get_args(); + return $args; + } +/** + * Constructs associative array from pairs of arguments. + * + * Example: + * + * `aa('a','b')` + * + * Would return: + * + * `array('a'=>'b')` + * + * @return array Associative array + * @link http://book.cakephp.org/view/695/aa + */ + function aa() { + $args = func_get_args(); + $argc = count($args); + for ($i = 0; $i < $argc; $i++) { + if ($i + 1 < $argc) { + $a[$args[$i]] = $args[$i + 1]; + } else { + $a[$args[$i]] = null; + } + $i++; + } + return $a; + } +/** + * Convenience method for echo(). + * + * @param string $text String to echo + * @link http://book.cakephp.org/view/700/e + */ + function e($text) { + echo $text; + } +/** + * Convenience method for strtolower(). + * + * @param string $str String to lowercase + * @return string Lowercased string + * @link http://book.cakephp.org/view/705/low + */ + function low($str) { + return strtolower($str); + } +/** + * Convenience method for strtoupper(). + * + * @param string $str String to uppercase + * @return string Uppercased string + * @link http://book.cakephp.org/view/710/up + */ + function up($str) { + return strtoupper($str); + } +/** + * Convenience method for str_replace(). + * + * @param string $search String to be replaced + * @param string $replace String to insert + * @param string $subject String to search + * @return string Replaced string + * @link http://book.cakephp.org/view/708/r + */ + function r($search, $replace, $subject) { + return str_replace($search, $replace, $subject); + } +/** + * Print_r convenience function, which prints out <PRE> tags around + * the output of given array. Similar to debug(). + * + * @see debug() + * @param array $var Variable to print out + * @param boolean $showFrom If set to true, the method prints from where the function was called + * @link http://book.cakephp.org/view/707/pr + */ + function pr($var) { + if (Configure::read() > 0) { + echo '<pre>'; + print_r($var); + echo '</pre>'; + } + } +/** + * Display parameters. + * + * @param mixed $p Parameter as string or array + * @return string + */ + function params($p) { + if (!is_array($p) || count($p) == 0) { + return null; + } + if (is_array($p[0]) && count($p) == 1) { + return $p[0]; + } + return $p; + } +/** + * Merge a group of arrays + * + * @param array First array + * @param array Second array + * @param array Third array + * @param array Etc... + * @return array All array parameters merged into one + * @link http://book.cakephp.org/view/696/am + */ + function am() { + $r = array(); + $args = func_get_args(); + foreach ($args as $a) { + if (!is_array($a)) { + $a = array($a); + } + $r = array_merge($r, $a); + } + return $r; + } +/** + * Gets an environment variable from available sources, and provides emulation + * for unsupported or inconsistent environment variables (i.e. DOCUMENT_ROOT on + * IIS, or SCRIPT_NAME in CGI mode). Also exposes some additional custom + * environment information. + * + * @param string $key Environment variable name. + * @return string Environment variable setting. + * @link http://book.cakephp.org/view/701/env + */ + function env($key) { + if ($key == 'HTTPS') { + if (isset($_SERVER['HTTPS'])) { + return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'); + } + return (strpos(env('SCRIPT_URI'), 'https://') === 0); + } + + if ($key == 'SCRIPT_NAME') { + if (env('CGI_MODE') && isset($_ENV['SCRIPT_URL'])) { + $key = 'SCRIPT_URL'; + } + } + + $val = null; + if (isset($_SERVER[$key])) { + $val = $_SERVER[$key]; + } elseif (isset($_ENV[$key])) { + $val = $_ENV[$key]; + } elseif (getenv($key) !== false) { + $val = getenv($key); + } + + if ($key === 'REMOTE_ADDR' && $val === env('SERVER_ADDR')) { + $addr = env('HTTP_PC_REMOTE_ADDR'); + if ($addr !== null) { + $val = $addr; + } + } + + if ($val !== null) { + return $val; + } + + switch ($key) { + case 'SCRIPT_FILENAME': + if (defined('SERVER_IIS') && SERVER_IIS === true) { + return str_replace('\\\\', '\\', env('PATH_TRANSLATED')); + } + break; + case 'DOCUMENT_ROOT': + $name = env('SCRIPT_NAME'); + $filename = env('SCRIPT_FILENAME'); + $offset = 0; + if (!strpos($name, '.php')) { + $offset = 4; + } + return substr($filename, 0, strlen($filename) - (strlen($name) + $offset)); + break; + case 'PHP_SELF': + return str_replace(env('DOCUMENT_ROOT'), '', env('SCRIPT_FILENAME')); + break; + case 'CGI_MODE': + return (PHP_SAPI === 'cgi'); + break; + case 'HTTP_BASE': + $host = env('HTTP_HOST'); + if (substr_count($host, '.') !== 1) { + return preg_replace('/^([^.])*/i', null, env('HTTP_HOST')); + } + return '.' . $host; + break; + } + return null; + } +if (!function_exists('file_put_contents')) { +/** + * Writes data into file. + * + * If file exists, it will be overwritten. If data is an array, it will be join()ed with an empty string. + * + * @param string $fileName File name. + * @param mixed $data String or array. + * @return boolean Success + */ + function file_put_contents($fileName, $data) { + if (is_array($data)) { + $data = join('', $data); + } + $res = @fopen($fileName, 'w+b'); + + if ($res) { + $write = @fwrite($res, $data); + if ($write === false) { + return false; + } else { + @fclose($res); + return $write; + } + } + return false; + } +} +/** + * Reads/writes temporary data to cache files or session. + * + * @param string $path File path within /tmp to save the file. + * @param mixed $data The data to save to the temporary file. + * @param mixed $expires A valid strtotime string when the data expires. + * @param string $target The target of the cached data; either 'cache' or 'public'. + * @return mixed The contents of the temporary file. + * @deprecated Please use Cache::write() instead + */ + function cache($path, $data = null, $expires = '+1 day', $target = 'cache') { + if (Configure::read('Cache.disable')) { + return null; + } + $now = time(); + + if (!is_numeric($expires)) { + $expires = strtotime($expires, $now); + } + + switch (low($target)) { + case 'cache': + $filename = CACHE . $path; + break; + case 'public': + $filename = WWW_ROOT . $path; + break; + case 'tmp': + $filename = TMP . $path; + break; + } + $timediff = $expires - $now; + $filetime = false; + + if (file_exists($filename)) { + $filetime = @filemtime($filename); + } + + if ($data === null) { + if (file_exists($filename) && $filetime !== false) { + if ($filetime + $timediff < $now) { + @unlink($filename); + } else { + $data = @file_get_contents($filename); + } + } + } elseif (is_writable(dirname($filename))) { + @file_put_contents($filename, $data); + } + return $data; + } +/** + * Used to delete files in the cache directories, or clear contents of cache directories + * + * @param mixed $params As String name to be searched for deletion, if name is a directory all files in + * directory will be deleted. If array, names to be searched for deletion. If clearCache() without params, + * all files in app/tmp/cache/views will be deleted + * @param string $type Directory in tmp/cache defaults to view directory + * @param string $ext The file extension you are deleting + * @return true if files found and deleted false otherwise + */ + function clearCache($params = null, $type = 'views', $ext = '.php') { + if (is_string($params) || $params === null) { + $params = preg_replace('/\/\//', '/', $params); + $cache = CACHE . $type . DS . $params; + + if (is_file($cache . $ext)) { + @unlink($cache . $ext); + return true; + } elseif (is_dir($cache)) { + $files = glob($cache . '*'); + + if ($files === false) { + return false; + } + + foreach ($files as $file) { + if (is_file($file)) { + @unlink($file); + } + } + return true; + } else { + $cache = array( + CACHE . $type . DS . '*' . $params . $ext, + CACHE . $type . DS . '*' . $params . '_*' . $ext + ); + $files = array(); + while ($search = array_shift($cache)) { + $results = glob($search); + if ($results !== false) { + $files = array_merge($files, $results); + } + } + if (empty($files)) { + return false; + } + foreach ($files as $file) { + if (is_file($file)) { + @unlink($file); + } + } + return true; + } + } elseif (is_array($params)) { + foreach ($params as $file) { + clearCache($file, $type, $ext); + } + return true; + } + return false; + } +/** + * Recursively strips slashes from all values in an array + * + * @param array $values Array of values to strip slashes + * @return mixed What is returned from calling stripslashes + * @link http://book.cakephp.org/view/709/stripslashes_deep + */ + function stripslashes_deep($values) { + if (is_array($values)) { + foreach ($values as $key => $value) { + $values[$key] = stripslashes_deep($value); + } + } else { + $values = stripslashes($values); + } + return $values; + } +/** + * Returns a translated string if one is found; Otherwise, the submitted message. + * + * @param string $singular Text to translate + * @param boolean $return Set to true to return translated string, or false to echo + * @return mixed translated string if $return is false string will be echoed + * @link http://book.cakephp.org/view/693/__ + */ + function __($singular, $return = false) { + if (!$singular) { + return; + } + if (!class_exists('I18n')) { + App::import('Core', 'i18n'); + } + + if ($return === false) { + echo I18n::translate($singular); + } else { + return I18n::translate($singular); + } + } +/** + * Returns correct plural form of message identified by $singular and $plural for count $count. + * Some languages have more than one form for plural messages dependent on the count. + * + * @param string $singular Singular text to translate + * @param string $plural Plural text + * @param integer $count Count + * @param boolean $return true to return, false to echo + * @return mixed plural form of translated string if $return is false string will be echoed + */ + function __n($singular, $plural, $count, $return = false) { + if (!$singular) { + return; + } + if (!class_exists('I18n')) { + App::import('Core', 'i18n'); + } + + if ($return === false) { + echo I18n::translate($singular, $plural, null, 6, $count); + } else { + return I18n::translate($singular, $plural, null, 6, $count); + } + } +/** + * Allows you to override the current domain for a single message lookup. + * + * @param string $domain Domain + * @param string $msg String to translate + * @param string $return true to return, false to echo + * @return translated string if $return is false string will be echoed + */ + function __d($domain, $msg, $return = false) { + if (!$msg) { + return; + } + if (!class_exists('I18n')) { + App::import('Core', 'i18n'); + } + + if ($return === false) { + echo I18n::translate($msg, null, $domain); + } else { + return I18n::translate($msg, null, $domain); + } + } +/** + * Allows you to override the current domain for a single plural message lookup. + * Returns correct plural form of message identified by $singular and $plural for count $count + * from domain $domain. + * + * @param string $domain Domain + * @param string $singular Singular string to translate + * @param string $plural Plural + * @param integer $count Count + * @param boolean $return true to return, false to echo + * @return plural form of translated string if $return is false string will be echoed + */ + function __dn($domain, $singular, $plural, $count, $return = false) { + if (!$singular) { + return; + } + if (!class_exists('I18n')) { + App::import('Core', 'i18n'); + } + + if ($return === false) { + echo I18n::translate($singular, $plural, $domain, 6, $count); + } else { + return I18n::translate($singular, $plural, $domain, 6, $count); + } + } +/** + * Allows you to override the current domain for a single message lookup. + * It also allows you to specify a category. + * + * The category argument allows a specific category of the locale settings to be used for fetching a message. + * Valid categories are: LC_CTYPE, LC_NUMERIC, LC_TIME, LC_COLLATE, LC_MONETARY, LC_MESSAGES and LC_ALL. + * + * Note that the category must be specified with a numeric value, instead of the constant name. The values are: + * + * - LC_ALL 0 + * - LC_COLLATE 1 + * - LC_CTYPE 2 + * - LC_MONETARY 3 + * - LC_NUMERIC 4 + * - LC_TIME 5 + * - LC_MESSAGES 6 + * + * @param string $domain Domain + * @param string $msg Message to translate + * @param integer $category Category + * @param boolean $return true to return, false to echo + * @return translated string if $return is false string will be echoed + */ + function __dc($domain, $msg, $category, $return = false) { + if (!$msg) { + return; + } + if (!class_exists('I18n')) { + App::import('Core', 'i18n'); + } + + if ($return === false) { + echo I18n::translate($msg, null, $domain, $category); + } else { + return I18n::translate($msg, null, $domain, $category); + } + } +/** + * Allows you to override the current domain for a single plural message lookup. + * It also allows you to specify a category. + * Returns correct plural form of message identified by $singular and $plural for count $count + * from domain $domain. + * + * The category argument allows a specific category of the locale settings to be used for fetching a message. + * Valid categories are: LC_CTYPE, LC_NUMERIC, LC_TIME, LC_COLLATE, LC_MONETARY, LC_MESSAGES and LC_ALL. + * + * Note that the category must be specified with a numeric value, instead of the constant name. The values are: + * + * - LC_ALL 0 + * - LC_COLLATE 1 + * - LC_CTYPE 2 + * - LC_MONETARY 3 + * - LC_NUMERIC 4 + * - LC_TIME 5 + * - LC_MESSAGES 6 + * + * @param string $domain Domain + * @param string $singular Singular string to translate + * @param string $plural Plural + * @param integer $count Count + * @param integer $category Category + * @param boolean $return true to return, false to echo + * @return plural form of translated string if $return is false string will be echoed + */ + function __dcn($domain, $singular, $plural, $count, $category, $return = false) { + if (!$singular) { + return; + } + if (!class_exists('I18n')) { + App::import('Core', 'i18n'); + } + + if ($return === false) { + echo I18n::translate($singular, $plural, $domain, $category, $count); + } else { + return I18n::translate($singular, $plural, $domain, $category, $count); + } + } +/** + * The category argument allows a specific category of the locale settings to be used for fetching a message. + * Valid categories are: LC_CTYPE, LC_NUMERIC, LC_TIME, LC_COLLATE, LC_MONETARY, LC_MESSAGES and LC_ALL. + * + * Note that the category must be specified with a numeric value, instead of the constant name. The values are: + * + * - LC_ALL 0 + * - LC_COLLATE 1 + * - LC_CTYPE 2 + * - LC_MONETARY 3 + * - LC_NUMERIC 4 + * - LC_TIME 5 + * - LC_MESSAGES 6 + * + * @param string $msg String to translate + * @param integer $category Category + * @param string $return true to return, false to echo + * @return translated string if $return is false string will be echoed + */ + function __c($msg, $category, $return = false) { + if (!$msg) { + return; + } + if (!class_exists('I18n')) { + App::import('Core', 'i18n'); + } + + if ($return === false) { + echo I18n::translate($msg, null, null, $category); + } else { + return I18n::translate($msg, null, null, $category); + } + } +/** + * Computes the difference of arrays using keys for comparison. + * + * @param array First array + * @param array Second array + * @return array Array with different keys + */ + if (!function_exists('array_diff_key')) { + function array_diff_key() { + $valuesDiff = array(); + + $argc = func_num_args(); + if ($argc < 2) { + return false; + } + + $args = func_get_args(); + foreach ($args as $param) { + if (!is_array($param)) { + return false; + } + } + + foreach ($args[0] as $valueKey => $valueData) { + for ($i = 1; $i < $argc; $i++) { + if (array_key_exists($valueKey, $args[$i])) { + continue 2; + } + } + $valuesDiff[$valueKey] = $valueData; + } + return $valuesDiff; + } + } +/** + * Computes the intersection of arrays using keys for comparison + * + * @param array First array + * @param array Second array + * @return array Array with interesected keys + */ + if (!function_exists('array_intersect_key')) { + function array_intersect_key($arr1, $arr2) { + $res = array(); + foreach ($arr1 as $key => $value) { + if (array_key_exists($key, $arr2)) { + $res[$key] = $arr1[$key]; + } + } + return $res; + } + } +/** + * Shortcut to Log::write. + * + * @param string $message Message to write to log + */ + function LogError($message) { + if (!class_exists('CakeLog')) { + App::import('Core', 'CakeLog'); + } + $bad = array("\n", "\r", "\t"); + $good = ' '; + CakeLog::write('error', str_replace($bad, $good, $message)); + } +/** + * Searches include path for files. + * + * @param string $file File to look for + * @return Full path to file if exists, otherwise false + * @link http://book.cakephp.org/view/702/fileExistsInPath + */ + function fileExistsInPath($file) { + $paths = explode(PATH_SEPARATOR, ini_get('include_path')); + foreach ($paths as $path) { + $fullPath = $path . DS . $file; + + if (file_exists($fullPath)) { + return $fullPath; + } elseif (file_exists($file)) { + return $file; + } + } + return false; + } +/** + * Convert forward slashes to underscores and removes first and last underscores in a string + * + * @param string String to convert + * @return string with underscore remove from start and end of string + * @link http://book.cakephp.org/view/697/convertSlash + */ + function convertSlash($string) { + $string = trim($string, '/'); + $string = preg_replace('/\/\//', '/', $string); + $string = str_replace('/', '_', $string); + return $string; + } +/** + * Implements http_build_query for PHP4. + * + * @param string $data Data to set in query string + * @param string $prefix If numeric indices, prepend this to index for elements in base array. + * @param string $argSep String used to separate arguments + * @param string $baseKey Base key + * @return string URL encoded query string + * @see http://php.net/http_build_query + */ + if (!function_exists('http_build_query')) { + function http_build_query($data, $prefix = null, $argSep = null, $baseKey = null) { + if (empty($argSep)) { + $argSep = ini_get('arg_separator.output'); + } + if (is_object($data)) { + $data = get_object_vars($data); + } + $out = array(); + + foreach ((array)$data as $key => $v) { + if (is_numeric($key) && !empty($prefix)) { + $key = $prefix . $key; + } + $key = urlencode($key); + + if (!empty($baseKey)) { + $key = $baseKey . '[' . $key . ']'; + } + + if (is_array($v) || is_object($v)) { + $out[] = http_build_query($v, $prefix, $argSep, $key); + } else { + $out[] = $key . '=' . urlencode($v); + } + } + return implode($argSep, $out); + } + } +/** + * Wraps ternary operations. If $condition is a non-empty value, $val1 is returned, otherwise $val2. + * Don't use for isset() conditions, or wrap your variable with @ operator: + * Example: + * + * `ife(isset($variable), @$variable, 'default');` + * + * @param mixed $condition Conditional expression + * @param mixed $val1 Value to return in case condition matches + * @param mixed $val2 Value to return if condition doesn't match + * @return mixed $val1 or $val2, depending on whether $condition evaluates to a non-empty expression. + * @link http://book.cakephp.org/view/704/ife + */ + function ife($condition, $val1 = null, $val2 = null) { + if (!empty($condition)) { + return $val1; + } + return $val2; + } +?> \ No newline at end of file diff --git a/cake/bootstrap.php b/cake/bootstrap.php new file mode 100755 index 0000000..0a28e55 --- /dev/null +++ b/cake/bootstrap.php @@ -0,0 +1,52 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Basic Cake functionality. + * + * Core functions for including other source files, loading models and so forth. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +if (!defined('PHP5')) { + define('PHP5', (PHP_VERSION >= 5)); +} +if (!defined('E_DEPRECATED')) { + define('E_DEPRECATED', 8192); +} +error_reporting(E_ALL & ~E_DEPRECATED); +/** + * Configuration, directory layout and standard libraries + */ + if (!isset($bootstrap)) { + require CORE_PATH . 'cake' . DS . 'basics.php'; + $TIME_START = getMicrotime(); + require CORE_PATH . 'cake' . DS . 'config' . DS . 'paths.php'; + require LIBS . 'object.php'; + require LIBS . 'inflector.php'; + require LIBS . 'configure.php'; + } + require LIBS . 'cache.php'; + + Configure::getInstance(); + + $url = null; + + App::import('Core', array('Dispatcher')); +?> \ No newline at end of file diff --git a/cake/config/config.php b/cake/config/config.php new file mode 100755 index 0000000..2471848 --- /dev/null +++ b/cake/config/config.php @@ -0,0 +1,26 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Core Configurations. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.app.config + * @since CakePHP(tm) v 1.1.11.4062 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +return $config['Cake.version'] = '1.2.4.8284'; +?> \ No newline at end of file diff --git a/cake/config/paths.php b/cake/config/paths.php new file mode 100755 index 0000000..6a6449f --- /dev/null +++ b/cake/config/paths.php @@ -0,0 +1,210 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Short description for file. + * + * In this file you set paths to different directories used by Cake. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.app.config + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * If the index.php file is used instead of an .htaccess file + * or if the user can not set the web root to use the public + * directory we will define ROOT there, otherwise we set it + * here. + */ + if (!defined('ROOT')) { + define('ROOT', '../'); + } + if (!defined('WEBROOT_DIR')) { + define('WEBROOT_DIR', 'webroot'); + } +/** + * Path to the cake directory. + */ + define('CAKE', CORE_PATH.'cake'.DS); +/** + * Path to the application's directory. + */ +if (!defined('APP')) { + define('APP', ROOT.DS.APP_DIR.DS); +} +/** + * Path to the application's models directory. + */ + define('MODELS', APP.'models'.DS); +/** + * Path to model behaviors directory. + */ + define('BEHAVIORS', MODELS.'behaviors'.DS); +/** + * Path to the application's controllers directory. + */ + define('CONTROLLERS', APP.'controllers'.DS); +/** + * Path to the application's components directory. + */ + define('COMPONENTS', CONTROLLERS.'components'.DS); +/** + * Path to the application's views directory. + */ + define('VIEWS', APP.'views'.DS); +/** + * Path to the application's helpers directory. + */ + define('HELPERS', VIEWS.'helpers'.DS); +/** + * Path to the application's view's layouts directory. + */ + define('LAYOUTS', VIEWS.'layouts'.DS); +/** + * Path to the application's view's elements directory. + * It's supposed to hold pieces of PHP/HTML that are used on multiple pages + * and are not linked to a particular layout (like polls, footers and so on). + */ + define('ELEMENTS', VIEWS.'elements'.DS); +/** + * Path to the configuration files directory. + */ +if (!defined('CONFIGS')) { + define('CONFIGS', APP.'config'.DS); +} +/** + * Path to the libs directory. + */ + define('INFLECTIONS', CAKE.'config'.DS.'inflections'.DS); +/** + * Path to the libs directory. + */ + define('LIBS', CAKE.'libs'.DS); +/** + * Path to the public CSS directory. + */ + define('CSS', WWW_ROOT.'css'.DS); +/** + * Path to the public JavaScript directory. + */ + define('JS', WWW_ROOT.'js'.DS); +/** + * Path to the public images directory. + */ + define('IMAGES', WWW_ROOT.'img'.DS); +/** + * Path to the console libs direcotry. + */ + define('CONSOLE_LIBS', CAKE.'console'.DS.'libs'.DS); +/** + * Path to the tests directory. + */ +if (!defined('TESTS')) { + define('TESTS', APP.'tests'.DS); +} +/** + * Path to the core tests directory. + */ +if (!defined('CAKE_TESTS')) { + define('CAKE_TESTS', CAKE.'tests'.DS); +} +/** + * Path to the test suite. + */ + define('CAKE_TESTS_LIB', CAKE_TESTS.'lib'.DS); + +/** + * Path to the controller test directory. + */ + define('CONTROLLER_TESTS', TESTS.'cases'.DS.'controllers'.DS); +/** + * Path to the components test directory. + */ + define('COMPONENT_TESTS', TESTS.'cases'.DS.'components'.DS); +/** + * Path to the helpers test directory. + */ + define('HELPER_TESTS', TESTS.'cases'.DS.'views'.DS.'helpers'.DS); +/** + * Path to the models' test directory. + */ + define('MODEL_TESTS', TESTS.'cases'.DS.'models'.DS); +/** + * Path to the lib test directory. + */ + define('LIB_TESTS', CAKE_TESTS.'cases'.DS.'lib'.DS); +/** + * Path to the temporary files directory. + */ +if (!defined('TMP')) { + define('TMP', APP.'tmp'.DS); +} +/** + * Path to the logs directory. + */ + define('LOGS', TMP.'logs'.DS); +/** + * Path to the cache files directory. It can be shared between hosts in a multi-server setup. + */ + define('CACHE', TMP.'cache'.DS); +/** + * Path to the vendors directory. + */ +if (!defined('VENDORS')) { + define('VENDORS', CAKE_CORE_INCLUDE_PATH.DS.'vendors'.DS); +} +/** + * Path to the Pear directory + * The purporse is to make it easy porting Pear libs into Cake + * without setting the include_path PHP variable. + */ + define('PEAR', VENDORS.'Pear'.DS); +/** + * Full url prefix + */ +if (!defined('FULL_BASE_URL')) { + $s = null; + if (env('HTTPS')) { + $s ='s'; + } + + $httpHost = env('HTTP_HOST'); + + if (isset($httpHost)) { + define('FULL_BASE_URL', 'http'.$s.'://'.$httpHost); + } + unset($httpHost, $s); +} +/** + * Web path to the public images directory. + */ +if (!defined('IMAGES_URL')) { + define('IMAGES_URL', 'img/'); +} +/** + * Web path to the CSS files directory. + */ +if (!defined('CSS_URL')) { + define('CSS_URL', 'css/'); +} +/** + * Web path to the js files directory. + */ +if (!defined('JS_URL')) { + define('JS_URL', 'js/'); +} +?> \ No newline at end of file diff --git a/cake/config/unicode/casefolding/0080_00ff.php b/cake/config/unicode/casefolding/0080_00ff.php new file mode 100755 index 0000000..361b9f4 --- /dev/null +++ b/cake/config/unicode/casefolding/0080_00ff.php @@ -0,0 +1,79 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Case Folding Properties. + * + * Provides case mapping of Unicode characters for code points U+0080 through U+00FF + * + * @see http://www.unicode.org/Public/UNIDATA/UCD.html + * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt + * @see http://www.unicode.org/reports/tr21/tr21-5.html + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.config.unicode.casefolding + * @since CakePHP(tm) v 1.2.0.5691 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * The upper field is the decimal value of the upper case character + * + * The lower filed is an array of the decimal values that form the lower case version of a character. + * + * The status field is: + * C: common case folding, common mappings shared by both simple and full mappings. + * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces. + * S: simple case folding, mappings to single characters where different from F. + * T: special case for uppercase I and dotted uppercase I + * - For non-Turkic languages, this mapping is normally not used. + * - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters. + * Note that the Turkic mappings do not maintain canonical equivalence without additional processing. + * See the discussions of case mapping in the Unicode Standard for more information. + */ +$config['0080_00ff'][] = array('upper' => 181, 'status' => 'C', 'lower' => array(956)); +$config['0080_00ff'][] = array('upper' => 924, 'status' => 'C', 'lower' => array(181)); +$config['0080_00ff'][] = array('upper' => 192, 'status' => 'C', 'lower' => array(224)); /* LATIN CAPITAL LETTER A WITH GRAVE */ +$config['0080_00ff'][] = array('upper' => 193, 'status' => 'C', 'lower' => array(225)); /* LATIN CAPITAL LETTER A WITH ACUTE */ +$config['0080_00ff'][] = array('upper' => 194, 'status' => 'C', 'lower' => array(226)); /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ +$config['0080_00ff'][] = array('upper' => 195, 'status' => 'C', 'lower' => array(227)); /* LATIN CAPITAL LETTER A WITH TILDE */ +$config['0080_00ff'][] = array('upper' => 196, 'status' => 'C', 'lower' => array(228)); /* LATIN CAPITAL LETTER A WITH DIAERESIS */ +$config['0080_00ff'][] = array('upper' => 197, 'status' => 'C', 'lower' => array(229)); /* LATIN CAPITAL LETTER A WITH RING ABOVE */ +$config['0080_00ff'][] = array('upper' => 198, 'status' => 'C', 'lower' => array(230)); /* LATIN CAPITAL LETTER AE */ +$config['0080_00ff'][] = array('upper' => 199, 'status' => 'C', 'lower' => array(231)); /* LATIN CAPITAL LETTER C WITH CEDILLA */ +$config['0080_00ff'][] = array('upper' => 200, 'status' => 'C', 'lower' => array(232)); /* LATIN CAPITAL LETTER E WITH GRAVE */ +$config['0080_00ff'][] = array('upper' => 201, 'status' => 'C', 'lower' => array(233)); /* LATIN CAPITAL LETTER E WITH ACUTE */ +$config['0080_00ff'][] = array('upper' => 202, 'status' => 'C', 'lower' => array(234)); /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ +$config['0080_00ff'][] = array('upper' => 203, 'status' => 'C', 'lower' => array(235)); /* LATIN CAPITAL LETTER E WITH DIAERESIS */ +$config['0080_00ff'][] = array('upper' => 204, 'status' => 'C', 'lower' => array(236)); /* LATIN CAPITAL LETTER I WITH GRAVE */ +$config['0080_00ff'][] = array('upper' => 205, 'status' => 'C', 'lower' => array(237)); /* LATIN CAPITAL LETTER I WITH ACUTE */ +$config['0080_00ff'][] = array('upper' => 206, 'status' => 'C', 'lower' => array(238)); /* LATIN CAPITAL LETTER I WITH CIRCUMFLEX */ +$config['0080_00ff'][] = array('upper' => 207, 'status' => 'C', 'lower' => array(239)); /* LATIN CAPITAL LETTER I WITH DIAERESIS */ +$config['0080_00ff'][] = array('upper' => 208, 'status' => 'C', 'lower' => array(240)); /* LATIN CAPITAL LETTER ETH */ +$config['0080_00ff'][] = array('upper' => 209, 'status' => 'C', 'lower' => array(241)); /* LATIN CAPITAL LETTER N WITH TILDE */ +$config['0080_00ff'][] = array('upper' => 210, 'status' => 'C', 'lower' => array(242)); /* LATIN CAPITAL LETTER O WITH GRAVE */ +$config['0080_00ff'][] = array('upper' => 211, 'status' => 'C', 'lower' => array(243)); /* LATIN CAPITAL LETTER O WITH ACUTE */ +$config['0080_00ff'][] = array('upper' => 212, 'status' => 'C', 'lower' => array(244)); /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ +$config['0080_00ff'][] = array('upper' => 213, 'status' => 'C', 'lower' => array(245)); /* LATIN CAPITAL LETTER O WITH TILDE */ +$config['0080_00ff'][] = array('upper' => 214, 'status' => 'C', 'lower' => array(246)); /* LATIN CAPITAL LETTER O WITH DIAERESIS */ +$config['0080_00ff'][] = array('upper' => 216, 'status' => 'C', 'lower' => array(248)); /* LATIN CAPITAL LETTER O WITH STROKE */ +$config['0080_00ff'][] = array('upper' => 217, 'status' => 'C', 'lower' => array(249)); /* LATIN CAPITAL LETTER U WITH GRAVE */ +$config['0080_00ff'][] = array('upper' => 218, 'status' => 'C', 'lower' => array(250)); /* LATIN CAPITAL LETTER U WITH ACUTE */ +$config['0080_00ff'][] = array('upper' => 219, 'status' => 'C', 'lower' => array(251)); /* LATIN CAPITAL LETTER U WITH CIRCUMFLEX */ +$config['0080_00ff'][] = array('upper' => 220, 'status' => 'C', 'lower' => array(252)); /* LATIN CAPITAL LETTER U WITH DIAERESIS */ +$config['0080_00ff'][] = array('upper' => 221, 'status' => 'C', 'lower' => array(253)); /* LATIN CAPITAL LETTER Y WITH ACUTE */ +$config['0080_00ff'][] = array('upper' => 222, 'status' => 'C', 'lower' => array(254)); /* LATIN CAPITAL LETTER THORN */ +$config['0080_00ff'][] = array('upper' => 223, 'status' => 'F', 'lower' => array(115, 115)); /* LATIN SMALL LETTER SHARP S */ +?> \ No newline at end of file diff --git a/cake/config/unicode/casefolding/0100_017f.php b/cake/config/unicode/casefolding/0100_017f.php new file mode 100755 index 0000000..4f27b2b --- /dev/null +++ b/cake/config/unicode/casefolding/0100_017f.php @@ -0,0 +1,112 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Case Folding Properties. + * + * Provides case mapping of Unicode characters for code points U+0100 through U+017F + * + * @see http://www.unicode.org/Public/UNIDATA/UCD.html + * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt + * @see http://www.unicode.org/reports/tr21/tr21-5.html + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.config.unicode.casefolding + * @since CakePHP(tm) v 1.2.0.5691 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * The upper field is the decimal value of the upper case character + * + * The lower filed is an array of the decimal values that form the lower case version of a character. + * + * The status field is: + * C: common case folding, common mappings shared by both simple and full mappings. + * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces. + * S: simple case folding, mappings to single characters where different from F. + * T: special case for uppercase I and dotted uppercase I + * - For non-Turkic languages, this mapping is normally not used. + * - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters. + * Note that the Turkic mappings do not maintain canonical equivalence without additional processing. + * See the discussions of case mapping in the Unicode Standard for more information. + */ +$config['0100_017f'][] = array('upper' => 256, 'status' => 'C', 'lower' => array(257)); /* LATIN CAPITAL LETTER A WITH MACRON */ +$config['0100_017f'][] = array('upper' => 258, 'status' => 'C', 'lower' => array(259)); /* LATIN CAPITAL LETTER A WITH BREVE */ +$config['0100_017f'][] = array('upper' => 260, 'status' => 'C', 'lower' => array(261)); /* LATIN CAPITAL LETTER A WITH OGONEK */ +$config['0100_017f'][] = array('upper' => 262, 'status' => 'C', 'lower' => array(263)); /* LATIN CAPITAL LETTER C WITH ACUTE */ +$config['0100_017f'][] = array('upper' => 264, 'status' => 'C', 'lower' => array(265)); /* LATIN CAPITAL LETTER C WITH CIRCUMFLEX */ +$config['0100_017f'][] = array('upper' => 266, 'status' => 'C', 'lower' => array(267)); /* LATIN CAPITAL LETTER C WITH DOT ABOVE */ +$config['0100_017f'][] = array('upper' => 268, 'status' => 'C', 'lower' => array(269)); /* LATIN CAPITAL LETTER C WITH CARON */ +$config['0100_017f'][] = array('upper' => 270, 'status' => 'C', 'lower' => array(271)); /* LATIN CAPITAL LETTER D WITH CARON */ +$config['0100_017f'][] = array('upper' => 272, 'status' => 'C', 'lower' => array(273)); /* LATIN CAPITAL LETTER D WITH STROKE */ +$config['0100_017f'][] = array('upper' => 274, 'status' => 'C', 'lower' => array(275)); /* LATIN CAPITAL LETTER E WITH MACRON */ +$config['0100_017f'][] = array('upper' => 276, 'status' => 'C', 'lower' => array(277)); /* LATIN CAPITAL LETTER E WITH BREVE */ +$config['0100_017f'][] = array('upper' => 278, 'status' => 'C', 'lower' => array(279)); /* LATIN CAPITAL LETTER E WITH DOT ABOVE */ +$config['0100_017f'][] = array('upper' => 280, 'status' => 'C', 'lower' => array(281)); /* LATIN CAPITAL LETTER E WITH OGONEK */ +$config['0100_017f'][] = array('upper' => 282, 'status' => 'C', 'lower' => array(283)); /* LATIN CAPITAL LETTER E WITH CARON */ +$config['0100_017f'][] = array('upper' => 284, 'status' => 'C', 'lower' => array(285)); /* LATIN CAPITAL LETTER G WITH CIRCUMFLEX */ +$config['0100_017f'][] = array('upper' => 286, 'status' => 'C', 'lower' => array(287)); /* LATIN CAPITAL LETTER G WITH BREVE */ +$config['0100_017f'][] = array('upper' => 288, 'status' => 'C', 'lower' => array(289)); /* LATIN CAPITAL LETTER G WITH DOT ABOVE */ +$config['0100_017f'][] = array('upper' => 290, 'status' => 'C', 'lower' => array(291)); /* LATIN CAPITAL LETTER G WITH CEDILLA */ +$config['0100_017f'][] = array('upper' => 292, 'status' => 'C', 'lower' => array(293)); /* LATIN CAPITAL LETTER H WITH CIRCUMFLEX */ +$config['0100_017f'][] = array('upper' => 294, 'status' => 'C', 'lower' => array(295)); /* LATIN CAPITAL LETTER H WITH STROKE */ +$config['0100_017f'][] = array('upper' => 296, 'status' => 'C', 'lower' => array(297)); /* LATIN CAPITAL LETTER I WITH TILDE */ +$config['0100_017f'][] = array('upper' => 298, 'status' => 'C', 'lower' => array(299)); /* LATIN CAPITAL LETTER I WITH MACRON */ +$config['0100_017f'][] = array('upper' => 300, 'status' => 'C', 'lower' => array(301)); /* LATIN CAPITAL LETTER I WITH BREVE */ +$config['0100_017f'][] = array('upper' => 302, 'status' => 'C', 'lower' => array(303)); /* LATIN CAPITAL LETTER I WITH OGONEK */ +$config['0100_017f'][] = array('upper' => 304, 'status' => 'F', 'lower' => array(105, 775)); /* LATIN CAPITAL LETTER I WITH DOT ABOVE */ +$config['0100_017f'][] = array('upper' => 304, 'status' => 'T', 'lower' => array(105)); /* LATIN CAPITAL LETTER I WITH DOT ABOVE */ +$config['0100_017f'][] = array('upper' => 306, 'status' => 'C', 'lower' => array(307)); /* LATIN CAPITAL LIGATURE IJ */ +$config['0100_017f'][] = array('upper' => 308, 'status' => 'C', 'lower' => array(309)); /* LATIN CAPITAL LETTER J WITH CIRCUMFLEX */ +$config['0100_017f'][] = array('upper' => 310, 'status' => 'C', 'lower' => array(311)); /* LATIN CAPITAL LETTER K WITH CEDILLA */ +$config['0100_017f'][] = array('upper' => 313, 'status' => 'C', 'lower' => array(314)); /* LATIN CAPITAL LETTER L WITH ACUTE */ +$config['0100_017f'][] = array('upper' => 315, 'status' => 'C', 'lower' => array(316)); /* LATIN CAPITAL LETTER L WITH CEDILLA */ +$config['0100_017f'][] = array('upper' => 317, 'status' => 'C', 'lower' => array(318)); /* LATIN CAPITAL LETTER L WITH CARON */ +$config['0100_017f'][] = array('upper' => 319, 'status' => 'C', 'lower' => array(320)); /* LATIN CAPITAL LETTER L WITH MIDDLE DOT */ +$config['0100_017f'][] = array('upper' => 321, 'status' => 'C', 'lower' => array(322)); /* LATIN CAPITAL LETTER L WITH STROKE */ +$config['0100_017f'][] = array('upper' => 323, 'status' => 'C', 'lower' => array(324)); /* LATIN CAPITAL LETTER N WITH ACUTE */ +$config['0100_017f'][] = array('upper' => 325, 'status' => 'C', 'lower' => array(326)); /* LATIN CAPITAL LETTER N WITH CEDILLA */ +$config['0100_017f'][] = array('upper' => 327, 'status' => 'C', 'lower' => array(328)); /* LATIN CAPITAL LETTER N WITH CARON */ +$config['0100_017f'][] = array('upper' => 329, 'status' => 'F', 'lower' => array(700, 110)); /* LATIN SMALL LETTER N PRECEDED BY APOSTROPHE */ +$config['0100_017f'][] = array('upper' => 330, 'status' => 'C', 'lower' => array(331)); /* LATIN CAPITAL LETTER ENG */ +$config['0100_017f'][] = array('upper' => 332, 'status' => 'C', 'lower' => array(333)); /* LATIN CAPITAL LETTER O WITH MACRON */ +$config['0100_017f'][] = array('upper' => 334, 'status' => 'C', 'lower' => array(335)); /* LATIN CAPITAL LETTER O WITH BREVE */ +$config['0100_017f'][] = array('upper' => 336, 'status' => 'C', 'lower' => array(337)); /* LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */ +$config['0100_017f'][] = array('upper' => 338, 'status' => 'C', 'lower' => array(339)); /* LATIN CAPITAL LIGATURE OE */ +$config['0100_017f'][] = array('upper' => 340, 'status' => 'C', 'lower' => array(341)); /* LATIN CAPITAL LETTER R WITH ACUTE */ +$config['0100_017f'][] = array('upper' => 342, 'status' => 'C', 'lower' => array(343)); /* LATIN CAPITAL LETTER R WITH CEDILLA */ +$config['0100_017f'][] = array('upper' => 344, 'status' => 'C', 'lower' => array(345)); /* LATIN CAPITAL LETTER R WITH CARON */ +$config['0100_017f'][] = array('upper' => 346, 'status' => 'C', 'lower' => array(347)); /* LATIN CAPITAL LETTER S WITH ACUTE */ +$config['0100_017f'][] = array('upper' => 348, 'status' => 'C', 'lower' => array(349)); /* LATIN CAPITAL LETTER S WITH CIRCUMFLEX */ +$config['0100_017f'][] = array('upper' => 350, 'status' => 'C', 'lower' => array(351)); /* LATIN CAPITAL LETTER S WITH CEDILLA */ +$config['0100_017f'][] = array('upper' => 352, 'status' => 'C', 'lower' => array(353)); /* LATIN CAPITAL LETTER S WITH CARON */ +$config['0100_017f'][] = array('upper' => 354, 'status' => 'C', 'lower' => array(355)); /* LATIN CAPITAL LETTER T WITH CEDILLA */ +$config['0100_017f'][] = array('upper' => 356, 'status' => 'C', 'lower' => array(357)); /* LATIN CAPITAL LETTER T WITH CARON */ +$config['0100_017f'][] = array('upper' => 358, 'status' => 'C', 'lower' => array(359)); /* LATIN CAPITAL LETTER T WITH STROKE */ +$config['0100_017f'][] = array('upper' => 360, 'status' => 'C', 'lower' => array(361)); /* LATIN CAPITAL LETTER U WITH TILDE */ +$config['0100_017f'][] = array('upper' => 362, 'status' => 'C', 'lower' => array(363)); /* LATIN CAPITAL LETTER U WITH MACRON */ +$config['0100_017f'][] = array('upper' => 364, 'status' => 'C', 'lower' => array(365)); /* LATIN CAPITAL LETTER U WITH BREVE */ +$config['0100_017f'][] = array('upper' => 366, 'status' => 'C', 'lower' => array(367)); /* LATIN CAPITAL LETTER U WITH RING ABOVE */ +$config['0100_017f'][] = array('upper' => 368, 'status' => 'C', 'lower' => array(369)); /* LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */ +$config['0100_017f'][] = array('upper' => 370, 'status' => 'C', 'lower' => array(371)); /* LATIN CAPITAL LETTER U WITH OGONEK */ +$config['0100_017f'][] = array('upper' => 372, 'status' => 'C', 'lower' => array(373)); /* LATIN CAPITAL LETTER W WITH CIRCUMFLEX */ +$config['0100_017f'][] = array('upper' => 374, 'status' => 'C', 'lower' => array(375)); /* LATIN CAPITAL LETTER Y WITH CIRCUMFLEX */ +$config['0100_017f'][] = array('upper' => 376, 'status' => 'C', 'lower' => array(255)); /* LATIN CAPITAL LETTER Y WITH DIAERESIS */ +$config['0100_017f'][] = array('upper' => 377, 'status' => 'C', 'lower' => array(378)); /* LATIN CAPITAL LETTER Z WITH ACUTE */ +$config['0100_017f'][] = array('upper' => 379, 'status' => 'C', 'lower' => array(380)); /* LATIN CAPITAL LETTER Z WITH DOT ABOVE */ +$config['0100_017f'][] = array('upper' => 381, 'status' => 'C', 'lower' => array(382)); /* LATIN CAPITAL LETTER Z WITH CARON */ +$config['0100_017f'][] = array('upper' => 383, 'status' => 'C', 'lower' => array(115)); /* LATIN SMALL LETTER LONG S */ +?> \ No newline at end of file diff --git a/cake/config/unicode/casefolding/0180_024F.php b/cake/config/unicode/casefolding/0180_024F.php new file mode 100755 index 0000000..c3d6032 --- /dev/null +++ b/cake/config/unicode/casefolding/0180_024F.php @@ -0,0 +1,154 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Case Folding Properties. + * + * Provides case mapping of Unicode characters for code points U+0180 through U+024F + * + * @see http://www.unicode.org/Public/UNIDATA/UCD.html + * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt + * @see http://www.unicode.org/reports/tr21/tr21-5.html + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.config.unicode.casefolding + * @since CakePHP(tm) v 1.2.0.5691 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * The upper field is the decimal value of the upper case character + * + * The lower filed is an array of the decimal values that form the lower case version of a character. + * + * The status field is: + * C: common case folding, common mappings shared by both simple and full mappings. + * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces. + * S: simple case folding, mappings to single characters where different from F. + * T: special case for uppercase I and dotted uppercase I + * - For non-Turkic languages, this mapping is normally not used. + * - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters. + * Note that the Turkic mappings do not maintain canonical equivalence without additional processing. + * See the discussions of case mapping in the Unicode Standard for more information. + */ +$config['0180_024F'][] = array('upper' => 385, 'status' => 'C', 'lower' => array(595)); /* LATIN CAPITAL LETTER B WITH HOOK */ +$config['0180_024F'][] = array('upper' => 386, 'status' => 'C', 'lower' => array(387)); /* LATIN CAPITAL LETTER B WITH TOPBAR */ +$config['0180_024F'][] = array('upper' => 388, 'status' => 'C', 'lower' => array(389)); /* LATIN CAPITAL LETTER TONE SIX */ +$config['0180_024F'][] = array('upper' => 390, 'status' => 'C', 'lower' => array(596)); /* LATIN CAPITAL LETTER OPEN O */ +$config['0180_024F'][] = array('upper' => 391, 'status' => 'C', 'lower' => array(392)); /* LATIN CAPITAL LETTER C WITH HOOK */ +$config['0180_024F'][] = array('upper' => 393, 'status' => 'C', 'lower' => array(598)); /* LATIN CAPITAL LETTER AFRICAN D */ +$config['0180_024F'][] = array('upper' => 394, 'status' => 'C', 'lower' => array(599)); /* LATIN CAPITAL LETTER D WITH HOOK */ +$config['0180_024F'][] = array('upper' => 395, 'status' => 'C', 'lower' => array(396)); /* LATIN CAPITAL LETTER D WITH TOPBAR */ +$config['0180_024F'][] = array('upper' => 398, 'status' => 'C', 'lower' => array(477)); /* LATIN CAPITAL LETTER REVERSED E */ +$config['0180_024F'][] = array('upper' => 399, 'status' => 'C', 'lower' => array(601)); /* LATIN CAPITAL LETTER SCHWA */ +$config['0180_024F'][] = array('upper' => 400, 'status' => 'C', 'lower' => array(603)); /* LATIN CAPITAL LETTER OPEN E */ +$config['0180_024F'][] = array('upper' => 401, 'status' => 'C', 'lower' => array(402)); /* LATIN CAPITAL LETTER F WITH HOOK */ +$config['0180_024F'][] = array('upper' => 403, 'status' => 'C', 'lower' => array(608)); /* LATIN CAPITAL LETTER G WITH HOOK */ +$config['0180_024F'][] = array('upper' => 404, 'status' => 'C', 'lower' => array(611)); /* LATIN CAPITAL LETTER GAMMA */ +$config['0180_024F'][] = array('upper' => 406, 'status' => 'C', 'lower' => array(617)); /* LATIN CAPITAL LETTER IOTA */ +$config['0180_024F'][] = array('upper' => 407, 'status' => 'C', 'lower' => array(616)); /* LATIN CAPITAL LETTER I WITH STROKE */ +$config['0180_024F'][] = array('upper' => 408, 'status' => 'C', 'lower' => array(409)); /* LATIN CAPITAL LETTER K WITH HOOK */ +$config['0180_024F'][] = array('upper' => 412, 'status' => 'C', 'lower' => array(623)); /* LATIN CAPITAL LETTER TURNED M */ +$config['0180_024F'][] = array('upper' => 413, 'status' => 'C', 'lower' => array(626)); /* LATIN CAPITAL LETTER N WITH LEFT HOOK */ +$config['0180_024F'][] = array('upper' => 415, 'status' => 'C', 'lower' => array(629)); /* LATIN CAPITAL LETTER O WITH MIDDLE TILDE */ +$config['0180_024F'][] = array('upper' => 416, 'status' => 'C', 'lower' => array(417)); /* LATIN CAPITAL LETTER O WITH HORN */ +$config['0180_024F'][] = array('upper' => 418, 'status' => 'C', 'lower' => array(419)); /* LATIN CAPITAL LETTER OI */ +$config['0180_024F'][] = array('upper' => 420, 'status' => 'C', 'lower' => array(421)); /* LATIN CAPITAL LETTER P WITH HOOK */ +$config['0180_024F'][] = array('upper' => 422, 'status' => 'C', 'lower' => array(640)); /* LATIN LETTER YR */ +$config['0180_024F'][] = array('upper' => 423, 'status' => 'C', 'lower' => array(424)); /* LATIN CAPITAL LETTER TONE TWO */ +$config['0180_024F'][] = array('upper' => 425, 'status' => 'C', 'lower' => array(643)); /* LATIN CAPITAL LETTER ESH */ +$config['0180_024F'][] = array('upper' => 428, 'status' => 'C', 'lower' => array(429)); /* LATIN CAPITAL LETTER T WITH HOOK */ +$config['0180_024F'][] = array('upper' => 430, 'status' => 'C', 'lower' => array(648)); /* LATIN CAPITAL LETTER T WITH RETROFLEX HOOK */ +$config['0180_024F'][] = array('upper' => 431, 'status' => 'C', 'lower' => array(432)); /* LATIN CAPITAL LETTER U WITH HORN */ +$config['0180_024F'][] = array('upper' => 433, 'status' => 'C', 'lower' => array(650)); /* LATIN CAPITAL LETTER UPSILON */ +$config['0180_024F'][] = array('upper' => 434, 'status' => 'C', 'lower' => array(651)); /* LATIN CAPITAL LETTER V WITH HOOK */ +$config['0180_024F'][] = array('upper' => 435, 'status' => 'C', 'lower' => array(436)); /* LATIN CAPITAL LETTER Y WITH HOOK */ +$config['0180_024F'][] = array('upper' => 437, 'status' => 'C', 'lower' => array(438)); /* LATIN CAPITAL LETTER Z WITH STROKE */ +$config['0180_024F'][] = array('upper' => 439, 'status' => 'C', 'lower' => array(658)); /* LATIN CAPITAL LETTER EZH */ +$config['0180_024F'][] = array('upper' => 440, 'status' => 'C', 'lower' => array(441)); /* LATIN CAPITAL LETTER EZH REVERSED */ +$config['0180_024F'][] = array('upper' => 444, 'status' => 'C', 'lower' => array(445)); /* LATIN CAPITAL LETTER TONE FIVE */ +$config['0180_024F'][] = array('upper' => 452, 'status' => 'C', 'lower' => array(454)); /* LATIN CAPITAL LETTER DZ WITH CARON */ +$config['0180_024F'][] = array('upper' => 453, 'status' => 'C', 'lower' => array(454)); /* LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON */ +$config['0180_024F'][] = array('upper' => 455, 'status' => 'C', 'lower' => array(457)); /* LATIN CAPITAL LETTER LJ */ +$config['0180_024F'][] = array('upper' => 456, 'status' => 'C', 'lower' => array(457)); /* LATIN CAPITAL LETTER L WITH SMALL LETTER J */ +$config['0180_024F'][] = array('upper' => 458, 'status' => 'C', 'lower' => array(460)); /* LATIN CAPITAL LETTER NJ */ +$config['0180_024F'][] = array('upper' => 459, 'status' => 'C', 'lower' => array(460)); /* LATIN CAPITAL LETTER N WITH SMALL LETTER J */ +$config['0180_024F'][] = array('upper' => 461, 'status' => 'C', 'lower' => array(462)); /* LATIN CAPITAL LETTER A WITH CARON */ +$config['0180_024F'][] = array('upper' => 463, 'status' => 'C', 'lower' => array(464)); /* LATIN CAPITAL LETTER I WITH CARON */ +$config['0180_024F'][] = array('upper' => 465, 'status' => 'C', 'lower' => array(466)); /* LATIN CAPITAL LETTER O WITH CARON */ +$config['0180_024F'][] = array('upper' => 467, 'status' => 'C', 'lower' => array(468)); /* LATIN CAPITAL LETTER U WITH CARON */ +$config['0180_024F'][] = array('upper' => 469, 'status' => 'C', 'lower' => array(470)); /* LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON */ +$config['0180_024F'][] = array('upper' => 471, 'status' => 'C', 'lower' => array(472)); /* LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE */ +$config['0180_024F'][] = array('upper' => 473, 'status' => 'C', 'lower' => array(474)); /* LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON */ +$config['0180_024F'][] = array('upper' => 475, 'status' => 'C', 'lower' => array(476)); /* LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE */ +$config['0180_024F'][] = array('upper' => 478, 'status' => 'C', 'lower' => array(479)); /* LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON */ +$config['0180_024F'][] = array('upper' => 480, 'status' => 'C', 'lower' => array(481)); /* LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON */ +$config['0180_024F'][] = array('upper' => 482, 'status' => 'C', 'lower' => array(483)); /* LATIN CAPITAL LETTER AE WITH MACRON */ +$config['0180_024F'][] = array('upper' => 484, 'status' => 'C', 'lower' => array(485)); /* LATIN CAPITAL LETTER G WITH STROKE */ +$config['0180_024F'][] = array('upper' => 486, 'status' => 'C', 'lower' => array(487)); /* LATIN CAPITAL LETTER G WITH CARON */ +$config['0180_024F'][] = array('upper' => 488, 'status' => 'C', 'lower' => array(489)); /* LATIN CAPITAL LETTER K WITH CARON */ +$config['0180_024F'][] = array('upper' => 490, 'status' => 'C', 'lower' => array(491)); /* LATIN CAPITAL LETTER O WITH OGONEK */ +$config['0180_024F'][] = array('upper' => 492, 'status' => 'C', 'lower' => array(493)); /* LATIN CAPITAL LETTER O WITH OGONEK AND MACRON */ +$config['0180_024F'][] = array('upper' => 494, 'status' => 'C', 'lower' => array(495)); /* LATIN CAPITAL LETTER EZH WITH CARON */ +$config['0180_024F'][] = array('upper' => 496, 'status' => 'F', 'lower' => array(106, 780)); /* LATIN SMALL LETTER J WITH CARON */ +$config['0180_024F'][] = array('upper' => 497, 'status' => 'C', 'lower' => array(499)); /* LATIN CAPITAL LETTER DZ */ +$config['0180_024F'][] = array('upper' => 498, 'status' => 'C', 'lower' => array(499)); /* LATIN CAPITAL LETTER D WITH SMALL LETTER Z */ +$config['0180_024F'][] = array('upper' => 500, 'status' => 'C', 'lower' => array(501)); /* LATIN CAPITAL LETTER G WITH ACUTE */ +$config['0180_024F'][] = array('upper' => 502, 'status' => 'C', 'lower' => array(405)); /* LATIN CAPITAL LETTER HWAIR */ +$config['0180_024F'][] = array('upper' => 503, 'status' => 'C', 'lower' => array(447)); /* LATIN CAPITAL LETTER WYNN */ +$config['0180_024F'][] = array('upper' => 504, 'status' => 'C', 'lower' => array(505)); /* LATIN CAPITAL LETTER N WITH GRAVE */ +$config['0180_024F'][] = array('upper' => 506, 'status' => 'C', 'lower' => array(507)); /* LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE */ +$config['0180_024F'][] = array('upper' => 508, 'status' => 'C', 'lower' => array(509)); /* LATIN CAPITAL LETTER AE WITH ACUTE */ +$config['0180_024F'][] = array('upper' => 510, 'status' => 'C', 'lower' => array(511)); /* LATIN CAPITAL LETTER O WITH STROKE AND ACUTE */ +$config['0180_024F'][] = array('upper' => 512, 'status' => 'C', 'lower' => array(513)); /* LATIN CAPITAL LETTER A WITH DOUBLE GRAVE */ +$config['0180_024F'][] = array('upper' => 514, 'status' => 'C', 'lower' => array(515)); /* LATIN CAPITAL LETTER A WITH INVERTED BREVE */ +$config['0180_024F'][] = array('upper' => 516, 'status' => 'C', 'lower' => array(517)); /* LATIN CAPITAL LETTER E WITH DOUBLE GRAVE */ +$config['0180_024F'][] = array('upper' => 518, 'status' => 'C', 'lower' => array(519)); /* LATIN CAPITAL LETTER E WITH INVERTED BREVE */ +$config['0180_024F'][] = array('upper' => 520, 'status' => 'C', 'lower' => array(521)); /* LATIN CAPITAL LETTER I WITH DOUBLE GRAVE */ +$config['0180_024F'][] = array('upper' => 522, 'status' => 'C', 'lower' => array(523)); /* LATIN CAPITAL LETTER I WITH INVERTED BREVE */ +$config['0180_024F'][] = array('upper' => 524, 'status' => 'C', 'lower' => array(525)); /* LATIN CAPITAL LETTER O WITH DOUBLE GRAVE */ +$config['0180_024F'][] = array('upper' => 526, 'status' => 'C', 'lower' => array(527)); /* LATIN CAPITAL LETTER O WITH INVERTED BREVE */ +$config['0180_024F'][] = array('upper' => 528, 'status' => 'C', 'lower' => array(529)); /* LATIN CAPITAL LETTER R WITH DOUBLE GRAVE */ +$config['0180_024F'][] = array('upper' => 530, 'status' => 'C', 'lower' => array(531)); /* LATIN CAPITAL LETTER R WITH INVERTED BREVE */ +$config['0180_024F'][] = array('upper' => 532, 'status' => 'C', 'lower' => array(533)); /* LATIN CAPITAL LETTER U WITH DOUBLE GRAVE */ +$config['0180_024F'][] = array('upper' => 534, 'status' => 'C', 'lower' => array(535)); /* LATIN CAPITAL LETTER U WITH INVERTED BREVE */ +$config['0180_024F'][] = array('upper' => 536, 'status' => 'C', 'lower' => array(537)); /* LATIN CAPITAL LETTER S WITH COMMA BELOW */ +$config['0180_024F'][] = array('upper' => 538, 'status' => 'C', 'lower' => array(539)); /* LATIN CAPITAL LETTER T WITH COMMA BELOW */ +$config['0180_024F'][] = array('upper' => 540, 'status' => 'C', 'lower' => array(541)); /* LATIN CAPITAL LETTER YOGH */ +$config['0180_024F'][] = array('upper' => 542, 'status' => 'C', 'lower' => array(543)); /* LATIN CAPITAL LETTER H WITH CARON */ +$config['0180_024F'][] = array('upper' => 544, 'status' => 'C', 'lower' => array(414)); /* LATIN CAPITAL LETTER N WITH LONG RIGHT LEG */ +$config['0180_024F'][] = array('upper' => 546, 'status' => 'C', 'lower' => array(547)); /* LATIN CAPITAL LETTER OU */ +$config['0180_024F'][] = array('upper' => 548, 'status' => 'C', 'lower' => array(549)); /* LATIN CAPITAL LETTER Z WITH HOOK */ +$config['0180_024F'][] = array('upper' => 550, 'status' => 'C', 'lower' => array(551)); /* LATIN CAPITAL LETTER A WITH DOT ABOVE */ +$config['0180_024F'][] = array('upper' => 552, 'status' => 'C', 'lower' => array(553)); /* LATIN CAPITAL LETTER E WITH CEDILLA */ +$config['0180_024F'][] = array('upper' => 554, 'status' => 'C', 'lower' => array(555)); /* LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON */ +$config['0180_024F'][] = array('upper' => 556, 'status' => 'C', 'lower' => array(557)); /* LATIN CAPITAL LETTER O WITH TILDE AND MACRON */ +$config['0180_024F'][] = array('upper' => 558, 'status' => 'C', 'lower' => array(559)); /* LATIN CAPITAL LETTER O WITH DOT ABOVE */ +$config['0180_024F'][] = array('upper' => 560, 'status' => 'C', 'lower' => array(561)); /* LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON */ +$config['0180_024F'][] = array('upper' => 562, 'status' => 'C', 'lower' => array(563)); /* LATIN CAPITAL LETTER Y WITH MACRON */ +$config['0180_024F'][] = array('upper' => 570, 'status' => 'C', 'lower' => array(11365)); /* LATIN CAPITAL LETTER A WITH STROKE */ +$config['0180_024F'][] = array('upper' => 571, 'status' => 'C', 'lower' => array(572)); /* LATIN CAPITAL LETTER C WITH STROKE */ +$config['0180_024F'][] = array('upper' => 573, 'status' => 'C', 'lower' => array(410)); /* LATIN CAPITAL LETTER L WITH BAR */ +$config['0180_024F'][] = array('upper' => 574, 'status' => 'C', 'lower' => array(11366)); /* LATIN CAPITAL LETTER T WITH DIAGONAL STROKE */ +$config['0180_024F'][] = array('upper' => 577, 'status' => 'C', 'lower' => array(578)); /* LATIN CAPITAL LETTER GLOTTAL STOP */ +$config['0180_024F'][] = array('upper' => 579, 'status' => 'C', 'lower' => array(384)); /* LATIN CAPITAL LETTER B WITH STROKE */ +$config['0180_024F'][] = array('upper' => 580, 'status' => 'C', 'lower' => array(649)); /* LATIN CAPITAL LETTER U BAR */ +$config['0180_024F'][] = array('upper' => 581, 'status' => 'C', 'lower' => array(652)); /* LATIN CAPITAL LETTER TURNED V */ +$config['0180_024F'][] = array('upper' => 582, 'status' => 'C', 'lower' => array(583)); /* LATIN CAPITAL LETTER E WITH STROKE */ +$config['0180_024F'][] = array('upper' => 584, 'status' => 'C', 'lower' => array(585)); /* LATIN CAPITAL LETTER J WITH STROKE */ +$config['0180_024F'][] = array('upper' => 586, 'status' => 'C', 'lower' => array(587)); /* LATIN CAPITAL LETTER SMALL Q WITH HOOK TAIL */ +$config['0180_024F'][] = array('upper' => 588, 'status' => 'C', 'lower' => array(589)); /* LATIN CAPITAL LETTER R WITH STROKE */ +$config['0180_024F'][] = array('upper' => 590, 'status' => 'C', 'lower' => array(591)); /* LATIN CAPITAL LETTER Y WITH STROKE */ +?> \ No newline at end of file diff --git a/cake/config/unicode/casefolding/0250_02af.php b/cake/config/unicode/casefolding/0250_02af.php new file mode 100755 index 0000000..31d9b91 --- /dev/null +++ b/cake/config/unicode/casefolding/0250_02af.php @@ -0,0 +1,47 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Case Folding Properties. + * + * Provides case mapping of Unicode characters for code points U+0080 through U+00FF + * + * @see http://www.unicode.org/Public/UNIDATA/UCD.html + * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt + * @see http://www.unicode.org/reports/tr21/tr21-5.html + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.config.unicode.casefolding + * @since CakePHP(tm) v 1.2.0.6833 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * The upper field is the decimal value of the upper case character + * + * The lower filed is an array of the decimal values that form the lower case version of a character. + * + * The status field is: + * C: common case folding, common mappings shared by both simple and full mappings. + * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces. + * S: simple case folding, mappings to single characters where different from F. + * T: special case for uppercase I and dotted uppercase I + * - For non-Turkic languages, this mapping is normally not used. + * - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters. + * Note that the Turkic mappings do not maintain canonical equivalence without additional processing. + * See the discussions of case mapping in the Unicode Standard for more information. + */ +$config['0250_02af'][] = array('upper' => 422, 'status' => 'C', 'lower' => array(640)); +?> \ No newline at end of file diff --git a/cake/config/unicode/casefolding/0370_03ff.php b/cake/config/unicode/casefolding/0370_03ff.php new file mode 100755 index 0000000..776667a --- /dev/null +++ b/cake/config/unicode/casefolding/0370_03ff.php @@ -0,0 +1,108 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Case Folding Properties. + * + * Provides case mapping of Unicode characters for code points U+0370 through U+03FF + * + * @see http://www.unicode.org/Public/UNIDATA/UCD.html + * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt + * @see http://www.unicode.org/reports/tr21/tr21-5.html + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.config.unicode.casefolding + * @since CakePHP(tm) v 1.2.0.5691 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * The upper field is the decimal value of the upper case character + * + * The lower filed is an array of the decimal values that form the lower case version of a character. + * + * The status field is: + * C: common case folding, common mappings shared by both simple and full mappings. + * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces. + * S: simple case folding, mappings to single characters where different from F. + * T: special case for uppercase I and dotted uppercase I + * - For non-Turkic languages, this mapping is normally not used. + * - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters. + * Note that the Turkic mappings do not maintain canonical equivalence without additional processing. + * See the discussions of case mapping in the Unicode Standard for more information. + */ +$config['0370_03ff'][] = array('upper' => 902, 'status' => 'C', 'lower' => array(940)); /* GREEK CAPITAL LETTER ALPHA WITH TONOS */ +$config['0370_03ff'][] = array('upper' => 904, 'status' => 'C', 'lower' => array(941)); /* GREEK CAPITAL LETTER EPSILON WITH TONOS */ +$config['0370_03ff'][] = array('upper' => 905, 'status' => 'C', 'lower' => array(942)); /* GREEK CAPITAL LETTER ETA WITH TONOS */ +$config['0370_03ff'][] = array('upper' => 906, 'status' => 'C', 'lower' => array(943)); /* GREEK CAPITAL LETTER IOTA WITH TONOS */ +$config['0370_03ff'][] = array('upper' => 908, 'status' => 'C', 'lower' => array(972)); /* GREEK CAPITAL LETTER OMICRON WITH TONOS */ +$config['0370_03ff'][] = array('upper' => 910, 'status' => 'C', 'lower' => array(973)); /* GREEK CAPITAL LETTER UPSILON WITH TONOS */ +$config['0370_03ff'][] = array('upper' => 911, 'status' => 'C', 'lower' => array(974)); /* GREEK CAPITAL LETTER OMEGA WITH TONOS */ +//$config['0370_03ff'][] = array('upper' => 912, 'status' => 'F', 'lower' => array(953, 776, 769)); /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */ +$config['0370_03ff'][] = array('upper' => 913, 'status' => 'C', 'lower' => array(945)); /* GREEK CAPITAL LETTER ALPHA */ +$config['0370_03ff'][] = array('upper' => 914, 'status' => 'C', 'lower' => array(946)); /* GREEK CAPITAL LETTER BETA */ +$config['0370_03ff'][] = array('upper' => 915, 'status' => 'C', 'lower' => array(947)); /* GREEK CAPITAL LETTER GAMMA */ +$config['0370_03ff'][] = array('upper' => 916, 'status' => 'C', 'lower' => array(948)); /* GREEK CAPITAL LETTER DELTA */ +$config['0370_03ff'][] = array('upper' => 917, 'status' => 'C', 'lower' => array(949)); /* GREEK CAPITAL LETTER EPSILON */ +$config['0370_03ff'][] = array('upper' => 918, 'status' => 'C', 'lower' => array(950)); /* GREEK CAPITAL LETTER ZETA */ +$config['0370_03ff'][] = array('upper' => 919, 'status' => 'C', 'lower' => array(951)); /* GREEK CAPITAL LETTER ETA */ +$config['0370_03ff'][] = array('upper' => 920, 'status' => 'C', 'lower' => array(952)); /* GREEK CAPITAL LETTER THETA */ +$config['0370_03ff'][] = array('upper' => 921, 'status' => 'C', 'lower' => array(953)); /* GREEK CAPITAL LETTER IOTA */ +$config['0370_03ff'][] = array('upper' => 922, 'status' => 'C', 'lower' => array(954)); /* GREEK CAPITAL LETTER KAPPA */ +$config['0370_03ff'][] = array('upper' => 923, 'status' => 'C', 'lower' => array(955)); /* GREEK CAPITAL LETTER LAMDA */ +$config['0370_03ff'][] = array('upper' => 924, 'status' => 'C', 'lower' => array(956)); /* GREEK CAPITAL LETTER MU */ +$config['0370_03ff'][] = array('upper' => 925, 'status' => 'C', 'lower' => array(957)); /* GREEK CAPITAL LETTER NU */ +$config['0370_03ff'][] = array('upper' => 926, 'status' => 'C', 'lower' => array(958)); /* GREEK CAPITAL LETTER XI */ +$config['0370_03ff'][] = array('upper' => 927, 'status' => 'C', 'lower' => array(959)); /* GREEK CAPITAL LETTER OMICRON */ +$config['0370_03ff'][] = array('upper' => 928, 'status' => 'C', 'lower' => array(960)); /* GREEK CAPITAL LETTER PI */ +$config['0370_03ff'][] = array('upper' => 929, 'status' => 'C', 'lower' => array(961)); /* GREEK CAPITAL LETTER RHO */ +$config['0370_03ff'][] = array('upper' => 931, 'status' => 'C', 'lower' => array(963)); /* GREEK CAPITAL LETTER SIGMA */ +$config['0370_03ff'][] = array('upper' => 932, 'status' => 'C', 'lower' => array(964)); /* GREEK CAPITAL LETTER TAU */ +$config['0370_03ff'][] = array('upper' => 933, 'status' => 'C', 'lower' => array(965)); /* GREEK CAPITAL LETTER UPSILON */ +$config['0370_03ff'][] = array('upper' => 934, 'status' => 'C', 'lower' => array(966)); /* GREEK CAPITAL LETTER PHI */ +$config['0370_03ff'][] = array('upper' => 935, 'status' => 'C', 'lower' => array(967)); /* GREEK CAPITAL LETTER CHI */ +$config['0370_03ff'][] = array('upper' => 936, 'status' => 'C', 'lower' => array(968)); /* GREEK CAPITAL LETTER PSI */ +$config['0370_03ff'][] = array('upper' => 937, 'status' => 'C', 'lower' => array(969)); /* GREEK CAPITAL LETTER OMEGA */ +$config['0370_03ff'][] = array('upper' => 938, 'status' => 'C', 'lower' => array(970)); /* GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */ +$config['0370_03ff'][] = array('upper' => 939, 'status' => 'C', 'lower' => array(971)); /* GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */ +$config['0370_03ff'][] = array('upper' => 944, 'status' => 'F', 'lower' => array(965, 776, 769)); /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */ +$config['0370_03ff'][] = array('upper' => 962, 'status' => 'C', 'lower' => array(963)); /* GREEK SMALL LETTER FINAL SIGMA */ +$config['0370_03ff'][] = array('upper' => 976, 'status' => 'C', 'lower' => array(946)); /* GREEK BETA SYMBOL */ +$config['0370_03ff'][] = array('upper' => 977, 'status' => 'C', 'lower' => array(952)); /* GREEK THETA SYMBOL */ +$config['0370_03ff'][] = array('upper' => 981, 'status' => 'C', 'lower' => array(966)); /* GREEK PHI SYMBOL */ +$config['0370_03ff'][] = array('upper' => 982, 'status' => 'C', 'lower' => array(960)); /* GREEK PI SYMBOL */ +$config['0370_03ff'][] = array('upper' => 984, 'status' => 'C', 'lower' => array(985)); /* GREEK LETTER ARCHAIC KOPPA */ +$config['0370_03ff'][] = array('upper' => 986, 'status' => 'C', 'lower' => array(987)); /* GREEK LETTER STIGMA */ +$config['0370_03ff'][] = array('upper' => 988, 'status' => 'C', 'lower' => array(989)); /* GREEK LETTER DIGAMMA */ +$config['0370_03ff'][] = array('upper' => 990, 'status' => 'C', 'lower' => array(991)); /* GREEK LETTER KOPPA */ +$config['0370_03ff'][] = array('upper' => 992, 'status' => 'C', 'lower' => array(993)); /* GREEK LETTER SAMPI */ +$config['0370_03ff'][] = array('upper' => 994, 'status' => 'C', 'lower' => array(995)); /* COPTIC CAPITAL LETTER SHEI */ +$config['0370_03ff'][] = array('upper' => 996, 'status' => 'C', 'lower' => array(997)); /* COPTIC CAPITAL LETTER FEI */ +$config['0370_03ff'][] = array('upper' => 998, 'status' => 'C', 'lower' => array(999)); /* COPTIC CAPITAL LETTER KHEI */ +$config['0370_03ff'][] = array('upper' => 1000, 'status' => 'C', 'lower' => array(1001)); /* COPTIC CAPITAL LETTER HORI */ +$config['0370_03ff'][] = array('upper' => 1002, 'status' => 'C', 'lower' => array(1003)); /* COPTIC CAPITAL LETTER GANGIA */ +$config['0370_03ff'][] = array('upper' => 1004, 'status' => 'C', 'lower' => array(1005)); /* COPTIC CAPITAL LETTER SHIMA */ +$config['0370_03ff'][] = array('upper' => 1006, 'status' => 'C', 'lower' => array(1007)); /* COPTIC CAPITAL LETTER DEI */ +$config['0370_03ff'][] = array('upper' => 1008, 'status' => 'C', 'lower' => array(954)); /* GREEK KAPPA SYMBOL */ +$config['0370_03ff'][] = array('upper' => 1009, 'status' => 'C', 'lower' => array(961)); /* GREEK RHO SYMBOL */ +$config['0370_03ff'][] = array('upper' => 1012, 'status' => 'C', 'lower' => array(952)); /* GREEK CAPITAL THETA SYMBOL */ +$config['0370_03ff'][] = array('upper' => 1013, 'status' => 'C', 'lower' => array(949)); /* GREEK LUNATE EPSILON SYMBOL */ +$config['0370_03ff'][] = array('upper' => 1015, 'status' => 'C', 'lower' => array(1016)); /* GREEK CAPITAL LETTER SHO */ +$config['0370_03ff'][] = array('upper' => 1017, 'status' => 'C', 'lower' => array(1010)); /* GREEK CAPITAL LUNATE SIGMA SYMBOL */ +$config['0370_03ff'][] = array('upper' => 1018, 'status' => 'C', 'lower' => array(1019)); /* GREEK CAPITAL LETTER SAN */ +$config['0370_03ff'][] = array('upper' => 1021, 'status' => 'C', 'lower' => array(891)); /* GREEK CAPITAL REVERSED LUNATE SIGMA SYMBOL */ +$config['0370_03ff'][] = array('upper' => 1022, 'status' => 'C', 'lower' => array(892)); /* GREEK CAPITAL DOTTED LUNATE SIGMA SYMBOL */ +$config['0370_03ff'][] = array('upper' => 1023, 'status' => 'C', 'lower' => array(893)); /* GREEK CAPITAL REVERSED DOTTED LUNATE SIGMA SYMBOL */ +?> \ No newline at end of file diff --git a/cake/config/unicode/casefolding/0400_04ff.php b/cake/config/unicode/casefolding/0400_04ff.php new file mode 100755 index 0000000..6e1f6e9 --- /dev/null +++ b/cake/config/unicode/casefolding/0400_04ff.php @@ -0,0 +1,170 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Case Folding Properties. + * + * Provides case mapping of Unicode characters for code points U+0400 through U+04FF + * + * @see http://www.unicode.org/Public/UNIDATA/UCD.html + * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt + * @see http://www.unicode.org/reports/tr21/tr21-5.html + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.config.unicode.casefolding + * @since CakePHP(tm) v 1.2.0.5691 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * The upper field is the decimal value of the upper case character + * + * The lower filed is an array of the decimal values that form the lower case version of a character. + * + * The status field is: + * C: common case folding, common mappings shared by both simple and full mappings. + * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces. + * S: simple case folding, mappings to single characters where different from F. + * T: special case for uppercase I and dotted uppercase I + * - For non-Turkic languages, this mapping is normally not used. + * - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters. + * Note that the Turkic mappings do not maintain canonical equivalence without additional processing. + * See the discussions of case mapping in the Unicode Standard for more information. + */ +$config['0400_04ff'][] = array('upper' => 1024, 'status' => 'C', 'lower' => array(1104)); /* CYRILLIC CAPITAL LETTER IE WITH GRAVE */ +$config['0400_04ff'][] = array('upper' => 1025, 'status' => 'C', 'lower' => array(1105)); /* CYRILLIC CAPITAL LETTER IO */ +$config['0400_04ff'][] = array('upper' => 1026, 'status' => 'C', 'lower' => array(1106)); /* CYRILLIC CAPITAL LETTER DJE */ +$config['0400_04ff'][] = array('upper' => 1027, 'status' => 'C', 'lower' => array(1107)); /* CYRILLIC CAPITAL LETTER GJE */ +$config['0400_04ff'][] = array('upper' => 1028, 'status' => 'C', 'lower' => array(1108)); /* CYRILLIC CAPITAL LETTER UKRAINIAN IE */ +$config['0400_04ff'][] = array('upper' => 1029, 'status' => 'C', 'lower' => array(1109)); /* CYRILLIC CAPITAL LETTER DZE */ +$config['0400_04ff'][] = array('upper' => 1030, 'status' => 'C', 'lower' => array(1110)); /* CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */ +$config['0400_04ff'][] = array('upper' => 1031, 'status' => 'C', 'lower' => array(1111)); /* CYRILLIC CAPITAL LETTER YI */ +$config['0400_04ff'][] = array('upper' => 1032, 'status' => 'C', 'lower' => array(1112)); /* CYRILLIC CAPITAL LETTER JE */ +$config['0400_04ff'][] = array('upper' => 1033, 'status' => 'C', 'lower' => array(1113)); /* CYRILLIC CAPITAL LETTER LJE */ +$config['0400_04ff'][] = array('upper' => 1034, 'status' => 'C', 'lower' => array(1114)); /* CYRILLIC CAPITAL LETTER NJE */ +$config['0400_04ff'][] = array('upper' => 1035, 'status' => 'C', 'lower' => array(1115)); /* CYRILLIC CAPITAL LETTER TSHE */ +$config['0400_04ff'][] = array('upper' => 1036, 'status' => 'C', 'lower' => array(1116)); /* CYRILLIC CAPITAL LETTER KJE */ +$config['0400_04ff'][] = array('upper' => 1037, 'status' => 'C', 'lower' => array(1117)); /* CYRILLIC CAPITAL LETTER I WITH GRAVE */ +$config['0400_04ff'][] = array('upper' => 1038, 'status' => 'C', 'lower' => array(1118)); /* CYRILLIC CAPITAL LETTER SHORT U */ +$config['0400_04ff'][] = array('upper' => 1039, 'status' => 'C', 'lower' => array(1119)); /* CYRILLIC CAPITAL LETTER DZHE */ +$config['0400_04ff'][] = array('upper' => 1040, 'status' => 'C', 'lower' => array(1072)); /* CYRILLIC CAPITAL LETTER A */ +$config['0400_04ff'][] = array('upper' => 1041, 'status' => 'C', 'lower' => array(1073)); /* CYRILLIC CAPITAL LETTER BE */ +$config['0400_04ff'][] = array('upper' => 1042, 'status' => 'C', 'lower' => array(1074)); /* CYRILLIC CAPITAL LETTER VE */ +$config['0400_04ff'][] = array('upper' => 1043, 'status' => 'C', 'lower' => array(1075)); /* CYRILLIC CAPITAL LETTER GHE */ +$config['0400_04ff'][] = array('upper' => 1044, 'status' => 'C', 'lower' => array(1076)); /* CYRILLIC CAPITAL LETTER DE */ +$config['0400_04ff'][] = array('upper' => 1045, 'status' => 'C', 'lower' => array(1077)); /* CYRILLIC CAPITAL LETTER IE */ +$config['0400_04ff'][] = array('upper' => 1046, 'status' => 'C', 'lower' => array(1078)); /* CYRILLIC CAPITAL LETTER ZHE */ +$config['0400_04ff'][] = array('upper' => 1047, 'status' => 'C', 'lower' => array(1079)); /* CYRILLIC CAPITAL LETTER ZE */ +$config['0400_04ff'][] = array('upper' => 1048, 'status' => 'C', 'lower' => array(1080)); /* CYRILLIC CAPITAL LETTER I */ +$config['0400_04ff'][] = array('upper' => 1049, 'status' => 'C', 'lower' => array(1081)); /* CYRILLIC CAPITAL LETTER SHORT I */ +$config['0400_04ff'][] = array('upper' => 1050, 'status' => 'C', 'lower' => array(1082)); /* CYRILLIC CAPITAL LETTER KA */ +$config['0400_04ff'][] = array('upper' => 1051, 'status' => 'C', 'lower' => array(1083)); /* CYRILLIC CAPITAL LETTER EL */ +$config['0400_04ff'][] = array('upper' => 1052, 'status' => 'C', 'lower' => array(1084)); /* CYRILLIC CAPITAL LETTER EM */ +$config['0400_04ff'][] = array('upper' => 1053, 'status' => 'C', 'lower' => array(1085)); /* CYRILLIC CAPITAL LETTER EN */ +$config['0400_04ff'][] = array('upper' => 1054, 'status' => 'C', 'lower' => array(1086)); /* CYRILLIC CAPITAL LETTER O */ +$config['0400_04ff'][] = array('upper' => 1055, 'status' => 'C', 'lower' => array(1087)); /* CYRILLIC CAPITAL LETTER PE */ +$config['0400_04ff'][] = array('upper' => 1056, 'status' => 'C', 'lower' => array(1088)); /* CYRILLIC CAPITAL LETTER ER */ +$config['0400_04ff'][] = array('upper' => 1057, 'status' => 'C', 'lower' => array(1089)); /* CYRILLIC CAPITAL LETTER ES */ +$config['0400_04ff'][] = array('upper' => 1058, 'status' => 'C', 'lower' => array(1090)); /* CYRILLIC CAPITAL LETTER TE */ +$config['0400_04ff'][] = array('upper' => 1059, 'status' => 'C', 'lower' => array(1091)); /* CYRILLIC CAPITAL LETTER U */ +$config['0400_04ff'][] = array('upper' => 1060, 'status' => 'C', 'lower' => array(1092)); /* CYRILLIC CAPITAL LETTER EF */ +$config['0400_04ff'][] = array('upper' => 1061, 'status' => 'C', 'lower' => array(1093)); /* CYRILLIC CAPITAL LETTER HA */ +$config['0400_04ff'][] = array('upper' => 1062, 'status' => 'C', 'lower' => array(1094)); /* CYRILLIC CAPITAL LETTER TSE */ +$config['0400_04ff'][] = array('upper' => 1063, 'status' => 'C', 'lower' => array(1095)); /* CYRILLIC CAPITAL LETTER CHE */ +$config['0400_04ff'][] = array('upper' => 1064, 'status' => 'C', 'lower' => array(1096)); /* CYRILLIC CAPITAL LETTER SHA */ +$config['0400_04ff'][] = array('upper' => 1065, 'status' => 'C', 'lower' => array(1097)); /* CYRILLIC CAPITAL LETTER SHCHA */ +$config['0400_04ff'][] = array('upper' => 1066, 'status' => 'C', 'lower' => array(1098)); /* CYRILLIC CAPITAL LETTER HARD SIGN */ +$config['0400_04ff'][] = array('upper' => 1067, 'status' => 'C', 'lower' => array(1099)); /* CYRILLIC CAPITAL LETTER YERU */ +$config['0400_04ff'][] = array('upper' => 1068, 'status' => 'C', 'lower' => array(1100)); /* CYRILLIC CAPITAL LETTER SOFT SIGN */ +$config['0400_04ff'][] = array('upper' => 1069, 'status' => 'C', 'lower' => array(1101)); /* CYRILLIC CAPITAL LETTER E */ +$config['0400_04ff'][] = array('upper' => 1070, 'status' => 'C', 'lower' => array(1102)); /* CYRILLIC CAPITAL LETTER YU */ +$config['0400_04ff'][] = array('upper' => 1071, 'status' => 'C', 'lower' => array(1103)); /* CYRILLIC CAPITAL LETTER YA */ +$config['0400_04ff'][] = array('upper' => 1120, 'status' => 'C', 'lower' => array(1121)); /* CYRILLIC CAPITAL LETTER OMEGA */ +$config['0400_04ff'][] = array('upper' => 1122, 'status' => 'C', 'lower' => array(1123)); /* CYRILLIC CAPITAL LETTER YAT */ +$config['0400_04ff'][] = array('upper' => 1124, 'status' => 'C', 'lower' => array(1125)); /* CYRILLIC CAPITAL LETTER IOTIFIED E */ +$config['0400_04ff'][] = array('upper' => 1126, 'status' => 'C', 'lower' => array(1127)); /* CYRILLIC CAPITAL LETTER LITTLE YUS */ +$config['0400_04ff'][] = array('upper' => 1128, 'status' => 'C', 'lower' => array(1129)); /* CYRILLIC CAPITAL LETTER IOTIFIED LITTLE YUS */ +$config['0400_04ff'][] = array('upper' => 1130, 'status' => 'C', 'lower' => array(1131)); /* CYRILLIC CAPITAL LETTER BIG YUS */ +$config['0400_04ff'][] = array('upper' => 1132, 'status' => 'C', 'lower' => array(1133)); /* CYRILLIC CAPITAL LETTER IOTIFIED BIG YUS */ +$config['0400_04ff'][] = array('upper' => 1134, 'status' => 'C', 'lower' => array(1135)); /* CYRILLIC CAPITAL LETTER KSI */ +$config['0400_04ff'][] = array('upper' => 1136, 'status' => 'C', 'lower' => array(1137)); /* CYRILLIC CAPITAL LETTER PSI */ +$config['0400_04ff'][] = array('upper' => 1138, 'status' => 'C', 'lower' => array(1139)); /* CYRILLIC CAPITAL LETTER FITA */ +$config['0400_04ff'][] = array('upper' => 1140, 'status' => 'C', 'lower' => array(1141)); /* CYRILLIC CAPITAL LETTER IZHITSA */ +$config['0400_04ff'][] = array('upper' => 1142, 'status' => 'C', 'lower' => array(1143)); /* CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT */ +$config['0400_04ff'][] = array('upper' => 1144, 'status' => 'C', 'lower' => array(1145)); /* CYRILLIC CAPITAL LETTER UK */ +$config['0400_04ff'][] = array('upper' => 1146, 'status' => 'C', 'lower' => array(1147)); /* CYRILLIC CAPITAL LETTER ROUND OMEGA */ +$config['0400_04ff'][] = array('upper' => 1148, 'status' => 'C', 'lower' => array(1149)); /* CYRILLIC CAPITAL LETTER OMEGA WITH TITLO */ +$config['0400_04ff'][] = array('upper' => 1150, 'status' => 'C', 'lower' => array(1151)); /* CYRILLIC CAPITAL LETTER OT */ +$config['0400_04ff'][] = array('upper' => 1152, 'status' => 'C', 'lower' => array(1153)); /* CYRILLIC CAPITAL LETTER KOPPA */ +$config['0400_04ff'][] = array('upper' => 1162, 'status' => 'C', 'lower' => array(1163)); /* CYRILLIC CAPITAL LETTER SHORT I WITH TAIL */ +$config['0400_04ff'][] = array('upper' => 1164, 'status' => 'C', 'lower' => array(1165)); /* CYRILLIC CAPITAL LETTER SEMISOFT SIGN */ +$config['0400_04ff'][] = array('upper' => 1166, 'status' => 'C', 'lower' => array(1167)); /* CYRILLIC CAPITAL LETTER ER WITH TICK */ +$config['0400_04ff'][] = array('upper' => 1168, 'status' => 'C', 'lower' => array(1169)); /* CYRILLIC CAPITAL LETTER GHE WITH UPTURN */ +$config['0400_04ff'][] = array('upper' => 1170, 'status' => 'C', 'lower' => array(1171)); /* CYRILLIC CAPITAL LETTER GHE WITH STROKE */ +$config['0400_04ff'][] = array('upper' => 1172, 'status' => 'C', 'lower' => array(1173)); /* CYRILLIC CAPITAL LETTER GHE WITH MIDDLE HOOK */ +$config['0400_04ff'][] = array('upper' => 1174, 'status' => 'C', 'lower' => array(1175)); /* CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER */ +$config['0400_04ff'][] = array('upper' => 1176, 'status' => 'C', 'lower' => array(1177)); /* CYRILLIC CAPITAL LETTER ZE WITH DESCENDER */ +$config['0400_04ff'][] = array('upper' => 1178, 'status' => 'C', 'lower' => array(1179)); /* CYRILLIC CAPITAL LETTER KA WITH DESCENDER */ +$config['0400_04ff'][] = array('upper' => 1180, 'status' => 'C', 'lower' => array(1181)); /* CYRILLIC CAPITAL LETTER KA WITH VERTICAL STROKE */ +$config['0400_04ff'][] = array('upper' => 1182, 'status' => 'C', 'lower' => array(1183)); /* CYRILLIC CAPITAL LETTER KA WITH STROKE */ +$config['0400_04ff'][] = array('upper' => 1184, 'status' => 'C', 'lower' => array(1185)); /* CYRILLIC CAPITAL LETTER BASHKIR KA */ +$config['0400_04ff'][] = array('upper' => 1186, 'status' => 'C', 'lower' => array(1187)); /* CYRILLIC CAPITAL LETTER EN WITH DESCENDER */ +$config['0400_04ff'][] = array('upper' => 1188, 'status' => 'C', 'lower' => array(1189)); /* CYRILLIC CAPITAL LIGATURE EN GHE */ +$config['0400_04ff'][] = array('upper' => 1190, 'status' => 'C', 'lower' => array(1191)); /* CYRILLIC CAPITAL LETTER PE WITH MIDDLE HOOK */ +$config['0400_04ff'][] = array('upper' => 1192, 'status' => 'C', 'lower' => array(1193)); /* CYRILLIC CAPITAL LETTER ABKHASIAN HA */ +$config['0400_04ff'][] = array('upper' => 1194, 'status' => 'C', 'lower' => array(1195)); /* CYRILLIC CAPITAL LETTER ES WITH DESCENDER */ +$config['0400_04ff'][] = array('upper' => 1196, 'status' => 'C', 'lower' => array(1197)); /* CYRILLIC CAPITAL LETTER TE WITH DESCENDER */ +$config['0400_04ff'][] = array('upper' => 1198, 'status' => 'C', 'lower' => array(1199)); /* CYRILLIC CAPITAL LETTER STRAIGHT U */ +$config['0400_04ff'][] = array('upper' => 1200, 'status' => 'C', 'lower' => array(1201)); /* CYRILLIC CAPITAL LETTER STRAIGHT U WITH STROKE */ +$config['0400_04ff'][] = array('upper' => 1202, 'status' => 'C', 'lower' => array(1203)); /* CYRILLIC CAPITAL LETTER HA WITH DESCENDER */ +$config['0400_04ff'][] = array('upper' => 1204, 'status' => 'C', 'lower' => array(1205)); /* CYRILLIC CAPITAL LIGATURE TE TSE */ +$config['0400_04ff'][] = array('upper' => 1206, 'status' => 'C', 'lower' => array(1207)); /* CYRILLIC CAPITAL LETTER CHE WITH DESCENDER */ +$config['0400_04ff'][] = array('upper' => 1208, 'status' => 'C', 'lower' => array(1209)); /* CYRILLIC CAPITAL LETTER CHE WITH VERTICAL STROKE */ +$config['0400_04ff'][] = array('upper' => 1210, 'status' => 'C', 'lower' => array(1211)); /* CYRILLIC CAPITAL LETTER SHHA */ +$config['0400_04ff'][] = array('upper' => 1212, 'status' => 'C', 'lower' => array(1213)); /* CYRILLIC CAPITAL LETTER ABKHASIAN CHE */ +$config['0400_04ff'][] = array('upper' => 1214, 'status' => 'C', 'lower' => array(1215)); /* CYRILLIC CAPITAL LETTER ABKHASIAN CHE WITH DESCENDER */ +$config['0400_04ff'][] = array('upper' => 1216, 'status' => 'C', 'lower' => array(1231)); /* CYRILLIC LETTER PALOCHKA */ +$config['0400_04ff'][] = array('upper' => 1217, 'status' => 'C', 'lower' => array(1218)); /* CYRILLIC CAPITAL LETTER ZHE WITH BREVE */ +$config['0400_04ff'][] = array('upper' => 1219, 'status' => 'C', 'lower' => array(1220)); /* CYRILLIC CAPITAL LETTER KA WITH HOOK */ +$config['0400_04ff'][] = array('upper' => 1221, 'status' => 'C', 'lower' => array(1222)); /* CYRILLIC CAPITAL LETTER EL WITH TAIL */ +$config['0400_04ff'][] = array('upper' => 1223, 'status' => 'C', 'lower' => array(1224)); /* CYRILLIC CAPITAL LETTER EN WITH HOOK */ +$config['0400_04ff'][] = array('upper' => 1225, 'status' => 'C', 'lower' => array(1226)); /* CYRILLIC CAPITAL LETTER EN WITH TAIL */ +$config['0400_04ff'][] = array('upper' => 1227, 'status' => 'C', 'lower' => array(1228)); /* CYRILLIC CAPITAL LETTER KHAKASSIAN CHE */ +$config['0400_04ff'][] = array('upper' => 1229, 'status' => 'C', 'lower' => array(1230)); /* CYRILLIC CAPITAL LETTER EM WITH TAIL */ +$config['0400_04ff'][] = array('upper' => 1232, 'status' => 'C', 'lower' => array(1233)); /* CYRILLIC CAPITAL LETTER A WITH BREVE */ +$config['0400_04ff'][] = array('upper' => 1234, 'status' => 'C', 'lower' => array(1235)); /* CYRILLIC CAPITAL LETTER A WITH DIAERESIS */ +$config['0400_04ff'][] = array('upper' => 1236, 'status' => 'C', 'lower' => array(1237)); /* CYRILLIC CAPITAL LIGATURE A IE */ +$config['0400_04ff'][] = array('upper' => 1238, 'status' => 'C', 'lower' => array(1239)); /* CYRILLIC CAPITAL LETTER IE WITH BREVE */ +$config['0400_04ff'][] = array('upper' => 1240, 'status' => 'C', 'lower' => array(1241)); /* CYRILLIC CAPITAL LETTER SCHWA */ +$config['0400_04ff'][] = array('upper' => 1242, 'status' => 'C', 'lower' => array(1243)); /* CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS */ +$config['0400_04ff'][] = array('upper' => 1244, 'status' => 'C', 'lower' => array(1245)); /* CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS */ +$config['0400_04ff'][] = array('upper' => 1246, 'status' => 'C', 'lower' => array(1247)); /* CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS */ +$config['0400_04ff'][] = array('upper' => 1248, 'status' => 'C', 'lower' => array(1249)); /* CYRILLIC CAPITAL LETTER ABKHASIAN DZE */ +$config['0400_04ff'][] = array('upper' => 1250, 'status' => 'C', 'lower' => array(1251)); /* CYRILLIC CAPITAL LETTER I WITH MACRON */ +$config['0400_04ff'][] = array('upper' => 1252, 'status' => 'C', 'lower' => array(1253)); /* CYRILLIC CAPITAL LETTER I WITH DIAERESIS */ +$config['0400_04ff'][] = array('upper' => 1254, 'status' => 'C', 'lower' => array(1255)); /* CYRILLIC CAPITAL LETTER O WITH DIAERESIS */ +$config['0400_04ff'][] = array('upper' => 1256, 'status' => 'C', 'lower' => array(1257)); /* CYRILLIC CAPITAL LETTER BARRED O */ +$config['0400_04ff'][] = array('upper' => 1258, 'status' => 'C', 'lower' => array(1259)); /* CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS */ +$config['0400_04ff'][] = array('upper' => 1260, 'status' => 'C', 'lower' => array(1261)); /* CYRILLIC CAPITAL LETTER E WITH DIAERESIS */ +$config['0400_04ff'][] = array('upper' => 1262, 'status' => 'C', 'lower' => array(1263)); /* CYRILLIC CAPITAL LETTER U WITH MACRON */ +$config['0400_04ff'][] = array('upper' => 1264, 'status' => 'C', 'lower' => array(1265)); /* CYRILLIC CAPITAL LETTER U WITH DIAERESIS */ +$config['0400_04ff'][] = array('upper' => 1266, 'status' => 'C', 'lower' => array(1267)); /* CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE */ +$config['0400_04ff'][] = array('upper' => 1268, 'status' => 'C', 'lower' => array(1269)); /* CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS */ +$config['0400_04ff'][] = array('upper' => 1270, 'status' => 'C', 'lower' => array(1271)); /* CYRILLIC CAPITAL LETTER GHE WITH DESCENDER */ +$config['0400_04ff'][] = array('upper' => 1272, 'status' => 'C', 'lower' => array(1273)); /* CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS */ +$config['0400_04ff'][] = array('upper' => 1274, 'status' => 'C', 'lower' => array(1275)); /* CYRILLIC CAPITAL LETTER GHE WITH STROKE AND HOOK */ +$config['0400_04ff'][] = array('upper' => 1276, 'status' => 'C', 'lower' => array(1277)); /* CYRILLIC CAPITAL LETTER HA WITH HOOK */ +$config['0400_04ff'][] = array('upper' => 1278, 'status' => 'C', 'lower' => array(1279)); /* CYRILLIC CAPITAL LETTER HA WITH STROKE */ +?> \ No newline at end of file diff --git a/cake/config/unicode/casefolding/0500_052f.php b/cake/config/unicode/casefolding/0500_052f.php new file mode 100755 index 0000000..1785359 --- /dev/null +++ b/cake/config/unicode/casefolding/0500_052f.php @@ -0,0 +1,54 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Case Folding Properties. + * + * Provides case mapping of Unicode characters for code points U+0500 through U+052F + * + * @see http://www.unicode.org/Public/UNIDATA/UCD.html + * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt + * @see http://www.unicode.org/reports/tr21/tr21-5.html + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.config.unicode.casefolding + * @since CakePHP(tm) v 1.2.0.5691 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * The upper field is the decimal value of the upper case character + * + * The lower filed is an array of the decimal values that form the lower case version of a character. + * + * The status field is: + * C: common case folding, common mappings shared by both simple and full mappings. + * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces. + * S: simple case folding, mappings to single characters where different from F. + * T: special case for uppercase I and dotted uppercase I + * - For non-Turkic languages, this mapping is normally not used. + * - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters. + * Note that the Turkic mappings do not maintain canonical equivalence without additional processing. + * See the discussions of case mapping in the Unicode Standard for more information. + */ +$config['0500_052f'][] = array('upper' => 1280, 'status' => 'C', 'lower' => array(1281)); /* CYRILLIC CAPITAL LETTER KOMI DE */ +$config['0500_052f'][] = array('upper' => 1282, 'status' => 'C', 'lower' => array(1283)); /* CYRILLIC CAPITAL LETTER KOMI DJE */ +$config['0500_052f'][] = array('upper' => 1284, 'status' => 'C', 'lower' => array(1285)); /* CYRILLIC CAPITAL LETTER KOMI ZJE */ +$config['0500_052f'][] = array('upper' => 1286, 'status' => 'C', 'lower' => array(1287)); /* CYRILLIC CAPITAL LETTER KOMI DZJE */ +$config['0500_052f'][] = array('upper' => 1288, 'status' => 'C', 'lower' => array(1289)); /* CYRILLIC CAPITAL LETTER KOMI LJE */ +$config['0500_052f'][] = array('upper' => 1290, 'status' => 'C', 'lower' => array(1291)); /* CYRILLIC CAPITAL LETTER KOMI NJE */ +$config['0500_052f'][] = array('upper' => 1292, 'status' => 'C', 'lower' => array(1293)); /* CYRILLIC CAPITAL LETTER KOMI SJE */ +$config['0500_052f'][] = array('upper' => 1294, 'status' => 'C', 'lower' => array(1295)); /* CYRILLIC CAPITAL LETTER KOMI TJE */ +?> \ No newline at end of file diff --git a/cake/config/unicode/casefolding/0530_058f.php b/cake/config/unicode/casefolding/0530_058f.php new file mode 100755 index 0000000..83000a1 --- /dev/null +++ b/cake/config/unicode/casefolding/0530_058f.php @@ -0,0 +1,84 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Case Folding Properties. + * + * Provides case mapping of Unicode characters for code points U+0530 through U+058F + * + * @see http://www.unicode.org/Public/UNIDATA/UCD.html + * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt + * @see http://www.unicode.org/reports/tr21/tr21-5.html + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.config.unicode.casefolding + * @since CakePHP(tm) v 1.2.0.5691 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * The upper field is the decimal value of the upper case character + * + * The lower filed is an array of the decimal values that form the lower case version of a character. + * + * The status field is: + * C: common case folding, common mappings shared by both simple and full mappings. + * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces. + * S: simple case folding, mappings to single characters where different from F. + * T: special case for uppercase I and dotted uppercase I + * - For non-Turkic languages, this mapping is normally not used. + * - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters. + * Note that the Turkic mappings do not maintain canonical equivalence without additional processing. + * See the discussions of case mapping in the Unicode Standard for more information. + */ +$config['0530_058f'][] = array('upper' => 1329, 'status' => 'C', 'lower' => array(1377)); /* ARMENIAN CAPITAL LETTER AYB */ +$config['0530_058f'][] = array('upper' => 1330, 'status' => 'C', 'lower' => array(1378)); /* ARMENIAN CAPITAL LETTER BEN */ +$config['0530_058f'][] = array('upper' => 1331, 'status' => 'C', 'lower' => array(1379)); /* ARMENIAN CAPITAL LETTER GIM */ +$config['0530_058f'][] = array('upper' => 1332, 'status' => 'C', 'lower' => array(1380)); /* ARMENIAN CAPITAL LETTER DA */ +$config['0530_058f'][] = array('upper' => 1333, 'status' => 'C', 'lower' => array(1381)); /* ARMENIAN CAPITAL LETTER ECH */ +$config['0530_058f'][] = array('upper' => 1334, 'status' => 'C', 'lower' => array(1382)); /* ARMENIAN CAPITAL LETTER ZA */ +$config['0530_058f'][] = array('upper' => 1335, 'status' => 'C', 'lower' => array(1383)); /* ARMENIAN CAPITAL LETTER EH */ +$config['0530_058f'][] = array('upper' => 1336, 'status' => 'C', 'lower' => array(1384)); /* ARMENIAN CAPITAL LETTER ET */ +$config['0530_058f'][] = array('upper' => 1337, 'status' => 'C', 'lower' => array(1385)); /* ARMENIAN CAPITAL LETTER TO */ +$config['0530_058f'][] = array('upper' => 1338, 'status' => 'C', 'lower' => array(1386)); /* ARMENIAN CAPITAL LETTER ZHE */ +$config['0530_058f'][] = array('upper' => 1339, 'status' => 'C', 'lower' => array(1387)); /* ARMENIAN CAPITAL LETTER INI */ +$config['0530_058f'][] = array('upper' => 1340, 'status' => 'C', 'lower' => array(1388)); /* ARMENIAN CAPITAL LETTER LIWN */ +$config['0530_058f'][] = array('upper' => 1341, 'status' => 'C', 'lower' => array(1389)); /* ARMENIAN CAPITAL LETTER XEH */ +$config['0530_058f'][] = array('upper' => 1342, 'status' => 'C', 'lower' => array(1390)); /* ARMENIAN CAPITAL LETTER CA */ +$config['0530_058f'][] = array('upper' => 1343, 'status' => 'C', 'lower' => array(1391)); /* ARMENIAN CAPITAL LETTER KEN */ +$config['0530_058f'][] = array('upper' => 1344, 'status' => 'C', 'lower' => array(1392)); /* ARMENIAN CAPITAL LETTER HO */ +$config['0530_058f'][] = array('upper' => 1345, 'status' => 'C', 'lower' => array(1393)); /* ARMENIAN CAPITAL LETTER JA */ +$config['0530_058f'][] = array('upper' => 1346, 'status' => 'C', 'lower' => array(1394)); /* ARMENIAN CAPITAL LETTER GHAD */ +$config['0530_058f'][] = array('upper' => 1347, 'status' => 'C', 'lower' => array(1395)); /* ARMENIAN CAPITAL LETTER CHEH */ +$config['0530_058f'][] = array('upper' => 1348, 'status' => 'C', 'lower' => array(1396)); /* ARMENIAN CAPITAL LETTER MEN */ +$config['0530_058f'][] = array('upper' => 1349, 'status' => 'C', 'lower' => array(1397)); /* ARMENIAN CAPITAL LETTER YI */ +$config['0530_058f'][] = array('upper' => 1350, 'status' => 'C', 'lower' => array(1398)); /* ARMENIAN CAPITAL LETTER NOW */ +$config['0530_058f'][] = array('upper' => 1351, 'status' => 'C', 'lower' => array(1399)); /* ARMENIAN CAPITAL LETTER SHA */ +$config['0530_058f'][] = array('upper' => 1352, 'status' => 'C', 'lower' => array(1400)); /* ARMENIAN CAPITAL LETTER VO */ +$config['0530_058f'][] = array('upper' => 1353, 'status' => 'C', 'lower' => array(1401)); /* ARMENIAN CAPITAL LETTER CHA */ +$config['0530_058f'][] = array('upper' => 1354, 'status' => 'C', 'lower' => array(1402)); /* ARMENIAN CAPITAL LETTER PEH */ +$config['0530_058f'][] = array('upper' => 1355, 'status' => 'C', 'lower' => array(1403)); /* ARMENIAN CAPITAL LETTER JHEH */ +$config['0530_058f'][] = array('upper' => 1356, 'status' => 'C', 'lower' => array(1404)); /* ARMENIAN CAPITAL LETTER RA */ +$config['0530_058f'][] = array('upper' => 1357, 'status' => 'C', 'lower' => array(1405)); /* ARMENIAN CAPITAL LETTER SEH */ +$config['0530_058f'][] = array('upper' => 1358, 'status' => 'C', 'lower' => array(1406)); /* ARMENIAN CAPITAL LETTER VEW */ +$config['0530_058f'][] = array('upper' => 1359, 'status' => 'C', 'lower' => array(1407)); /* ARMENIAN CAPITAL LETTER TIWN */ +$config['0530_058f'][] = array('upper' => 1360, 'status' => 'C', 'lower' => array(1408)); /* ARMENIAN CAPITAL LETTER REH */ +$config['0530_058f'][] = array('upper' => 1361, 'status' => 'C', 'lower' => array(1409)); /* ARMENIAN CAPITAL LETTER CO */ +$config['0530_058f'][] = array('upper' => 1362, 'status' => 'C', 'lower' => array(1410)); /* ARMENIAN CAPITAL LETTER YIWN */ +$config['0530_058f'][] = array('upper' => 1363, 'status' => 'C', 'lower' => array(1411)); /* ARMENIAN CAPITAL LETTER PIWR */ +$config['0530_058f'][] = array('upper' => 1364, 'status' => 'C', 'lower' => array(1412)); /* ARMENIAN CAPITAL LETTER KEH */ +$config['0530_058f'][] = array('upper' => 1365, 'status' => 'C', 'lower' => array(1413)); /* ARMENIAN CAPITAL LETTER OH */ +$config['0530_058f'][] = array('upper' => 1366, 'status' => 'C', 'lower' => array(1414)); /* ARMENIAN CAPITAL LETTER FEH */ +?> \ No newline at end of file diff --git a/cake/config/unicode/casefolding/1e00_1eff.php b/cake/config/unicode/casefolding/1e00_1eff.php new file mode 100755 index 0000000..b8ad76f --- /dev/null +++ b/cake/config/unicode/casefolding/1e00_1eff.php @@ -0,0 +1,174 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Case Folding Properties. + * + * Provides case mapping of Unicode characters for code points U+1E00 through U+1EFF + * + * @see http://www.unicode.org/Public/UNIDATA/UCD.html + * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt + * @see http://www.unicode.org/reports/tr21/tr21-5.html + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.config.unicode.casefolding + * @since CakePHP(tm) v 1.2.0.5691 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * The upper field is the decimal value of the upper case character + * + * The lower filed is an array of the decimal values that form the lower case version of a character. + * + * The status field is: + * C: common case folding, common mappings shared by both simple and full mappings. + * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces. + * S: simple case folding, mappings to single characters where different from F. + * T: special case for uppercase I and dotted uppercase I + * - For non-Turkic languages, this mapping is normally not used. + * - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters. + * Note that the Turkic mappings do not maintain canonical equivalence without additional processing. + * See the discussions of case mapping in the Unicode Standard for more information. + */ +$config['1e00_1eff'][] = array('upper' => 7680, 'status' => 'C', 'lower' => array(7681)); /* LATIN CAPITAL LETTER A WITH RING BELOW */ +$config['1e00_1eff'][] = array('upper' => 7682, 'status' => 'C', 'lower' => array(7683)); /* LATIN CAPITAL LETTER B WITH DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7684, 'status' => 'C', 'lower' => array(7685)); /* LATIN CAPITAL LETTER B WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7686, 'status' => 'C', 'lower' => array(7687)); /* LATIN CAPITAL LETTER B WITH LINE BELOW */ +$config['1e00_1eff'][] = array('upper' => 7688, 'status' => 'C', 'lower' => array(7689)); /* LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7690, 'status' => 'C', 'lower' => array(7691)); /* LATIN CAPITAL LETTER D WITH DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7692, 'status' => 'C', 'lower' => array(7693)); /* LATIN CAPITAL LETTER D WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7694, 'status' => 'C', 'lower' => array(7695)); /* LATIN CAPITAL LETTER D WITH LINE BELOW */ +$config['1e00_1eff'][] = array('upper' => 7696, 'status' => 'C', 'lower' => array(7697)); /* LATIN CAPITAL LETTER D WITH CEDILLA */ +$config['1e00_1eff'][] = array('upper' => 7698, 'status' => 'C', 'lower' => array(7699)); /* LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW */ +$config['1e00_1eff'][] = array('upper' => 7700, 'status' => 'C', 'lower' => array(7701)); /* LATIN CAPITAL LETTER E WITH MACRON AND GRAVE */ +$config['1e00_1eff'][] = array('upper' => 7702, 'status' => 'C', 'lower' => array(7703)); /* LATIN CAPITAL LETTER E WITH MACRON AND ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7704, 'status' => 'C', 'lower' => array(7705)); /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW */ +$config['1e00_1eff'][] = array('upper' => 7706, 'status' => 'C', 'lower' => array(7707)); /* LATIN CAPITAL LETTER E WITH TILDE BELOW */ +$config['1e00_1eff'][] = array('upper' => 7708, 'status' => 'C', 'lower' => array(7709)); /* LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE */ +$config['1e00_1eff'][] = array('upper' => 7710, 'status' => 'C', 'lower' => array(7711)); /* LATIN CAPITAL LETTER F WITH DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7712, 'status' => 'C', 'lower' => array(7713)); /* LATIN CAPITAL LETTER G WITH MACRON */ +$config['1e00_1eff'][] = array('upper' => 7714, 'status' => 'C', 'lower' => array(7715)); /* LATIN CAPITAL LETTER H WITH DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7716, 'status' => 'C', 'lower' => array(7717)); /* LATIN CAPITAL LETTER H WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7718, 'status' => 'C', 'lower' => array(7719)); /* LATIN CAPITAL LETTER H WITH DIAERESIS */ +$config['1e00_1eff'][] = array('upper' => 7720, 'status' => 'C', 'lower' => array(7721)); /* LATIN CAPITAL LETTER H WITH CEDILLA */ +$config['1e00_1eff'][] = array('upper' => 7722, 'status' => 'C', 'lower' => array(7723)); /* LATIN CAPITAL LETTER H WITH BREVE BELOW */ +$config['1e00_1eff'][] = array('upper' => 7724, 'status' => 'C', 'lower' => array(7725)); /* LATIN CAPITAL LETTER I WITH TILDE BELOW */ +$config['1e00_1eff'][] = array('upper' => 7726, 'status' => 'C', 'lower' => array(7727)); /* LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7728, 'status' => 'C', 'lower' => array(7729)); /* LATIN CAPITAL LETTER K WITH ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7730, 'status' => 'C', 'lower' => array(7731)); /* LATIN CAPITAL LETTER K WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7732, 'status' => 'C', 'lower' => array(7733)); /* LATIN CAPITAL LETTER K WITH LINE BELOW */ +$config['1e00_1eff'][] = array('upper' => 7734, 'status' => 'C', 'lower' => array(7735)); /* LATIN CAPITAL LETTER L WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7736, 'status' => 'C', 'lower' => array(7737)); /* LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON */ +$config['1e00_1eff'][] = array('upper' => 7738, 'status' => 'C', 'lower' => array(7739)); /* LATIN CAPITAL LETTER L WITH LINE BELOW */ +$config['1e00_1eff'][] = array('upper' => 7740, 'status' => 'C', 'lower' => array(7741)); /* LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW */ +$config['1e00_1eff'][] = array('upper' => 7742, 'status' => 'C', 'lower' => array(7743)); /* LATIN CAPITAL LETTER M WITH ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7744, 'status' => 'C', 'lower' => array(7745)); /* LATIN CAPITAL LETTER M WITH DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7746, 'status' => 'C', 'lower' => array(7747)); /* LATIN CAPITAL LETTER M WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7748, 'status' => 'C', 'lower' => array(7749)); /* LATIN CAPITAL LETTER N WITH DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7750, 'status' => 'C', 'lower' => array(7751)); /* LATIN CAPITAL LETTER N WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7752, 'status' => 'C', 'lower' => array(7753)); /* LATIN CAPITAL LETTER N WITH LINE BELOW */ +$config['1e00_1eff'][] = array('upper' => 7754, 'status' => 'C', 'lower' => array(7755)); /* LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW */ +$config['1e00_1eff'][] = array('upper' => 7756, 'status' => 'C', 'lower' => array(7757)); /* LATIN CAPITAL LETTER O WITH TILDE AND ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7758, 'status' => 'C', 'lower' => array(7759)); /* LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS */ +$config['1e00_1eff'][] = array('upper' => 7760, 'status' => 'C', 'lower' => array(7761)); /* LATIN CAPITAL LETTER O WITH MACRON AND GRAVE */ +$config['1e00_1eff'][] = array('upper' => 7762, 'status' => 'C', 'lower' => array(7763)); /* LATIN CAPITAL LETTER O WITH MACRON AND ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7764, 'status' => 'C', 'lower' => array(7765)); /* LATIN CAPITAL LETTER P WITH ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7766, 'status' => 'C', 'lower' => array(7767)); /* LATIN CAPITAL LETTER P WITH DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7768, 'status' => 'C', 'lower' => array(7769)); /* LATIN CAPITAL LETTER R WITH DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7770, 'status' => 'C', 'lower' => array(7771)); /* LATIN CAPITAL LETTER R WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7772, 'status' => 'C', 'lower' => array(7773)); /* LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON */ +$config['1e00_1eff'][] = array('upper' => 7774, 'status' => 'C', 'lower' => array(7775)); /* LATIN CAPITAL LETTER R WITH LINE BELOW */ +$config['1e00_1eff'][] = array('upper' => 7776, 'status' => 'C', 'lower' => array(7777)); /* LATIN CAPITAL LETTER S WITH DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7778, 'status' => 'C', 'lower' => array(7779)); /* LATIN CAPITAL LETTER S WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7780, 'status' => 'C', 'lower' => array(7781)); /* LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7782, 'status' => 'C', 'lower' => array(7783)); /* LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7784, 'status' => 'C', 'lower' => array(7785)); /* LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7786, 'status' => 'C', 'lower' => array(7787)); /* LATIN CAPITAL LETTER T WITH DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7788, 'status' => 'C', 'lower' => array(7789)); /* LATIN CAPITAL LETTER T WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7790, 'status' => 'C', 'lower' => array(7791)); /* LATIN CAPITAL LETTER T WITH LINE BELOW */ +$config['1e00_1eff'][] = array('upper' => 7792, 'status' => 'C', 'lower' => array(7793)); /* LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW */ +$config['1e00_1eff'][] = array('upper' => 7794, 'status' => 'C', 'lower' => array(7795)); /* LATIN CAPITAL LETTER U WITH DIAERESIS BELOW */ +$config['1e00_1eff'][] = array('upper' => 7796, 'status' => 'C', 'lower' => array(7797)); /* LATIN CAPITAL LETTER U WITH TILDE BELOW */ +$config['1e00_1eff'][] = array('upper' => 7798, 'status' => 'C', 'lower' => array(7799)); /* LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW */ +$config['1e00_1eff'][] = array('upper' => 7800, 'status' => 'C', 'lower' => array(7801)); /* LATIN CAPITAL LETTER U WITH TILDE AND ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7802, 'status' => 'C', 'lower' => array(7803)); /* LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS */ +$config['1e00_1eff'][] = array('upper' => 7804, 'status' => 'C', 'lower' => array(7805)); /* LATIN CAPITAL LETTER V WITH TILDE */ +$config['1e00_1eff'][] = array('upper' => 7806, 'status' => 'C', 'lower' => array(7807)); /* LATIN CAPITAL LETTER V WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7808, 'status' => 'C', 'lower' => array(7809)); /* LATIN CAPITAL LETTER W WITH GRAVE */ +$config['1e00_1eff'][] = array('upper' => 7810, 'status' => 'C', 'lower' => array(7811)); /* LATIN CAPITAL LETTER W WITH ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7812, 'status' => 'C', 'lower' => array(7813)); /* LATIN CAPITAL LETTER W WITH DIAERESIS */ +$config['1e00_1eff'][] = array('upper' => 7814, 'status' => 'C', 'lower' => array(7815)); /* LATIN CAPITAL LETTER W WITH DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7816, 'status' => 'C', 'lower' => array(7817)); /* LATIN CAPITAL LETTER W WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7818, 'status' => 'C', 'lower' => array(7819)); /* LATIN CAPITAL LETTER X WITH DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7820, 'status' => 'C', 'lower' => array(7821)); /* LATIN CAPITAL LETTER X WITH DIAERESIS */ +$config['1e00_1eff'][] = array('upper' => 7822, 'status' => 'C', 'lower' => array(7823)); /* LATIN CAPITAL LETTER Y WITH DOT ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7824, 'status' => 'C', 'lower' => array(7825)); /* LATIN CAPITAL LETTER Z WITH CIRCUMFLEX */ +$config['1e00_1eff'][] = array('upper' => 7826, 'status' => 'C', 'lower' => array(7827)); /* LATIN CAPITAL LETTER Z WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7828, 'status' => 'C', 'lower' => array(7829)); /* LATIN CAPITAL LETTER Z WITH LINE BELOW */ + +//$config['1e00_1eff'][] = array('upper' => 7830, 'status' => 'F', 'lower' => array(104, 817)); /* LATIN SMALL LETTER H WITH LINE BELOW */ +//$config['1e00_1eff'][] = array('upper' => 7831, 'status' => 'F', 'lower' => array(116, 776)); /* LATIN SMALL LETTER T WITH DIAERESIS */ +//$config['1e00_1eff'][] = array('upper' => 7832, 'status' => 'F', 'lower' => array(119, 778)); /* LATIN SMALL LETTER W WITH RING ABOVE */ +//$config['1e00_1eff'][] = array('upper' => 7833, 'status' => 'F', 'lower' => array(121, 778)); /* LATIN SMALL LETTER Y WITH RING ABOVE */ +//$config['1e00_1eff'][] = array('upper' => 7834, 'status' => 'F', 'lower' => array(97, 702)); /* LATIN SMALL LETTER A WITH RIGHT HALF RING */ +//$config['1e00_1eff'][] = array('upper' => 7835, 'status' => 'C', 'lower' => array(7777)); /* LATIN SMALL LETTER LONG S WITH DOT ABOVE */ + +$config['1e00_1eff'][] = array('upper' => 7840, 'status' => 'C', 'lower' => array(7841)); /* LATIN CAPITAL LETTER A WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7842, 'status' => 'C', 'lower' => array(7843)); /* LATIN CAPITAL LETTER A WITH HOOK ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7844, 'status' => 'C', 'lower' => array(7845)); /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7846, 'status' => 'C', 'lower' => array(7847)); /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE */ +$config['1e00_1eff'][] = array('upper' => 7848, 'status' => 'C', 'lower' => array(7849)); /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7850, 'status' => 'C', 'lower' => array(7851)); /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE */ +$config['1e00_1eff'][] = array('upper' => 7852, 'status' => 'C', 'lower' => array(7853)); /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7854, 'status' => 'C', 'lower' => array(7855)); /* LATIN CAPITAL LETTER A WITH BREVE AND ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7856, 'status' => 'C', 'lower' => array(7857)); /* LATIN CAPITAL LETTER A WITH BREVE AND GRAVE */ +$config['1e00_1eff'][] = array('upper' => 7858, 'status' => 'C', 'lower' => array(7859)); /* LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7860, 'status' => 'C', 'lower' => array(7861)); /* LATIN CAPITAL LETTER A WITH BREVE AND TILDE */ +$config['1e00_1eff'][] = array('upper' => 7862, 'status' => 'C', 'lower' => array(7863)); /* LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7864, 'status' => 'C', 'lower' => array(7865)); /* LATIN CAPITAL LETTER E WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7866, 'status' => 'C', 'lower' => array(7867)); /* LATIN CAPITAL LETTER E WITH HOOK ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7868, 'status' => 'C', 'lower' => array(7869)); /* LATIN CAPITAL LETTER E WITH TILDE */ +$config['1e00_1eff'][] = array('upper' => 7870, 'status' => 'C', 'lower' => array(7871)); /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7872, 'status' => 'C', 'lower' => array(7873)); /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE */ +$config['1e00_1eff'][] = array('upper' => 7874, 'status' => 'C', 'lower' => array(7875)); /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7876, 'status' => 'C', 'lower' => array(7877)); /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE */ +$config['1e00_1eff'][] = array('upper' => 7878, 'status' => 'C', 'lower' => array(7879)); /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7880, 'status' => 'C', 'lower' => array(7881)); /* LATIN CAPITAL LETTER I WITH HOOK ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7882, 'status' => 'C', 'lower' => array(7883)); /* LATIN CAPITAL LETTER I WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7884, 'status' => 'C', 'lower' => array(7885)); /* LATIN CAPITAL LETTER O WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7886, 'status' => 'C', 'lower' => array(7887)); /* LATIN CAPITAL LETTER O WITH HOOK ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7888, 'status' => 'C', 'lower' => array(7889)); /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7890, 'status' => 'C', 'lower' => array(7891)); /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE */ +$config['1e00_1eff'][] = array('upper' => 7892, 'status' => 'C', 'lower' => array(7893)); /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7894, 'status' => 'C', 'lower' => array(7895)); /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE */ +$config['1e00_1eff'][] = array('upper' => 7896, 'status' => 'C', 'lower' => array(7897)); /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7898, 'status' => 'C', 'lower' => array(7899)); /* LATIN CAPITAL LETTER O WITH HORN AND ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7900, 'status' => 'C', 'lower' => array(7901)); /* LATIN CAPITAL LETTER O WITH HORN AND GRAVE */ +$config['1e00_1eff'][] = array('upper' => 7902, 'status' => 'C', 'lower' => array(7903)); /* LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7904, 'status' => 'C', 'lower' => array(7905)); /* LATIN CAPITAL LETTER O WITH HORN AND TILDE */ +$config['1e00_1eff'][] = array('upper' => 7906, 'status' => 'C', 'lower' => array(7907)); /* LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7908, 'status' => 'C', 'lower' => array(7909)); /* LATIN CAPITAL LETTER U WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7910, 'status' => 'C', 'lower' => array(7911)); /* LATIN CAPITAL LETTER U WITH HOOK ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7912, 'status' => 'C', 'lower' => array(7913)); /* LATIN CAPITAL LETTER U WITH HORN AND ACUTE */ +$config['1e00_1eff'][] = array('upper' => 7914, 'status' => 'C', 'lower' => array(7915)); /* LATIN CAPITAL LETTER U WITH HORN AND GRAVE */ +$config['1e00_1eff'][] = array('upper' => 7916, 'status' => 'C', 'lower' => array(7917)); /* LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7918, 'status' => 'C', 'lower' => array(7919)); /* LATIN CAPITAL LETTER U WITH HORN AND TILDE */ +$config['1e00_1eff'][] = array('upper' => 7920, 'status' => 'C', 'lower' => array(7921)); /* LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7922, 'status' => 'C', 'lower' => array(7923)); /* LATIN CAPITAL LETTER Y WITH GRAVE */ +$config['1e00_1eff'][] = array('upper' => 7924, 'status' => 'C', 'lower' => array(7925)); /* LATIN CAPITAL LETTER Y WITH DOT BELOW */ +$config['1e00_1eff'][] = array('upper' => 7926, 'status' => 'C', 'lower' => array(7927)); /* LATIN CAPITAL LETTER Y WITH HOOK ABOVE */ +$config['1e00_1eff'][] = array('upper' => 7928, 'status' => 'C', 'lower' => array(7929)); /* LATIN CAPITAL LETTER Y WITH TILDE */ +?> \ No newline at end of file diff --git a/cake/config/unicode/casefolding/1f00_1fff.php b/cake/config/unicode/casefolding/1f00_1fff.php new file mode 100755 index 0000000..2e75892 --- /dev/null +++ b/cake/config/unicode/casefolding/1f00_1fff.php @@ -0,0 +1,222 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Case Folding Properties. + * + * Provides case mapping of Unicode characters for code points U+1F00 through U+1FFF + * + * @see http://www.unicode.org/Public/UNIDATA/UCD.html + * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt + * @see http://www.unicode.org/reports/tr21/tr21-5.html + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.config.unicode.casefolding + * @since CakePHP(tm) v 1.2.0.5691 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * The upper field is the decimal value of the upper case character + * + * The lower filed is an array of the decimal values that form the lower case version of a character. + * + * The status field is: + * C: common case folding, common mappings shared by both simple and full mappings. + * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces. + * S: simple case folding, mappings to single characters where different from F. + * T: special case for uppercase I and dotted uppercase I + * - For non-Turkic languages, this mapping is normally not used. + * - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters. + * Note that the Turkic mappings do not maintain canonical equivalence without additional processing. + * See the discussions of case mapping in the Unicode Standard for more information. + */ +$config['1f00_1fff'][] = array('upper' => 7944, 'status' => 'C', 'lower' => array(7936, 953)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI */ +$config['1f00_1fff'][] = array('upper' => 7945, 'status' => 'C', 'lower' => array(7937)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA */ +$config['1f00_1fff'][] = array('upper' => 7946, 'status' => 'C', 'lower' => array(7938)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 7947, 'status' => 'C', 'lower' => array(7939)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 7948, 'status' => 'C', 'lower' => array(7940)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 7949, 'status' => 'C', 'lower' => array(7941)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 7950, 'status' => 'C', 'lower' => array(7942)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 7951, 'status' => 'C', 'lower' => array(7943)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 7960, 'status' => 'C', 'lower' => array(7952)); /* GREEK CAPITAL LETTER EPSILON WITH PSILI */ +$config['1f00_1fff'][] = array('upper' => 7961, 'status' => 'C', 'lower' => array(7953)); /* GREEK CAPITAL LETTER EPSILON WITH DASIA */ +$config['1f00_1fff'][] = array('upper' => 7962, 'status' => 'C', 'lower' => array(7954)); /* GREEK CAPITAL LETTER EPSILON WITH PSILI AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 7963, 'status' => 'C', 'lower' => array(7955)); /* GREEK CAPITAL LETTER EPSILON WITH DASIA AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 7964, 'status' => 'C', 'lower' => array(7956)); /* GREEK CAPITAL LETTER EPSILON WITH PSILI AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 7965, 'status' => 'C', 'lower' => array(7957)); /* GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 7976, 'status' => 'C', 'lower' => array(7968)); /* GREEK CAPITAL LETTER ETA WITH PSILI */ +$config['1f00_1fff'][] = array('upper' => 7977, 'status' => 'C', 'lower' => array(7969)); /* GREEK CAPITAL LETTER ETA WITH DASIA */ +$config['1f00_1fff'][] = array('upper' => 7978, 'status' => 'C', 'lower' => array(7970)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 7979, 'status' => 'C', 'lower' => array(7971)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 7980, 'status' => 'C', 'lower' => array(7972)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 7981, 'status' => 'C', 'lower' => array(7973)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 7982, 'status' => 'C', 'lower' => array(7974)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 7983, 'status' => 'C', 'lower' => array(7975)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 7992, 'status' => 'C', 'lower' => array(7984)); /* GREEK CAPITAL LETTER IOTA WITH PSILI */ +$config['1f00_1fff'][] = array('upper' => 7993, 'status' => 'C', 'lower' => array(7985)); /* GREEK CAPITAL LETTER IOTA WITH DASIA */ +$config['1f00_1fff'][] = array('upper' => 7994, 'status' => 'C', 'lower' => array(7986)); /* GREEK CAPITAL LETTER IOTA WITH PSILI AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 7995, 'status' => 'C', 'lower' => array(7987)); /* GREEK CAPITAL LETTER IOTA WITH DASIA AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 7996, 'status' => 'C', 'lower' => array(7988)); /* GREEK CAPITAL LETTER IOTA WITH PSILI AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 7997, 'status' => 'C', 'lower' => array(7989)); /* GREEK CAPITAL LETTER IOTA WITH DASIA AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 7998, 'status' => 'C', 'lower' => array(7990)); /* GREEK CAPITAL LETTER IOTA WITH PSILI AND PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 7999, 'status' => 'C', 'lower' => array(7991)); /* GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 8008, 'status' => 'C', 'lower' => array(8000)); /* GREEK CAPITAL LETTER OMICRON WITH PSILI */ +$config['1f00_1fff'][] = array('upper' => 8009, 'status' => 'C', 'lower' => array(8001)); /* GREEK CAPITAL LETTER OMICRON WITH DASIA */ +$config['1f00_1fff'][] = array('upper' => 8010, 'status' => 'C', 'lower' => array(8002)); /* GREEK CAPITAL LETTER OMICRON WITH PSILI AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 8011, 'status' => 'C', 'lower' => array(8003)); /* GREEK CAPITAL LETTER OMICRON WITH DASIA AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 8012, 'status' => 'C', 'lower' => array(8004)); /* GREEK CAPITAL LETTER OMICRON WITH PSILI AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 8013, 'status' => 'C', 'lower' => array(8005)); /* GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 8016, 'status' => 'F', 'lower' => array(965, 787)); /* GREEK SMALL LETTER UPSILON WITH PSILI */ +$config['1f00_1fff'][] = array('upper' => 8018, 'status' => 'F', 'lower' => array(965, 787, 768)); /* GREEK SMALL LETTER UPSILON WITH PSILI AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 8020, 'status' => 'F', 'lower' => array(965, 787, 769)); /* GREEK SMALL LETTER UPSILON WITH PSILI AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 8022, 'status' => 'F', 'lower' => array(965, 787, 834)); /* GREEK SMALL LETTER UPSILON WITH PSILI AND PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 8025, 'status' => 'C', 'lower' => array(8017)); /* GREEK CAPITAL LETTER UPSILON WITH DASIA */ +$config['1f00_1fff'][] = array('upper' => 8027, 'status' => 'C', 'lower' => array(8019)); /* GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 8029, 'status' => 'C', 'lower' => array(8021)); /* GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 8031, 'status' => 'C', 'lower' => array(8023)); /* GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 8040, 'status' => 'C', 'lower' => array(8032)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI */ +$config['1f00_1fff'][] = array('upper' => 8041, 'status' => 'C', 'lower' => array(8033)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA */ +$config['1f00_1fff'][] = array('upper' => 8042, 'status' => 'C', 'lower' => array(8034)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 8043, 'status' => 'C', 'lower' => array(8035)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 8044, 'status' => 'C', 'lower' => array(8036)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 8045, 'status' => 'C', 'lower' => array(8037)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 8046, 'status' => 'C', 'lower' => array(8038)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 8047, 'status' => 'C', 'lower' => array(8039)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 8064, 'status' => 'F', 'lower' => array(7936, 953)); /* GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8065, 'status' => 'F', 'lower' => array(7937, 953)); /* GREEK SMALL LETTER ALPHA WITH DASIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8066, 'status' => 'F', 'lower' => array(7938, 953)); /* GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8067, 'status' => 'F', 'lower' => array(7939, 953)); /* GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8068, 'status' => 'F', 'lower' => array(7940, 953)); /* GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8069, 'status' => 'F', 'lower' => array(7941, 953)); /* GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8070, 'status' => 'F', 'lower' => array(7942, 953)); /* GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8071, 'status' => 'F', 'lower' => array(7943, 953)); /* GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8072, 'status' => 'F', 'lower' => array(7936, 953)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8072, 'status' => 'S', 'lower' => array(8064)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8073, 'status' => 'F', 'lower' => array(7937, 953)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8073, 'status' => 'S', 'lower' => array(8065)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8074, 'status' => 'F', 'lower' => array(7938, 953)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8074, 'status' => 'S', 'lower' => array(8066)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8075, 'status' => 'F', 'lower' => array(7939, 953)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8075, 'status' => 'S', 'lower' => array(8067)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8076, 'status' => 'F', 'lower' => array(7940, 953)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8076, 'status' => 'S', 'lower' => array(8068)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8077, 'status' => 'F', 'lower' => array(7941, 953)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8077, 'status' => 'S', 'lower' => array(8069)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8078, 'status' => 'F', 'lower' => array(7942, 953)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8078, 'status' => 'S', 'lower' => array(8070)); /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8079, 'status' => 'F', 'lower' => array(7943, 953)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8079, 'status' => 'S', 'lower' => array(8071)); /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8080, 'status' => 'F', 'lower' => array(7968, 953)); /* GREEK SMALL LETTER ETA WITH PSILI AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8081, 'status' => 'F', 'lower' => array(7969, 953)); /* GREEK SMALL LETTER ETA WITH DASIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8082, 'status' => 'F', 'lower' => array(7970, 953)); /* GREEK SMALL LETTER ETA WITH PSILI AND VARIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8083, 'status' => 'F', 'lower' => array(7971, 953)); /* GREEK SMALL LETTER ETA WITH DASIA AND VARIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8084, 'status' => 'F', 'lower' => array(7972, 953)); /* GREEK SMALL LETTER ETA WITH PSILI AND OXIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8085, 'status' => 'F', 'lower' => array(7973, 953)); /* GREEK SMALL LETTER ETA WITH DASIA AND OXIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8086, 'status' => 'F', 'lower' => array(7974, 953)); /* GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8087, 'status' => 'F', 'lower' => array(7975, 953)); /* GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8088, 'status' => 'F', 'lower' => array(7968, 953)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8088, 'status' => 'S', 'lower' => array(8080)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8089, 'status' => 'F', 'lower' => array(7969, 953)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8089, 'status' => 'S', 'lower' => array(8081)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8090, 'status' => 'F', 'lower' => array(7970, 953)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8090, 'status' => 'S', 'lower' => array(8082)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8091, 'status' => 'F', 'lower' => array(7971, 953)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8091, 'status' => 'S', 'lower' => array(8083)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8092, 'status' => 'F', 'lower' => array(7972, 953)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8092, 'status' => 'S', 'lower' => array(8084)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8093, 'status' => 'F', 'lower' => array(7973, 953)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8093, 'status' => 'S', 'lower' => array(8085)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8094, 'status' => 'F', 'lower' => array(7974, 953)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8094, 'status' => 'S', 'lower' => array(8086)); /* GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8095, 'status' => 'F', 'lower' => array(7975, 953)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8095, 'status' => 'S', 'lower' => array(8087)); /* GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8096, 'status' => 'F', 'lower' => array(8032, 953)); /* GREEK SMALL LETTER OMEGA WITH PSILI AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8097, 'status' => 'F', 'lower' => array(8033, 953)); /* GREEK SMALL LETTER OMEGA WITH DASIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8098, 'status' => 'F', 'lower' => array(8034, 953)); /* GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8099, 'status' => 'F', 'lower' => array(8035, 953)); /* GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8100, 'status' => 'F', 'lower' => array(8036, 953)); /* GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8101, 'status' => 'F', 'lower' => array(8037, 953)); /* GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8102, 'status' => 'F', 'lower' => array(8038, 953)); /* GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8103, 'status' => 'F', 'lower' => array(8039, 953)); /* GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8104, 'status' => 'F', 'lower' => array(8032, 953)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8104, 'status' => 'S', 'lower' => array(8096)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8105, 'status' => 'F', 'lower' => array(8033, 953)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8105, 'status' => 'S', 'lower' => array(8097)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8106, 'status' => 'F', 'lower' => array(8034, 953)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8106, 'status' => 'S', 'lower' => array(8098)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8107, 'status' => 'F', 'lower' => array(8035, 953)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8107, 'status' => 'S', 'lower' => array(8099)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8108, 'status' => 'F', 'lower' => array(8036, 953)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8108, 'status' => 'S', 'lower' => array(8100)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8109, 'status' => 'F', 'lower' => array(8037, 953)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8109, 'status' => 'S', 'lower' => array(8101)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8110, 'status' => 'F', 'lower' => array(8038, 953)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8110, 'status' => 'S', 'lower' => array(8102)); /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8111, 'status' => 'F', 'lower' => array(8039, 953)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8111, 'status' => 'S', 'lower' => array(8103)); /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8114, 'status' => 'F', 'lower' => array(8048, 953)); /* GREEK SMALL LETTER ALPHA WITH VARIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8115, 'status' => 'F', 'lower' => array(945, 953)); /* GREEK SMALL LETTER ALPHA WITH YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8116, 'status' => 'F', 'lower' => array(940, 953)); /* GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8118, 'status' => 'F', 'lower' => array(945, 834)); /* GREEK SMALL LETTER ALPHA WITH PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 8119, 'status' => 'F', 'lower' => array(945, 834, 953)); /* GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8120, 'status' => 'C', 'lower' => array(8112)); /* GREEK CAPITAL LETTER ALPHA WITH VRACHY */ +$config['1f00_1fff'][] = array('upper' => 8121, 'status' => 'C', 'lower' => array(8113)); /* GREEK CAPITAL LETTER ALPHA WITH MACRON */ +$config['1f00_1fff'][] = array('upper' => 8122, 'status' => 'C', 'lower' => array(8048)); /* GREEK CAPITAL LETTER ALPHA WITH VARIA */ +$config['1f00_1fff'][] = array('upper' => 8123, 'status' => 'C', 'lower' => array(8049)); /* GREEK CAPITAL LETTER ALPHA WITH OXIA */ +$config['1f00_1fff'][] = array('upper' => 8124, 'status' => 'F', 'lower' => array(945, 953)); /* GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8124, 'status' => 'S', 'lower' => array(8115)); /* GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8126, 'status' => 'C', 'lower' => array(953)); /* GREEK PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8130, 'status' => 'F', 'lower' => array(8052, 953)); /* GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8131, 'status' => 'F', 'lower' => array(951, 953)); /* GREEK SMALL LETTER ETA WITH YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8132, 'status' => 'F', 'lower' => array(942, 953)); /* GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8134, 'status' => 'F', 'lower' => array(951, 834)); /* GREEK SMALL LETTER ETA WITH PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 8135, 'status' => 'F', 'lower' => array(951, 834, 953)); /* GREEK SMALL LETTER ETA WITH PERISPOMENI AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8136, 'status' => 'C', 'lower' => array(8050)); /* GREEK CAPITAL LETTER EPSILON WITH VARIA */ +$config['1f00_1fff'][] = array('upper' => 8137, 'status' => 'C', 'lower' => array(8051)); /* GREEK CAPITAL LETTER EPSILON WITH OXIA */ +$config['1f00_1fff'][] = array('upper' => 8138, 'status' => 'C', 'lower' => array(8052)); /* GREEK CAPITAL LETTER ETA WITH VARIA */ +$config['1f00_1fff'][] = array('upper' => 8139, 'status' => 'C', 'lower' => array(8053)); /* GREEK CAPITAL LETTER ETA WITH OXIA */ +$config['1f00_1fff'][] = array('upper' => 8140, 'status' => 'F', 'lower' => array(951, 953)); /* GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8140, 'status' => 'S', 'lower' => array(8131)); /* GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8146, 'status' => 'F', 'lower' => array(953, 776, 768)); /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 8147, 'status' => 'F', 'lower' => array(953, 776, 769)); /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 8150, 'status' => 'F', 'lower' => array(953, 834)); /* GREEK SMALL LETTER IOTA WITH PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 8151, 'status' => 'F', 'lower' => array(953, 776, 834)); /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 8152, 'status' => 'C', 'lower' => array(8144)); /* GREEK CAPITAL LETTER IOTA WITH VRACHY */ +$config['1f00_1fff'][] = array('upper' => 8153, 'status' => 'C', 'lower' => array(8145)); /* GREEK CAPITAL LETTER IOTA WITH MACRON */ +$config['1f00_1fff'][] = array('upper' => 8154, 'status' => 'C', 'lower' => array(8054)); /* GREEK CAPITAL LETTER IOTA WITH VARIA */ +$config['1f00_1fff'][] = array('upper' => 8155, 'status' => 'C', 'lower' => array(8055)); /* GREEK CAPITAL LETTER IOTA WITH OXIA */ +$config['1f00_1fff'][] = array('upper' => 8162, 'status' => 'F', 'lower' => array(965, 776, 768)); /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND VARIA */ +$config['1f00_1fff'][] = array('upper' => 8163, 'status' => 'F', 'lower' => array(965, 776, 769)); /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA */ +$config['1f00_1fff'][] = array('upper' => 8164, 'status' => 'F', 'lower' => array(961, 787)); /* GREEK SMALL LETTER RHO WITH PSILI */ +$config['1f00_1fff'][] = array('upper' => 8166, 'status' => 'F', 'lower' => array(965, 834)); /* GREEK SMALL LETTER UPSILON WITH PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 8167, 'status' => 'F', 'lower' => array(965, 776, 834)); /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 8168, 'status' => 'C', 'lower' => array(8160)); /* GREEK CAPITAL LETTER UPSILON WITH VRACHY */ +$config['1f00_1fff'][] = array('upper' => 8169, 'status' => 'C', 'lower' => array(8161)); /* GREEK CAPITAL LETTER UPSILON WITH MACRON */ +$config['1f00_1fff'][] = array('upper' => 8170, 'status' => 'C', 'lower' => array(8058)); /* GREEK CAPITAL LETTER UPSILON WITH VARIA */ +$config['1f00_1fff'][] = array('upper' => 8171, 'status' => 'C', 'lower' => array(8059)); /* GREEK CAPITAL LETTER UPSILON WITH OXIA */ +$config['1f00_1fff'][] = array('upper' => 8172, 'status' => 'C', 'lower' => array(8165)); /* GREEK CAPITAL LETTER RHO WITH DASIA */ +$config['1f00_1fff'][] = array('upper' => 8178, 'status' => 'F', 'lower' => array(8060, 953)); /* GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8179, 'status' => 'F', 'lower' => array(969, 953)); /* GREEK SMALL LETTER OMEGA WITH YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8180, 'status' => 'F', 'lower' => array(974, 953)); /* GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8182, 'status' => 'F', 'lower' => array(969, 834)); /* GREEK SMALL LETTER OMEGA WITH PERISPOMENI */ +$config['1f00_1fff'][] = array('upper' => 8183, 'status' => 'F', 'lower' => array(969, 834, 953)); /* GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8184, 'status' => 'C', 'lower' => array(8056)); /* GREEK CAPITAL LETTER OMICRON WITH VARIA */ +$config['1f00_1fff'][] = array('upper' => 8185, 'status' => 'C', 'lower' => array(8057)); /* GREEK CAPITAL LETTER OMICRON WITH OXIA */ +$config['1f00_1fff'][] = array('upper' => 8186, 'status' => 'C', 'lower' => array(8060)); /* GREEK CAPITAL LETTER OMEGA WITH VARIA */ +$config['1f00_1fff'][] = array('upper' => 8187, 'status' => 'C', 'lower' => array(8061)); /* GREEK CAPITAL LETTER OMEGA WITH OXIA */ +$config['1f00_1fff'][] = array('upper' => 8188, 'status' => 'F', 'lower' => array(969, 953)); /* GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI */ +$config['1f00_1fff'][] = array('upper' => 8188, 'status' => 'S', 'lower' => array(8179)); /* GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI */ +?> \ No newline at end of file diff --git a/cake/config/unicode/casefolding/2100_214f.php b/cake/config/unicode/casefolding/2100_214f.php new file mode 100755 index 0000000..42bf207 --- /dev/null +++ b/cake/config/unicode/casefolding/2100_214f.php @@ -0,0 +1,50 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Case Folding Properties. + * + * Provides case mapping of Unicode characters for code points U+2100 through U+214F + * + * @see http://www.unicode.org/Public/UNIDATA/UCD.html + * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt + * @see http://www.unicode.org/reports/tr21/tr21-5.html + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.config.unicode.casefolding + * @since CakePHP(tm) v 1.2.0.5691 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * The upper field is the decimal value of the upper case character + * + * The lower filed is an array of the decimal values that form the lower case version of a character. + * + * The status field is: + * C: common case folding, common mappings shared by both simple and full mappings. + * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces. + * S: simple case folding, mappings to single characters where different from F. + * T: special case for uppercase I and dotted uppercase I + * - For non-Turkic languages, this mapping is normally not used. + * - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters. + * Note that the Turkic mappings do not maintain canonical equivalence without additional processing. + * See the discussions of case mapping in the Unicode Standard for more information. + */ +$config['2100_214f'][] = array('upper' => 8486, 'status' => 'C', 'lower' => array(969)); /* OHM SIGN */ +$config['2100_214f'][] = array('upper' => 8490, 'status' => 'C', 'lower' => array(107)); /* KELVIN SIGN */ +$config['2100_214f'][] = array('upper' => 8491, 'status' => 'C', 'lower' => array(229)); /* ANGSTROM SIGN */ +$config['2100_214f'][] = array('upper' => 8498, 'status' => 'C', 'lower' => array(8526)); /* TURNED CAPITAL F */ +?> \ No newline at end of file diff --git a/cake/config/unicode/casefolding/2150_218f.php b/cake/config/unicode/casefolding/2150_218f.php new file mode 100755 index 0000000..ac4f9d1 --- /dev/null +++ b/cake/config/unicode/casefolding/2150_218f.php @@ -0,0 +1,63 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Case Folding Properties. + * + * Provides case mapping of Unicode characters for code points U+2150 through U+218F + * + * @see http://www.unicode.org/Public/UNIDATA/UCD.html + * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt + * @see http://www.unicode.org/reports/tr21/tr21-5.html + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.config.unicode.casefolding + * @since CakePHP(tm) v 1.2.0.5691 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * The upper field is the decimal value of the upper case character + * + * The lower filed is an array of the decimal values that form the lower case version of a character. + * + * The status field is: + * C: common case folding, common mappings shared by both simple and full mappings. + * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces. + * S: simple case folding, mappings to single characters where different from F. + * T: special case for uppercase I and dotted uppercase I + * - For non-Turkic languages, this mapping is normally not used. + * - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters. + * Note that the Turkic mappings do not maintain canonical equivalence without additional processing. + * See the discussions of case mapping in the Unicode Standard for more information. + */ +$config['2150_218f'][] = array('upper' => 8544, 'status' => 'C', 'lower' => array(8560)); /* ROMAN NUMERAL ONE */ +$config['2150_218f'][] = array('upper' => 8545, 'status' => 'C', 'lower' => array(8561)); /* ROMAN NUMERAL TWO */ +$config['2150_218f'][] = array('upper' => 8546, 'status' => 'C', 'lower' => array(8562)); /* ROMAN NUMERAL THREE */ +$config['2150_218f'][] = array('upper' => 8547, 'status' => 'C', 'lower' => array(8563)); /* ROMAN NUMERAL FOUR */ +$config['2150_218f'][] = array('upper' => 8548, 'status' => 'C', 'lower' => array(8564)); /* ROMAN NUMERAL FIVE */ +$config['2150_218f'][] = array('upper' => 8549, 'status' => 'C', 'lower' => array(8565)); /* ROMAN NUMERAL SIX */ +$config['2150_218f'][] = array('upper' => 8550, 'status' => 'C', 'lower' => array(8566)); /* ROMAN NUMERAL SEVEN */ +$config['2150_218f'][] = array('upper' => 8551, 'status' => 'C', 'lower' => array(8567)); /* ROMAN NUMERAL EIGHT */ +$config['2150_218f'][] = array('upper' => 8552, 'status' => 'C', 'lower' => array(8568)); /* ROMAN NUMERAL NINE */ +$config['2150_218f'][] = array('upper' => 8553, 'status' => 'C', 'lower' => array(8569)); /* ROMAN NUMERAL TEN */ +$config['2150_218f'][] = array('upper' => 8554, 'status' => 'C', 'lower' => array(8570)); /* ROMAN NUMERAL ELEVEN */ +$config['2150_218f'][] = array('upper' => 8555, 'status' => 'C', 'lower' => array(8571)); /* ROMAN NUMERAL TWELVE */ +$config['2150_218f'][] = array('upper' => 8556, 'status' => 'C', 'lower' => array(8572)); /* ROMAN NUMERAL FIFTY */ +$config['2150_218f'][] = array('upper' => 8557, 'status' => 'C', 'lower' => array(8573)); /* ROMAN NUMERAL ONE HUNDRED */ +$config['2150_218f'][] = array('upper' => 8558, 'status' => 'C', 'lower' => array(8574)); /* ROMAN NUMERAL FIVE HUNDRED */ +$config['2150_218f'][] = array('upper' => 8559, 'status' => 'C', 'lower' => array(8575)); /* ROMAN NUMERAL ONE THOUSAND */ +$config['2150_218f'][] = array('upper' => 8579, 'status' => 'C', 'lower' => array(8580)); /* ROMAN NUMERAL REVERSED ONE HUNDRED */ +?> \ No newline at end of file diff --git a/cake/config/unicode/casefolding/2460_24ff.php b/cake/config/unicode/casefolding/2460_24ff.php new file mode 100755 index 0000000..9efd391 --- /dev/null +++ b/cake/config/unicode/casefolding/2460_24ff.php @@ -0,0 +1,72 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Case Folding Properties. + * + * Provides case mapping of Unicode characters for code points U+2460 through U+24FF + * + * @see http://www.unicode.org/Public/UNIDATA/UCD.html + * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt + * @see http://www.unicode.org/reports/tr21/tr21-5.html + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.config.unicode.casefolding + * @since CakePHP(tm) v 1.2.0.5691 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * The upper field is the decimal value of the upper case character + * + * The lower filed is an array of the decimal values that form the lower case version of a character. + * + * The status field is: + * C: common case folding, common mappings shared by both simple and full mappings. + * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces. + * S: simple case folding, mappings to single characters where different from F. + * T: special case for uppercase I and dotted uppercase I + * - For non-Turkic languages, this mapping is normally not used. + * - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters. + * Note that the Turkic mappings do not maintain canonical equivalence without additional processing. + * See the discussions of case mapping in the Unicode Standard for more information. + */ +$config['2460_24ff'][] = array('upper' => 9398, 'status' => 'C', 'lower' => array(9424)); /* CIRCLED LATIN CAPITAL LETTER A */ +$config['2460_24ff'][] = array('upper' => 9399, 'status' => 'C', 'lower' => array(9425)); /* CIRCLED LATIN CAPITAL LETTER B */ +$config['2460_24ff'][] = array('upper' => 9400, 'status' => 'C', 'lower' => array(9426)); /* CIRCLED LATIN CAPITAL LETTER C */ +$config['2460_24ff'][] = array('upper' => 9401, 'status' => 'C', 'lower' => array(9427)); /* CIRCLED LATIN CAPITAL LETTER D */ +$config['2460_24ff'][] = array('upper' => 9402, 'status' => 'C', 'lower' => array(9428)); /* CIRCLED LATIN CAPITAL LETTER E */ +$config['2460_24ff'][] = array('upper' => 9403, 'status' => 'C', 'lower' => array(9429)); /* CIRCLED LATIN CAPITAL LETTER F */ +$config['2460_24ff'][] = array('upper' => 9404, 'status' => 'C', 'lower' => array(9430)); /* CIRCLED LATIN CAPITAL LETTER G */ +$config['2460_24ff'][] = array('upper' => 9405, 'status' => 'C', 'lower' => array(9431)); /* CIRCLED LATIN CAPITAL LETTER H */ +$config['2460_24ff'][] = array('upper' => 9406, 'status' => 'C', 'lower' => array(9432)); /* CIRCLED LATIN CAPITAL LETTER I */ +$config['2460_24ff'][] = array('upper' => 9407, 'status' => 'C', 'lower' => array(9433)); /* CIRCLED LATIN CAPITAL LETTER J */ +$config['2460_24ff'][] = array('upper' => 9408, 'status' => 'C', 'lower' => array(9434)); /* CIRCLED LATIN CAPITAL LETTER K */ +$config['2460_24ff'][] = array('upper' => 9409, 'status' => 'C', 'lower' => array(9435)); /* CIRCLED LATIN CAPITAL LETTER L */ +$config['2460_24ff'][] = array('upper' => 9410, 'status' => 'C', 'lower' => array(9436)); /* CIRCLED LATIN CAPITAL LETTER M */ +$config['2460_24ff'][] = array('upper' => 9411, 'status' => 'C', 'lower' => array(9437)); /* CIRCLED LATIN CAPITAL LETTER N */ +$config['2460_24ff'][] = array('upper' => 9412, 'status' => 'C', 'lower' => array(9438)); /* CIRCLED LATIN CAPITAL LETTER O */ +$config['2460_24ff'][] = array('upper' => 9413, 'status' => 'C', 'lower' => array(9439)); /* CIRCLED LATIN CAPITAL LETTER P */ +$config['2460_24ff'][] = array('upper' => 9414, 'status' => 'C', 'lower' => array(9440)); /* CIRCLED LATIN CAPITAL LETTER Q */ +$config['2460_24ff'][] = array('upper' => 9415, 'status' => 'C', 'lower' => array(9441)); /* CIRCLED LATIN CAPITAL LETTER R */ +$config['2460_24ff'][] = array('upper' => 9416, 'status' => 'C', 'lower' => array(9442)); /* CIRCLED LATIN CAPITAL LETTER S */ +$config['2460_24ff'][] = array('upper' => 9417, 'status' => 'C', 'lower' => array(9443)); /* CIRCLED LATIN CAPITAL LETTER T */ +$config['2460_24ff'][] = array('upper' => 9418, 'status' => 'C', 'lower' => array(9444)); /* CIRCLED LATIN CAPITAL LETTER U */ +$config['2460_24ff'][] = array('upper' => 9419, 'status' => 'C', 'lower' => array(9445)); /* CIRCLED LATIN CAPITAL LETTER V */ +$config['2460_24ff'][] = array('upper' => 9420, 'status' => 'C', 'lower' => array(9446)); /* CIRCLED LATIN CAPITAL LETTER W */ +$config['2460_24ff'][] = array('upper' => 9421, 'status' => 'C', 'lower' => array(9447)); /* CIRCLED LATIN CAPITAL LETTER X */ +$config['2460_24ff'][] = array('upper' => 9422, 'status' => 'C', 'lower' => array(9448)); /* CIRCLED LATIN CAPITAL LETTER Y */ +$config['2460_24ff'][] = array('upper' => 9423, 'status' => 'C', 'lower' => array(9449)); /* CIRCLED LATIN CAPITAL LETTER Z */ +?> \ No newline at end of file diff --git a/cake/config/unicode/casefolding/2c00_2c5f.php b/cake/config/unicode/casefolding/2c00_2c5f.php new file mode 100755 index 0000000..0b00aa5 --- /dev/null +++ b/cake/config/unicode/casefolding/2c00_2c5f.php @@ -0,0 +1,93 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Case Folding Properties. + * + * Provides case mapping of Unicode characters for code points U+2C00 through U+2C5F + * + * @see http://www.unicode.org/Public/UNIDATA/UCD.html + * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt + * @see http://www.unicode.org/reports/tr21/tr21-5.html + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.config.unicode.casefolding + * @since CakePHP(tm) v 1.2.0.5691 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * The upper field is the decimal value of the upper case character + * + * The lower filed is an array of the decimal values that form the lower case version of a character. + * + * The status field is: + * C: common case folding, common mappings shared by both simple and full mappings. + * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces. + * S: simple case folding, mappings to single characters where different from F. + * T: special case for uppercase I and dotted uppercase I + * - For non-Turkic languages, this mapping is normally not used. + * - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters. + * Note that the Turkic mappings do not maintain canonical equivalence without additional processing. + * See the discussions of case mapping in the Unicode Standard for more information. + */ +$config['2c00_2c5f'][] = array('upper' => 11264, 'status' => 'C', 'lower' => array(11312)); /* GLAGOLITIC CAPITAL LETTER AZU */ +$config['2c00_2c5f'][] = array('upper' => 11265, 'status' => 'C', 'lower' => array(11313)); /* GLAGOLITIC CAPITAL LETTER BUKY */ +$config['2c00_2c5f'][] = array('upper' => 11266, 'status' => 'C', 'lower' => array(11314)); /* GLAGOLITIC CAPITAL LETTER VEDE */ +$config['2c00_2c5f'][] = array('upper' => 11267, 'status' => 'C', 'lower' => array(11315)); /* GLAGOLITIC CAPITAL LETTER GLAGOLI */ +$config['2c00_2c5f'][] = array('upper' => 11268, 'status' => 'C', 'lower' => array(11316)); /* GLAGOLITIC CAPITAL LETTER DOBRO */ +$config['2c00_2c5f'][] = array('upper' => 11269, 'status' => 'C', 'lower' => array(11317)); /* GLAGOLITIC CAPITAL LETTER YESTU */ +$config['2c00_2c5f'][] = array('upper' => 11270, 'status' => 'C', 'lower' => array(11318)); /* GLAGOLITIC CAPITAL LETTER ZHIVETE */ +$config['2c00_2c5f'][] = array('upper' => 11271, 'status' => 'C', 'lower' => array(11319)); /* GLAGOLITIC CAPITAL LETTER DZELO */ +$config['2c00_2c5f'][] = array('upper' => 11272, 'status' => 'C', 'lower' => array(11320)); /* GLAGOLITIC CAPITAL LETTER ZEMLJA */ +$config['2c00_2c5f'][] = array('upper' => 11273, 'status' => 'C', 'lower' => array(11321)); /* GLAGOLITIC CAPITAL LETTER IZHE */ +$config['2c00_2c5f'][] = array('upper' => 11274, 'status' => 'C', 'lower' => array(11322)); /* GLAGOLITIC CAPITAL LETTER INITIAL IZHE */ +$config['2c00_2c5f'][] = array('upper' => 11275, 'status' => 'C', 'lower' => array(11323)); /* GLAGOLITIC CAPITAL LETTER I */ +$config['2c00_2c5f'][] = array('upper' => 11276, 'status' => 'C', 'lower' => array(11324)); /* GLAGOLITIC CAPITAL LETTER DJERVI */ +$config['2c00_2c5f'][] = array('upper' => 11277, 'status' => 'C', 'lower' => array(11325)); /* GLAGOLITIC CAPITAL LETTER KAKO */ +$config['2c00_2c5f'][] = array('upper' => 11278, 'status' => 'C', 'lower' => array(11326)); /* GLAGOLITIC CAPITAL LETTER LJUDIJE */ +$config['2c00_2c5f'][] = array('upper' => 11279, 'status' => 'C', 'lower' => array(11327)); /* GLAGOLITIC CAPITAL LETTER MYSLITE */ +$config['2c00_2c5f'][] = array('upper' => 11280, 'status' => 'C', 'lower' => array(11328)); /* GLAGOLITIC CAPITAL LETTER NASHI */ +$config['2c00_2c5f'][] = array('upper' => 11281, 'status' => 'C', 'lower' => array(11329)); /* GLAGOLITIC CAPITAL LETTER ONU */ +$config['2c00_2c5f'][] = array('upper' => 11282, 'status' => 'C', 'lower' => array(11330)); /* GLAGOLITIC CAPITAL LETTER POKOJI */ +$config['2c00_2c5f'][] = array('upper' => 11283, 'status' => 'C', 'lower' => array(11331)); /* GLAGOLITIC CAPITAL LETTER RITSI */ +$config['2c00_2c5f'][] = array('upper' => 11284, 'status' => 'C', 'lower' => array(11332)); /* GLAGOLITIC CAPITAL LETTER SLOVO */ +$config['2c00_2c5f'][] = array('upper' => 11285, 'status' => 'C', 'lower' => array(11333)); /* GLAGOLITIC CAPITAL LETTER TVRIDO */ +$config['2c00_2c5f'][] = array('upper' => 11286, 'status' => 'C', 'lower' => array(11334)); /* GLAGOLITIC CAPITAL LETTER UKU */ +$config['2c00_2c5f'][] = array('upper' => 11287, 'status' => 'C', 'lower' => array(11335)); /* GLAGOLITIC CAPITAL LETTER FRITU */ +$config['2c00_2c5f'][] = array('upper' => 11288, 'status' => 'C', 'lower' => array(11336)); /* GLAGOLITIC CAPITAL LETTER HERU */ +$config['2c00_2c5f'][] = array('upper' => 11289, 'status' => 'C', 'lower' => array(11337)); /* GLAGOLITIC CAPITAL LETTER OTU */ +$config['2c00_2c5f'][] = array('upper' => 11290, 'status' => 'C', 'lower' => array(11338)); /* GLAGOLITIC CAPITAL LETTER PE */ +$config['2c00_2c5f'][] = array('upper' => 11291, 'status' => 'C', 'lower' => array(11339)); /* GLAGOLITIC CAPITAL LETTER SHTA */ +$config['2c00_2c5f'][] = array('upper' => 11292, 'status' => 'C', 'lower' => array(11340)); /* GLAGOLITIC CAPITAL LETTER TSI */ +$config['2c00_2c5f'][] = array('upper' => 11293, 'status' => 'C', 'lower' => array(11341)); /* GLAGOLITIC CAPITAL LETTER CHRIVI */ +$config['2c00_2c5f'][] = array('upper' => 11294, 'status' => 'C', 'lower' => array(11342)); /* GLAGOLITIC CAPITAL LETTER SHA */ +$config['2c00_2c5f'][] = array('upper' => 11295, 'status' => 'C', 'lower' => array(11343)); /* GLAGOLITIC CAPITAL LETTER YERU */ +$config['2c00_2c5f'][] = array('upper' => 11296, 'status' => 'C', 'lower' => array(11344)); /* GLAGOLITIC CAPITAL LETTER YERI */ +$config['2c00_2c5f'][] = array('upper' => 11297, 'status' => 'C', 'lower' => array(11345)); /* GLAGOLITIC CAPITAL LETTER YATI */ +$config['2c00_2c5f'][] = array('upper' => 11298, 'status' => 'C', 'lower' => array(11346)); /* GLAGOLITIC CAPITAL LETTER SPIDERY HA */ +$config['2c00_2c5f'][] = array('upper' => 11299, 'status' => 'C', 'lower' => array(11347)); /* GLAGOLITIC CAPITAL LETTER YU */ +$config['2c00_2c5f'][] = array('upper' => 11300, 'status' => 'C', 'lower' => array(11348)); /* GLAGOLITIC CAPITAL LETTER SMALL YUS */ +$config['2c00_2c5f'][] = array('upper' => 11301, 'status' => 'C', 'lower' => array(11349)); /* GLAGOLITIC CAPITAL LETTER SMALL YUS WITH TAIL */ +$config['2c00_2c5f'][] = array('upper' => 11302, 'status' => 'C', 'lower' => array(11350)); /* GLAGOLITIC CAPITAL LETTER YO */ +$config['2c00_2c5f'][] = array('upper' => 11303, 'status' => 'C', 'lower' => array(11351)); /* GLAGOLITIC CAPITAL LETTER IOTATED SMALL YUS */ +$config['2c00_2c5f'][] = array('upper' => 11304, 'status' => 'C', 'lower' => array(11352)); /* GLAGOLITIC CAPITAL LETTER BIG YUS */ +$config['2c00_2c5f'][] = array('upper' => 11305, 'status' => 'C', 'lower' => array(11353)); /* GLAGOLITIC CAPITAL LETTER IOTATED BIG YUS */ +$config['2c00_2c5f'][] = array('upper' => 11306, 'status' => 'C', 'lower' => array(11354)); /* GLAGOLITIC CAPITAL LETTER FITA */ +$config['2c00_2c5f'][] = array('upper' => 11307, 'status' => 'C', 'lower' => array(11355)); /* GLAGOLITIC CAPITAL LETTER IZHITSA */ +$config['2c00_2c5f'][] = array('upper' => 11308, 'status' => 'C', 'lower' => array(11356)); /* GLAGOLITIC CAPITAL LETTER SHTAPIC */ +$config['2c00_2c5f'][] = array('upper' => 11309, 'status' => 'C', 'lower' => array(11357)); /* GLAGOLITIC CAPITAL LETTER TROKUTASTI A */ +$config['2c00_2c5f'][] = array('upper' => 11310, 'status' => 'C', 'lower' => array(11358)); /* GLAGOLITIC CAPITAL LETTER LATINATE MYSLITE */ +?> \ No newline at end of file diff --git a/cake/config/unicode/casefolding/2c60_2c7f.php b/cake/config/unicode/casefolding/2c60_2c7f.php new file mode 100755 index 0000000..91dc2e4 --- /dev/null +++ b/cake/config/unicode/casefolding/2c60_2c7f.php @@ -0,0 +1,54 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Case Folding Properties. + * + * Provides case mapping of Unicode characters for code points U+2C60 through U+2C7F + * + * @see http://www.unicode.org/Public/UNIDATA/UCD.html + * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt + * @see http://www.unicode.org/reports/tr21/tr21-5.html + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.config.unicode.casefolding + * @since CakePHP(tm) v 1.2.0.5691 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * The upper field is the decimal value of the upper case character + * + * The lower filed is an array of the decimal values that form the lower case version of a character. + * + * The status field is: + * C: common case folding, common mappings shared by both simple and full mappings. + * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces. + * S: simple case folding, mappings to single characters where different from F. + * T: special case for uppercase I and dotted uppercase I + * - For non-Turkic languages, this mapping is normally not used. + * - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters. + * Note that the Turkic mappings do not maintain canonical equivalence without additional processing. + * See the discussions of case mapping in the Unicode Standard for more information. + */ +$config['2c60_2c7f'][] = array('upper' => 11360, 'status' => 'C', 'lower' => array(11361)); /* LATIN CAPITAL LETTER L WITH DOUBLE BAR */ +$config['2c60_2c7f'][] = array('upper' => 11362, 'status' => 'C', 'lower' => array(619)); /* LATIN CAPITAL LETTER L WITH MIDDLE TILDE */ +$config['2c60_2c7f'][] = array('upper' => 11363, 'status' => 'C', 'lower' => array(7549)); /* LATIN CAPITAL LETTER P WITH STROKE */ +$config['2c60_2c7f'][] = array('upper' => 11364, 'status' => 'C', 'lower' => array(637)); /* LATIN CAPITAL LETTER R WITH TAIL */ +$config['2c60_2c7f'][] = array('upper' => 11367, 'status' => 'C', 'lower' => array(11368)); /* LATIN CAPITAL LETTER H WITH DESCENDER */ +$config['2c60_2c7f'][] = array('upper' => 11369, 'status' => 'C', 'lower' => array(11370)); /* LATIN CAPITAL LETTER K WITH DESCENDER */ +$config['2c60_2c7f'][] = array('upper' => 11371, 'status' => 'C', 'lower' => array(11372)); /* LATIN CAPITAL LETTER Z WITH DESCENDER */ +$config['2c60_2c7f'][] = array('upper' => 11381, 'status' => 'C', 'lower' => array(11382)); /* LATIN CAPITAL LETTER HALF H */ +?> \ No newline at end of file diff --git a/cake/config/unicode/casefolding/2c80_2cff.php b/cake/config/unicode/casefolding/2c80_2cff.php new file mode 100755 index 0000000..8f07be2 --- /dev/null +++ b/cake/config/unicode/casefolding/2c80_2cff.php @@ -0,0 +1,96 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Case Folding Properties. + * + * Provides case mapping of Unicode characters for code points U+2C80 through U+2CFF + * + * @see http://www.unicode.org/Public/UNIDATA/UCD.html + * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt + * @see http://www.unicode.org/reports/tr21/tr21-5.html + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.config.unicode.casefolding + * @since CakePHP(tm) v 1.2.0.5691 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * The upper field is the decimal value of the upper case character + * + * The lower filed is an array of the decimal values that form the lower case version of a character. + * + * The status field is: + * C: common case folding, common mappings shared by both simple and full mappings. + * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces. + * S: simple case folding, mappings to single characters where different from F. + * T: special case for uppercase I and dotted uppercase I + * - For non-Turkic languages, this mapping is normally not used. + * - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters. + * Note that the Turkic mappings do not maintain canonical equivalence without additional processing. + * See the discussions of case mapping in the Unicode Standard for more information. + */ +$config['2c80_2cff'][] = array('upper' => 11392, 'status' => 'C', 'lower' => array(11393)); /* COPTIC CAPITAL LETTER ALFA */ +$config['2c80_2cff'][] = array('upper' => 11394, 'status' => 'C', 'lower' => array(11395)); /* COPTIC CAPITAL LETTER VIDA */ +$config['2c80_2cff'][] = array('upper' => 11396, 'status' => 'C', 'lower' => array(11397)); /* COPTIC CAPITAL LETTER GAMMA */ +$config['2c80_2cff'][] = array('upper' => 11398, 'status' => 'C', 'lower' => array(11399)); /* COPTIC CAPITAL LETTER DALDA */ +$config['2c80_2cff'][] = array('upper' => 11400, 'status' => 'C', 'lower' => array(11401)); /* COPTIC CAPITAL LETTER EIE */ +$config['2c80_2cff'][] = array('upper' => 11402, 'status' => 'C', 'lower' => array(11403)); /* COPTIC CAPITAL LETTER SOU */ +$config['2c80_2cff'][] = array('upper' => 11404, 'status' => 'C', 'lower' => array(11405)); /* COPTIC CAPITAL LETTER ZATA */ +$config['2c80_2cff'][] = array('upper' => 11406, 'status' => 'C', 'lower' => array(11407)); /* COPTIC CAPITAL LETTER HATE */ +$config['2c80_2cff'][] = array('upper' => 11408, 'status' => 'C', 'lower' => array(11409)); /* COPTIC CAPITAL LETTER THETHE */ +$config['2c80_2cff'][] = array('upper' => 11410, 'status' => 'C', 'lower' => array(11411)); /* COPTIC CAPITAL LETTER IAUDA */ +$config['2c80_2cff'][] = array('upper' => 11412, 'status' => 'C', 'lower' => array(11413)); /* COPTIC CAPITAL LETTER KAPA */ +$config['2c80_2cff'][] = array('upper' => 11414, 'status' => 'C', 'lower' => array(11415)); /* COPTIC CAPITAL LETTER LAULA */ +$config['2c80_2cff'][] = array('upper' => 11416, 'status' => 'C', 'lower' => array(11417)); /* COPTIC CAPITAL LETTER MI */ +$config['2c80_2cff'][] = array('upper' => 11418, 'status' => 'C', 'lower' => array(11419)); /* COPTIC CAPITAL LETTER NI */ +$config['2c80_2cff'][] = array('upper' => 11420, 'status' => 'C', 'lower' => array(11421)); /* COPTIC CAPITAL LETTER KSI */ +$config['2c80_2cff'][] = array('upper' => 11422, 'status' => 'C', 'lower' => array(11423)); /* COPTIC CAPITAL LETTER O */ +$config['2c80_2cff'][] = array('upper' => 11424, 'status' => 'C', 'lower' => array(11425)); /* COPTIC CAPITAL LETTER PI */ +$config['2c80_2cff'][] = array('upper' => 11426, 'status' => 'C', 'lower' => array(11427)); /* COPTIC CAPITAL LETTER RO */ +$config['2c80_2cff'][] = array('upper' => 11428, 'status' => 'C', 'lower' => array(11429)); /* COPTIC CAPITAL LETTER SIMA */ +$config['2c80_2cff'][] = array('upper' => 11430, 'status' => 'C', 'lower' => array(11431)); /* COPTIC CAPITAL LETTER TAU */ +$config['2c80_2cff'][] = array('upper' => 11432, 'status' => 'C', 'lower' => array(11433)); /* COPTIC CAPITAL LETTER UA */ +$config['2c80_2cff'][] = array('upper' => 11434, 'status' => 'C', 'lower' => array(11435)); /* COPTIC CAPITAL LETTER FI */ +$config['2c80_2cff'][] = array('upper' => 11436, 'status' => 'C', 'lower' => array(11437)); /* COPTIC CAPITAL LETTER KHI */ +$config['2c80_2cff'][] = array('upper' => 11438, 'status' => 'C', 'lower' => array(11439)); /* COPTIC CAPITAL LETTER PSI */ +$config['2c80_2cff'][] = array('upper' => 11440, 'status' => 'C', 'lower' => array(11441)); /* COPTIC CAPITAL LETTER OOU */ +$config['2c80_2cff'][] = array('upper' => 11442, 'status' => 'C', 'lower' => array(11443)); /* COPTIC CAPITAL LETTER DIALECT-P ALEF */ +$config['2c80_2cff'][] = array('upper' => 11444, 'status' => 'C', 'lower' => array(11445)); /* COPTIC CAPITAL LETTER OLD COPTIC AIN */ +$config['2c80_2cff'][] = array('upper' => 11446, 'status' => 'C', 'lower' => array(11447)); /* COPTIC CAPITAL LETTER CRYPTOGRAMMIC EIE */ +$config['2c80_2cff'][] = array('upper' => 11448, 'status' => 'C', 'lower' => array(11449)); /* COPTIC CAPITAL LETTER DIALECT-P KAPA */ +$config['2c80_2cff'][] = array('upper' => 11450, 'status' => 'C', 'lower' => array(11451)); /* COPTIC CAPITAL LETTER DIALECT-P NI */ +$config['2c80_2cff'][] = array('upper' => 11452, 'status' => 'C', 'lower' => array(11453)); /* COPTIC CAPITAL LETTER CRYPTOGRAMMIC NI */ +$config['2c80_2cff'][] = array('upper' => 11454, 'status' => 'C', 'lower' => array(11455)); /* COPTIC CAPITAL LETTER OLD COPTIC OOU */ +$config['2c80_2cff'][] = array('upper' => 11456, 'status' => 'C', 'lower' => array(11457)); /* COPTIC CAPITAL LETTER SAMPI */ +$config['2c80_2cff'][] = array('upper' => 11458, 'status' => 'C', 'lower' => array(11459)); /* COPTIC CAPITAL LETTER CROSSED SHEI */ +$config['2c80_2cff'][] = array('upper' => 11460, 'status' => 'C', 'lower' => array(11461)); /* COPTIC CAPITAL LETTER OLD COPTIC SHEI */ +$config['2c80_2cff'][] = array('upper' => 11462, 'status' => 'C', 'lower' => array(11463)); /* COPTIC CAPITAL LETTER OLD COPTIC ESH */ +$config['2c80_2cff'][] = array('upper' => 11464, 'status' => 'C', 'lower' => array(11465)); /* COPTIC CAPITAL LETTER AKHMIMIC KHEI */ +$config['2c80_2cff'][] = array('upper' => 11466, 'status' => 'C', 'lower' => array(11467)); /* COPTIC CAPITAL LETTER DIALECT-P HORI */ +$config['2c80_2cff'][] = array('upper' => 11468, 'status' => 'C', 'lower' => array(11469)); /* COPTIC CAPITAL LETTER OLD COPTIC HORI */ +$config['2c80_2cff'][] = array('upper' => 11470, 'status' => 'C', 'lower' => array(11471)); /* COPTIC CAPITAL LETTER OLD COPTIC HA */ +$config['2c80_2cff'][] = array('upper' => 11472, 'status' => 'C', 'lower' => array(11473)); /* COPTIC CAPITAL LETTER L-SHAPED HA */ +$config['2c80_2cff'][] = array('upper' => 11474, 'status' => 'C', 'lower' => array(11475)); /* COPTIC CAPITAL LETTER OLD COPTIC HEI */ +$config['2c80_2cff'][] = array('upper' => 11476, 'status' => 'C', 'lower' => array(11477)); /* COPTIC CAPITAL LETTER OLD COPTIC HAT */ +$config['2c80_2cff'][] = array('upper' => 11478, 'status' => 'C', 'lower' => array(11479)); /* COPTIC CAPITAL LETTER OLD COPTIC GANGIA */ +$config['2c80_2cff'][] = array('upper' => 11480, 'status' => 'C', 'lower' => array(11481)); /* COPTIC CAPITAL LETTER OLD COPTIC DJA */ +$config['2c80_2cff'][] = array('upper' => 11482, 'status' => 'C', 'lower' => array(11483)); /* COPTIC CAPITAL LETTER OLD COPTIC SHIMA */ +$config['2c80_2cff'][] = array('upper' => 11484, 'status' => 'C', 'lower' => array(11485)); /* COPTIC CAPITAL LETTER OLD NUBIAN SHIMA */ +$config['2c80_2cff'][] = array('upper' => 11486, 'status' => 'C', 'lower' => array(11487)); /* COPTIC CAPITAL LETTER OLD NUBIAN NGI */ +$config['2c80_2cff'][] = array('upper' => 11488, 'status' => 'C', 'lower' => array(11489)); /* COPTIC CAPITAL LETTER OLD NUBIAN NYI */ +$config['2c80_2cff'][] = array('upper' => 11490, 'status' => 'C', 'lower' => array(11491)); /* COPTIC CAPITAL LETTER OLD NUBIAN WAU */ +?> \ No newline at end of file diff --git a/cake/config/unicode/casefolding/ff00_ffef.php b/cake/config/unicode/casefolding/ff00_ffef.php new file mode 100755 index 0000000..ddcd35e --- /dev/null +++ b/cake/config/unicode/casefolding/ff00_ffef.php @@ -0,0 +1,72 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Case Folding Properties. + * + * Provides case mapping of Unicode characters for code points U+FF00 through U+FFEF + * + * @see http://www.unicode.org/Public/UNIDATA/UCD.html + * @see http://www.unicode.org/Public/UNIDATA/CaseFolding.txt + * @see http://www.unicode.org/reports/tr21/tr21-5.html + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.config.unicode.casefolding + * @since CakePHP(tm) v 1.2.0.5691 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * The upper field is the decimal value of the upper case character + * + * The lower filed is an array of the decimal values that form the lower case version of a character. + * + * The status field is: + * C: common case folding, common mappings shared by both simple and full mappings. + * F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces. + * S: simple case folding, mappings to single characters where different from F. + * T: special case for uppercase I and dotted uppercase I + * - For non-Turkic languages, this mapping is normally not used. + * - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters. + * Note that the Turkic mappings do not maintain canonical equivalence without additional processing. + * See the discussions of case mapping in the Unicode Standard for more information. + */ +$config['ff00_ffef'][] = array('upper' => 65313, 'status' => 'C', 'lower' => array(65345)); /* FULLWIDTH LATIN CAPITAL LETTER A */ +$config['ff00_ffef'][] = array('upper' => 65314, 'status' => 'C', 'lower' => array(65346)); /* FULLWIDTH LATIN CAPITAL LETTER B */ +$config['ff00_ffef'][] = array('upper' => 65315, 'status' => 'C', 'lower' => array(65347)); /* FULLWIDTH LATIN CAPITAL LETTER C */ +$config['ff00_ffef'][] = array('upper' => 65316, 'status' => 'C', 'lower' => array(65348)); /* FULLWIDTH LATIN CAPITAL LETTER D */ +$config['ff00_ffef'][] = array('upper' => 65317, 'status' => 'C', 'lower' => array(65349)); /* FULLWIDTH LATIN CAPITAL LETTER E */ +$config['ff00_ffef'][] = array('upper' => 65318, 'status' => 'C', 'lower' => array(65350)); /* FULLWIDTH LATIN CAPITAL LETTER F */ +$config['ff00_ffef'][] = array('upper' => 65319, 'status' => 'C', 'lower' => array(65351)); /* FULLWIDTH LATIN CAPITAL LETTER G */ +$config['ff00_ffef'][] = array('upper' => 65320, 'status' => 'C', 'lower' => array(65352)); /* FULLWIDTH LATIN CAPITAL LETTER H */ +$config['ff00_ffef'][] = array('upper' => 65321, 'status' => 'C', 'lower' => array(65353)); /* FULLWIDTH LATIN CAPITAL LETTER I */ +$config['ff00_ffef'][] = array('upper' => 65322, 'status' => 'C', 'lower' => array(65354)); /* FULLWIDTH LATIN CAPITAL LETTER J */ +$config['ff00_ffef'][] = array('upper' => 65323, 'status' => 'C', 'lower' => array(65355)); /* FULLWIDTH LATIN CAPITAL LETTER K */ +$config['ff00_ffef'][] = array('upper' => 65324, 'status' => 'C', 'lower' => array(65356)); /* FULLWIDTH LATIN CAPITAL LETTER L */ +$config['ff00_ffef'][] = array('upper' => 65325, 'status' => 'C', 'lower' => array(65357)); /* FULLWIDTH LATIN CAPITAL LETTER M */ +$config['ff00_ffef'][] = array('upper' => 65326, 'status' => 'C', 'lower' => array(65358)); /* FULLWIDTH LATIN CAPITAL LETTER N */ +$config['ff00_ffef'][] = array('upper' => 65327, 'status' => 'C', 'lower' => array(65359)); /* FULLWIDTH LATIN CAPITAL LETTER O */ +$config['ff00_ffef'][] = array('upper' => 65328, 'status' => 'C', 'lower' => array(65360)); /* FULLWIDTH LATIN CAPITAL LETTER P */ +$config['ff00_ffef'][] = array('upper' => 65329, 'status' => 'C', 'lower' => array(65361)); /* FULLWIDTH LATIN CAPITAL LETTER Q */ +$config['ff00_ffef'][] = array('upper' => 65330, 'status' => 'C', 'lower' => array(65362)); /* FULLWIDTH LATIN CAPITAL LETTER R */ +$config['ff00_ffef'][] = array('upper' => 65331, 'status' => 'C', 'lower' => array(65363)); /* FULLWIDTH LATIN CAPITAL LETTER S */ +$config['ff00_ffef'][] = array('upper' => 65332, 'status' => 'C', 'lower' => array(65364)); /* FULLWIDTH LATIN CAPITAL LETTER T */ +$config['ff00_ffef'][] = array('upper' => 65333, 'status' => 'C', 'lower' => array(65365)); /* FULLWIDTH LATIN CAPITAL LETTER U */ +$config['ff00_ffef'][] = array('upper' => 65334, 'status' => 'C', 'lower' => array(65366)); /* FULLWIDTH LATIN CAPITAL LETTER V */ +$config['ff00_ffef'][] = array('upper' => 65335, 'status' => 'C', 'lower' => array(65367)); /* FULLWIDTH LATIN CAPITAL LETTER W */ +$config['ff00_ffef'][] = array('upper' => 65336, 'status' => 'C', 'lower' => array(65368)); /* FULLWIDTH LATIN CAPITAL LETTER X */ +$config['ff00_ffef'][] = array('upper' => 65337, 'status' => 'C', 'lower' => array(65369)); /* FULLWIDTH LATIN CAPITAL LETTER Y */ +$config['ff00_ffef'][] = array('upper' => 65338, 'status' => 'C', 'lower' => array(65370)); /* FULLWIDTH LATIN CAPITAL LETTER Z */ +?> \ No newline at end of file diff --git a/cake/console/cake b/cake/console/cake new file mode 100755 index 0000000..1257251 --- /dev/null +++ b/cake/console/cake @@ -0,0 +1,32 @@ +#!/bin/bash +################################################################################ +# +# Bake is a shell script for running CakePHP bake script +# PHP versions 4 and 5 +# +# CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) +# Copyright 2005-2007, Cake Software Foundation, Inc. +# +# Licensed under The MIT License +# Redistributions of files must retain the above copyright notice. +# +# @filesource +# @copyright Copyright 2005-2007, Cake Software Foundation, Inc. +# @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project +# @package cake +# @subpackage cake.cake.console +# @since CakePHP(tm) v 1.2.0.5012 +# @version $Revision$ +# @modifiedby $LastChangedBy$ +# @lastmodified $Date$ +# @license http://www.opensource.org/licenses/mit-license.php The MIT License +# +################################################################################ +clear + +LIB=${0/%cake/} +APP=`pwd` + +exec php -q ${LIB}cake.php -working "${APP}" "$@" + +exit; \ No newline at end of file diff --git a/cake/console/cake.bat b/cake/console/cake.bat new file mode 100755 index 0000000..c0a531c --- /dev/null +++ b/cake/console/cake.bat @@ -0,0 +1,35 @@ +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:: +:: Bake is a shell script for running CakePHP bake script +:: PHP versions 4 and 5 +:: +:: CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) +:: Copyright 2005-2007, Cake Software Foundation, Inc. +:: +:: Licensed under The MIT License +:: Redistributions of files must retain the above copyright notice. +:: +:: @filesource +:: @copyright Copyright 2005-2007, Cake Software Foundation, Inc. +:: @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project +:: @package cake +:: @subpackage cake.cake.console +:: @since CakePHP(tm) v 1.2.0.5012 +:: @version $Revision$ +:: @modifiedby $LastChangedBy$ +:: @lastmodified $Date$ +:: @license http://www.opensource.org/licenses/mit-license.php The MIT License +:: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + +:: In order for this script to work as intended, the cake\console\ folder must be in your PATH + +@echo. +@echo off + +SET app=%0 +SET lib=%~dp0 + +php -q "%lib%cake.php" -working "%CD%" %* + +echo. \ No newline at end of file diff --git a/cake/console/cake.php b/cake/console/cake.php new file mode 100755 index 0000000..2210bee --- /dev/null +++ b/cake/console/cake.php @@ -0,0 +1,592 @@ +#!/usr/bin/php -q +<?php +/* SVN FILE: $Id$ */ +/** + * Command-line code generation utility to automate programmer chores. + * + * Shell dispatcher class + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.console + * @since CakePHP(tm) v 1.2.0.5012 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +if (!defined('E_DEPRECATED')) { + define('E_DEPRECATED', 8192); +} +/** + * Shell dispatcher + * + * @package cake + * @subpackage cake.cake.console + */ +class ShellDispatcher { +/** + * Standard input stream. + * + * @var filehandle + * @access public + */ + var $stdin; +/** + * Standard output stream. + * + * @var filehandle + * @access public + */ + var $stdout; +/** + * Standard error stream. + * + * @var filehandle + * @access public + */ + var $stderr; +/** + * Contains command switches parsed from the command line. + * + * @var array + * @access public + */ + var $params = array(); +/** + * Contains arguments parsed from the command line. + * + * @var array + * @access public + */ + var $args = array(); +/** + * The file name of the shell that was invoked. + * + * @var string + * @access public + */ + var $shell = null; +/** + * The class name of the shell that was invoked. + * + * @var string + * @access public + */ + var $shellClass = null; +/** + * The command called if public methods are available. + * + * @var string + * @access public + */ + var $shellCommand = null; +/** + * The path locations of shells. + * + * @var array + * @access public + */ + var $shellPaths = array(); +/** + * The path to the current shell location. + * + * @var string + * @access public + */ + var $shellPath = null; +/** + * The name of the shell in camelized. + * + * @var string + * @access public + */ + var $shellName = null; +/** + * Constructs this ShellDispatcher instance. + * + * @param array $args the argv. + */ + function ShellDispatcher($args = array()) { + $this->__construct($args); + } +/** + * Constructor + * + * @param array $args the argv. + */ + function __construct($args = array()) { + set_time_limit(0); + $this->__initConstants(); + $this->parseParams($args); + $this->_initEnvironment(); + $this->__buildPaths(); + $this->_stop($this->dispatch()); + } +/** + * Defines core configuration. + * + * @access private + */ + function __initConstants() { + if (function_exists('ini_set')) { + ini_set('display_errors', '1'); + ini_set('error_reporting', E_ALL & ~E_DEPRECATED); + ini_set('html_errors', false); + ini_set('implicit_flush', true); + ini_set('max_execution_time', 0); + } + + if (!defined('CAKE_CORE_INCLUDE_PATH')) { + define('PHP5', (PHP_VERSION >= 5)); + define('DS', DIRECTORY_SEPARATOR); + define('CAKE_CORE_INCLUDE_PATH', dirname(dirname(dirname(__FILE__)))); + define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS); + define('DISABLE_DEFAULT_ERROR_HANDLING', false); + define('CAKEPHP_SHELL', true); + } + require_once(CORE_PATH . 'cake' . DS . 'basics.php'); + } +/** + * Defines current working environment. + * + * @access protected + */ + function _initEnvironment() { + $this->stdin = fopen('php://stdin', 'r'); + $this->stdout = fopen('php://stdout', 'w'); + $this->stderr = fopen('php://stderr', 'w'); + + if (!$this->__bootstrap()) { + $this->stderr("\nCakePHP Console: "); + $this->stderr("\nUnable to load Cake core:"); + $this->stderr("\tMake sure " . DS . 'cake' . DS . 'libs exists in ' . CAKE_CORE_INCLUDE_PATH); + $this->_stop(); + } + + if (!isset($this->args[0]) || !isset($this->params['working'])) { + $this->stderr("\nCakePHP Console: "); + $this->stderr('This file has been loaded incorrectly and cannot continue.'); + $this->stderr('Please make sure that ' . DIRECTORY_SEPARATOR . 'cake' . DIRECTORY_SEPARATOR . 'console is in your system path,'); + $this->stderr('and check the manual for the correct usage of this command.'); + $this->stderr('(http://manual.cakephp.org/)'); + $this->_stop(); + } + + if (basename(__FILE__) != basename($this->args[0])) { + $this->stderr("\nCakePHP Console: "); + $this->stderr('Warning: the dispatcher may have been loaded incorrectly, which could lead to unexpected results...'); + if ($this->getInput('Continue anyway?', array('y', 'n'), 'y') == 'n') { + $this->_stop(); + } + } + + $this->shiftArgs(); + } +/** + * Builds the shell paths. + * + * @access private + * @return void + */ + function __buildPaths() { + $paths = array(); + $pluginPaths = Configure::read('pluginPaths'); + if (!class_exists('Folder')) { + require LIBS . 'folder.php'; + } + + foreach ($pluginPaths as $pluginPath) { + $Folder = new Folder($pluginPath); + list($plugins,) = $Folder->read(false, true); + foreach ((array)$plugins as $plugin) { + $path = $pluginPath . Inflector::underscore($plugin) . DS . 'vendors' . DS . 'shells' . DS; + if (file_exists($path)) { + $paths[] = $path; + } + } + } + + $vendorPaths = array_values(Configure::read('vendorPaths')); + foreach ($vendorPaths as $vendorPath) { + $path = rtrim($vendorPath, DS) . DS . 'shells' . DS; + if (file_exists($path)) { + $paths[] = $path; + } + } + + $this->shellPaths = array_values(array_unique(array_merge($paths, Configure::read('shellPaths')))); + } +/** + * Initializes the environment and loads the Cake core. + * + * @return boolean Success. + * @access private + */ + function __bootstrap() { + + define('ROOT', $this->params['root']); + define('APP_DIR', $this->params['app']); + define('APP_PATH', $this->params['working'] . DS); + define('WWW_ROOT', APP_PATH . $this->params['webroot'] . DS); + + $includes = array( + CORE_PATH . 'cake' . DS . 'config' . DS . 'paths.php', + CORE_PATH . 'cake' . DS . 'libs' . DS . 'object.php', + CORE_PATH . 'cake' . DS . 'libs' . DS . 'inflector.php', + CORE_PATH . 'cake' . DS . 'libs' . DS . 'configure.php', + CORE_PATH . 'cake' . DS . 'libs' . DS . 'file.php', + CORE_PATH . 'cake' . DS . 'libs' . DS . 'cache.php', + CORE_PATH . 'cake' . DS . 'libs' . DS . 'string.php', + CORE_PATH . 'cake' . DS . 'libs' . DS . 'class_registry.php', + CORE_PATH . 'cake' . DS . 'console' . DS . 'error.php' + ); + + foreach ($includes as $inc) { + if (!require($inc)) { + $this->stderr("Failed to load Cake core file {$inc}"); + return false; + } + } + + Configure::getInstance(file_exists(CONFIGS . 'bootstrap.php')); + + if (!file_exists(APP_PATH . 'config' . DS . 'core.php')) { + include_once CORE_PATH . 'cake' . DS . 'console' . DS . 'libs' . DS . 'templates' . DS . 'skel' . DS . 'config' . DS . 'core.php'; + Configure::buildPaths(array()); + } + + Configure::write('debug', 1); + return true; + } +/** + * Dispatches a CLI request + * + * @access public + */ + function dispatch() { + if (isset($this->args[0])) { + $plugin = null; + $shell = $this->args[0]; + if (strpos($shell, '.') !== false) { + list($plugin, $shell) = explode('.', $this->args[0]); + } + + $this->shell = $shell; + $this->shiftArgs(); + $this->shellName = Inflector::camelize($this->shell); + $this->shellClass = $this->shellName . 'Shell'; + + if ($this->shell === 'help') { + $this->help(); + } else { + $loaded = false; + foreach ($this->shellPaths as $path) { + $this->shellPath = $path . $this->shell . '.php'; + + $isPlugin = ($plugin && strpos($path, DS . $plugin . DS . 'vendors' . DS . 'shells' . DS) !== false); + if (($isPlugin && file_exists($this->shellPath)) || (!$plugin && file_exists($this->shellPath))) { + $loaded = true; + break; + } + } + + if ($loaded) { + if (!class_exists('Shell')) { + require CONSOLE_LIBS . 'shell.php'; + } + require $this->shellPath; + if (class_exists($this->shellClass)) { + $command = null; + if (isset($this->args[0])) { + $command = $this->args[0]; + } + $this->shellCommand = Inflector::variable($command); + $shell = new $this->shellClass($this); + + if (strtolower(get_parent_class($shell)) == 'shell') { + $shell->initialize(); + $shell->loadTasks(); + + foreach ($shell->taskNames as $task) { + if (strtolower(get_parent_class($shell)) == 'shell') { + $shell->{$task}->initialize(); + $shell->{$task}->loadTasks(); + } + } + + $task = Inflector::camelize($command); + if (in_array($task, $shell->taskNames)) { + $this->shiftArgs(); + $shell->{$task}->startup(); + if (isset($this->args[0]) && $this->args[0] == 'help') { + if (method_exists($shell->{$task}, 'help')) { + $shell->{$task}->help(); + $this->_stop(); + } else { + $this->help(); + } + } + return $shell->{$task}->execute(); + } + } + + $classMethods = get_class_methods($shell); + + $privateMethod = $missingCommand = false; + if ((in_array($command, $classMethods) || in_array(strtolower($command), $classMethods)) && strpos($command, '_', 0) === 0) { + $privateMethod = true; + } + + if (!in_array($command, $classMethods) && !in_array(strtolower($command), $classMethods)) { + $missingCommand = true; + } + + $protectedCommands = array( + 'initialize','in','out','err','hr', + 'createfile', 'isdir','copydir','object','tostring', + 'requestaction','log','cakeerror', 'shelldispatcher', + '__initconstants','__initenvironment','__construct', + 'dispatch','__bootstrap','getinput','stdout','stderr','parseparams','shiftargs' + ); + + if (in_array(strtolower($command), $protectedCommands)) { + $missingCommand = true; + } + + if ($missingCommand && method_exists($shell, 'main')) { + $shell->startup(); + return $shell->main(); + } elseif (!$privateMethod && method_exists($shell, $command)) { + $this->shiftArgs(); + $shell->startup(); + return $shell->{$command}(); + } else { + $this->stderr("Unknown {$this->shellName} command '$command'.\nFor usage, try 'cake {$this->shell} help'.\n\n"); + } + } else { + $this->stderr('Class '.$this->shellClass.' could not be loaded'); + } + } else { + $this->help(); + } + } + } else { + $this->help(); + } + } +/** + * Prompts the user for input, and returns it. + * + * @param string $prompt Prompt text. + * @param mixed $options Array or string of options. + * @param string $default Default input value. + * @return Either the default value, or the user-provided input. + * @access public + */ + function getInput($prompt, $options = null, $default = null) { + if (!is_array($options)) { + $printOptions = ''; + } else { + $printOptions = '(' . implode('/', $options) . ')'; + } + + if ($default == null) { + $this->stdout($prompt . " $printOptions \n" . '> ', false); + } else { + $this->stdout($prompt . " $printOptions \n" . "[$default] > ", false); + } + $result = fgets($this->stdin); + + if ($result === false) { + exit; + } + $result = trim($result); + + if ($default != null && empty($result)) { + return $default; + } + return $result; + } +/** + * Outputs to the stdout filehandle. + * + * @param string $string String to output. + * @param boolean $newline If true, the outputs gets an added newline. + * @access public + */ + function stdout($string, $newline = true) { + if ($newline) { + fwrite($this->stdout, $string . "\n"); + } else { + fwrite($this->stdout, $string); + } + } +/** + * Outputs to the stderr filehandle. + * + * @param string $string Error text to output. + * @access public + */ + function stderr($string) { + fwrite($this->stderr, 'Error: '. $string); + } +/** + * Parses command line options + * + * @param array $params Parameters to parse + * @access public + */ + function parseParams($params) { + $this->__parseParams($params); + $defaults = array('app' => 'app', 'root' => dirname(dirname(dirname(__FILE__))), 'working' => null, 'webroot' => 'webroot'); + $params = array_merge($defaults, array_intersect_key($this->params, $defaults)); + $isWin = false; + foreach ($defaults as $default => $value) { + if (strpos($params[$default], '\\') !== false) { + $isWin = true; + break; + } + } + $params = str_replace('\\', '/', $params); + + if (!empty($params['working']) && (!isset($this->args[0]) || isset($this->args[0]) && $this->args[0]{0} !== '.')) { + if (empty($this->params['app']) && $params['working'] != $params['root']) { + $params['root'] = dirname($params['working']); + $params['app'] = basename($params['working']); + } else { + $params['root'] = $params['working']; + } + } + + if ($params['app'][0] == '/' || preg_match('/([a-z])(:)/i', $params['app'], $matches)) { + $params['root'] = dirname($params['app']); + } elseif (strpos($params['app'], '/')) { + $params['root'] .= '/' . dirname($params['app']); + } + + $params['app'] = basename($params['app']); + $params['working'] = rtrim($params['root'], '/') . '/' . $params['app']; + + if (!empty($matches[0]) || !empty($isWin)) { + $params = str_replace('/', '\\', $params); + } + + $this->params = array_merge($this->params, $params); + } +/** + * Helper for recursively paraing params + * + * @return array params + * @access private + */ + function __parseParams($params) { + $count = count($params); + for ($i = 0; $i < $count; $i++) { + if (isset($params[$i])) { + if ($params[$i]{0} === '-') { + $key = substr($params[$i], 1); + $this->params[$key] = true; + unset($params[$i]); + if (isset($params[++$i])) { + if ($params[$i]{0} !== '-') { + $this->params[$key] = str_replace('"', '', $params[$i]); + unset($params[$i]); + } else { + $i--; + $this->__parseParams($params); + } + } + } else { + $this->args[] = $params[$i]; + unset($params[$i]); + } + + } + } + } +/** + * Removes first argument and shifts other arguments up + * + * @return boolean False if there are no arguments + * @access public + */ + function shiftArgs() { + if (empty($this->args)) { + return false; + } + unset($this->args[0]); + $this->args = array_values($this->args); + return true; + } +/** + * Shows console help + * + * @access public + */ + function help() { + $this->stdout("\nWelcome to CakePHP v" . Configure::version() . " Console"); + $this->stdout("---------------------------------------------------------------"); + $this->stdout("Current Paths:"); + $this->stdout(" -app: ". $this->params['app']); + $this->stdout(" -working: " . rtrim($this->params['working'], DS)); + $this->stdout(" -root: " . rtrim($this->params['root'], DS)); + $this->stdout(" -core: " . rtrim(CORE_PATH, DS)); + $this->stdout(""); + $this->stdout("Changing Paths:"); + $this->stdout("your working path should be the same as your application path"); + $this->stdout("to change your path use the '-app' param."); + $this->stdout("Example: -app relative/path/to/myapp or -app /absolute/path/to/myapp"); + + $this->stdout("\nAvailable Shells:"); + $_shells = array(); + + foreach ($this->shellPaths as $path) { + if (is_dir($path)) { + $shells = Configure::listObjects('file', $path); + $path = str_replace(CAKE_CORE_INCLUDE_PATH . DS . 'cake' . DS, 'CORE' . DS, $path); + $path = str_replace(APP, 'APP' . DS, $path); + $path = str_replace(ROOT, 'ROOT', $path); + $path = rtrim($path, DS); + $this->stdout("\n " . $path . ":"); + if (empty($shells)) { + $this->stdout("\t - none"); + } else { + sort($shells); + foreach ($shells as $shell) { + if ($shell !== 'shell.php') { + $this->stdout("\t " . str_replace('.php', '', $shell)); + } + } + } + } + } + $this->stdout("\nTo run a command, type 'cake shell_name [args]'"); + $this->stdout("To get help on a specific command, type 'cake shell_name help'"); + $this->_stop(); + } +/** + * Stop execution of the current script + * + * @param $status see http://php.net/exit for values + * @return void + * @access protected + */ + function _stop($status = 0) { + exit($status); + } +} +if (!defined('DISABLE_AUTO_DISPATCH')) { + $dispatcher = new ShellDispatcher($argv); +} +?> \ No newline at end of file diff --git a/cake/console/error.php b/cake/console/error.php new file mode 100755 index 0000000..9c88a40 --- /dev/null +++ b/cake/console/error.php @@ -0,0 +1,254 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * ErrorHandler for Console Shells + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.console + * @since CakePHP(tm) v 1.2.0.5074 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Error Handler for Cake console. + * + * @package cake + * @subpackage cake.cake.console + */ +class ErrorHandler extends Object { +/** + * Standard output stream. + * + * @var filehandle + * @access public + */ + var $stdout; +/** + * Standard error stream. + * + * @var filehandle + * @access public + */ + var $stderr; +/** + * Class constructor. + * + * @param string $method Method dispatching an error + * @param array $messages Error messages + */ + function __construct($method, $messages) { + $this->stdout = fopen('php://stdout', 'w'); + $this->stderr = fopen('php://stderr', 'w'); + if (Configure::read() > 0 || $method == 'error') { + call_user_func_array(array(&$this, $method), $messages); + } else { + call_user_func_array(array(&$this, 'error404'), $messages); + } + } +/** + * Displays an error page (e.g. 404 Not found). + * + * @param array $params Parameters (code, name, and message) + * @access public + */ + function error($params) { + extract($params, EXTR_OVERWRITE); + $this->stderr($code . $name . $message."\n"); + $this->_stop(); + } +/** + * Convenience method to display a 404 page. + * + * @param array $params Parameters (url, message) + * @access public + */ + function error404($params) { + extract($params, EXTR_OVERWRITE); + $this->error(array('code' => '404', + 'name' => 'Not found', + 'message' => sprintf(__("The requested address %s was not found on this server.", true), $url, $message))); + $this->_stop(); + } +/** + * Renders the Missing Controller web page. + * + * @param array $params Parameters (className) + * @access public + */ + function missingController($params) { + extract($params, EXTR_OVERWRITE); + $controllerName = str_replace('Controller', '', $className); + $this->stderr(sprintf(__("Missing Controller '%s'", true), $controllerName)); + $this->_stop(); + } +/** + * Renders the Missing Action web page. + * + * @param array $params Parameters (action, className) + * @access public + */ + function missingAction($params) { + extract($params, EXTR_OVERWRITE); + $this->stderr(sprintf(__("Missing Method '%s' in '%s'", true), $action, $className)); + $this->_stop(); + } +/** + * Renders the Private Action web page. + * + * @param array $params Parameters (action, className) + * @access public + */ + function privateAction($params) { + extract($params, EXTR_OVERWRITE); + $this->stderr(sprintf(__("Trying to access private method '%s' in '%s'", true), $action, $className)); + $this->_stop(); + } +/** + * Renders the Missing Table web page. + * + * @param array $params Parameters (table, className) + * @access public + */ + function missingTable($params) { + extract($params, EXTR_OVERWRITE); + $this->stderr(sprintf(__("Missing database table '%s' for model '%s'", true), $table, $className)); + $this->_stop(); + } +/** + * Renders the Missing Database web page. + * + * @param array $params Parameters + * @access public + */ + function missingDatabase($params = array()) { + $this->stderr(__("Missing Database", true)); + $this->_stop(); + } +/** + * Renders the Missing View web page. + * + * @param array $params Parameters (file, action, className) + * @access public + */ + function missingView($params) { + extract($params, EXTR_OVERWRITE); + $this->stderr(sprintf(__("Missing View '%s' for '%s' in '%s'", true), $file, $action, $className)); + $this->_stop(); + } +/** + * Renders the Missing Layout web page. + * + * @param array $params Parameters (file) + * @access public + */ + function missingLayout($params) { + extract($params, EXTR_OVERWRITE); + $this->stderr(sprintf(__("Missing Layout '%s'", true), $file)); + $this->_stop(); + } +/** + * Renders the Database Connection web page. + * + * @param array $params Parameters + * @access public + */ + function missingConnection($params) { + extract($params, EXTR_OVERWRITE); + $this->stderr(__("Missing Database Connection. Try 'cake bake'", true)); + $this->_stop(); + } +/** + * Renders the Missing Helper file web page. + * + * @param array $params Parameters (file, helper) + * @access public + */ + function missingHelperFile($params) { + extract($params, EXTR_OVERWRITE); + $this->stderr(sprintf(__("Missing Helper file '%s' for '%s'", true), $file, Inflector::camelize($helper))); + $this->_stop(); + } +/** + * Renders the Missing Helper class web page. + * + * @param array $params Parameters (file, helper) + * @access public + */ + function missingHelperClass($params) { + extract($params, EXTR_OVERWRITE); + $this->stderr(sprintf(__("Missing Helper class '%s' in '%s'", true), Inflector::camelize($helper), $file)); + $this->_stop(); + } +/** + * Renders the Missing Component file web page. + * + * @param array $params Parameters (file, component) + * @access public + */ + function missingComponentFile($params) { + extract($params, EXTR_OVERWRITE); + $this->stderr(sprintf(__("Missing Component file '%s' for '%s'", true), $file, Inflector::camelize($component))); + $this->_stop(); + } +/** + * Renders the Missing Component class web page. + * + * @param array $params Parameters (file, component) + * @access public + */ + function missingComponentClass($params) { + extract($params, EXTR_OVERWRITE); + $this->stderr(sprintf(__("Missing Component class '%s' in '%s'", true), Inflector::camelize($component), $file)); + $this->_stop(); + } +/** + * Renders the Missing Model class web page. + * + * @param array $params Parameters (className) + * @access public + */ + function missingModel($params) { + extract($params, EXTR_OVERWRITE); + $this->stderr(sprintf(__("Missing model '%s'", true), $className)); + $this->_stop(); + } +/** + * Outputs to the stdout filehandle. + * + * @param string $string String to output. + * @param boolean $newline If true, the outputs gets an added newline. + * @access public + */ + function stdout($string, $newline = true) { + if ($newline) { + fwrite($this->stdout, $string . "\n"); + } else { + fwrite($this->stdout, $string); + } + } +/** + * Outputs to the stderr filehandle. + * + * @param string $string Error text to output. + * @access public + */ + function stderr($string) { + fwrite($this->stderr, "Error: ". $string . "\n"); + } +} +?> \ No newline at end of file diff --git a/cake/console/libs/acl.php b/cake/console/libs/acl.php new file mode 100755 index 0000000..5a7873d --- /dev/null +++ b/cake/console/libs/acl.php @@ -0,0 +1,522 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Short description for file. + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.console.libs + * @since CakePHP(tm) v 1.2.0.5012 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +App::import('Component', 'Acl'); +App::import('Model', 'DbAcl'); +/** + * Shell for ACL management. + * + * @package cake + * @subpackage cake.cake.console.libs + */ +class AclShell extends Shell { +/** + * Contains instance of AclComponent + * + * @var AclComponent + * @access public + */ + var $Acl; +/** + * Contains arguments parsed from the command line. + * + * @var array + * @access public + */ + var $args; +/** + * Contains database source to use + * + * @var string + * @access public + */ + var $dataSource = 'default'; +/** + * Contains tasks to load and instantiate + * + * @var array + * @access public + */ + var $tasks = array('DbConfig'); +/** + * Override startup of the Shell + * + * @access public + */ + function startup() { + $this->dataSource = 'default'; + + if (isset($this->params['datasource'])) { + $this->dataSource = $this->params['datasource']; + } + + if (!in_array(Configure::read('Acl.classname'), array('DbAcl', 'DB_ACL'))) { + $out = "--------------------------------------------------\n"; + $out .= __("Error: Your current Cake configuration is set to", true) . "\n"; + $out .= __("an ACL implementation other than DB. Please change", true) . "\n"; + $out .= __("your core config to reflect your decision to use", true) . "\n"; + $out .= __("DbAcl before attempting to use this script", true) . ".\n"; + $out .= "--------------------------------------------------\n"; + $out .= sprintf(__("Current ACL Classname: %s", true), Configure::read('Acl.classname')) . "\n"; + $out .= "--------------------------------------------------\n"; + $this->err($out); + $this->_stop(); + } + + if ($this->command && !in_array($this->command, array('help'))) { + if (!config('database')) { + $this->out(__("Your database configuration was not found. Take a moment to create one.", true), true); + $this->args = null; + return $this->DbConfig->execute(); + } + require_once (CONFIGS.'database.php'); + + if (!in_array($this->command, array('initdb'))) { + $this->Acl = new AclComponent(); + $controller = null; + $this->Acl->startup($controller); + } + } + } +/** + * Override main() for help message hook + * + * @access public + */ + function main() { + $out = __("Available ACL commands:", true) . "\n"; + $out .= "\t - create\n"; + $out .= "\t - delete\n"; + $out .= "\t - setParent\n"; + $out .= "\t - getPath\n"; + $out .= "\t - check\n"; + $out .= "\t - grant\n"; + $out .= "\t - deny\n"; + $out .= "\t - inherit\n"; + $out .= "\t - view\n"; + $out .= "\t - initdb\n"; + $out .= "\t - help\n\n"; + $out .= __("For help, run the 'help' command. For help on a specific command, run 'help <command>'", true); + $this->out($out); + } +/** + * Creates an ARO/ACO node + * + * @access public + */ + function create() { + + $this->_checkArgs(3, 'create'); + $this->checkNodeType(); + extract($this->__dataVars()); + + $class = ucfirst($this->args[0]); + $object = new $class(); + + if (preg_match('/^([\w]+)\.(.*)$/', $this->args[1], $matches) && count($matches) == 3) { + $parent = array( + 'model' => $matches[1], + 'foreign_key' => $matches[2], + ); + } else { + $parent = $this->args[1]; + } + + if (!empty($parent) && $parent != '/' && $parent != 'root') { + @$parent = $object->node($parent); + if (empty($parent)) { + $this->err(sprintf(__('Could not find parent node using reference "%s"', true), $this->args[1])); + return; + } else { + $parent = Set::extract($parent, "0.{$class}.id"); + } + } else { + $parent = null; + } + + if (preg_match('/^([\w]+)\.(.*)$/', $this->args[2], $matches) && count($matches) == 3) { + $data = array( + 'model' => $matches[1], + 'foreign_key' => $matches[2], + ); + } else { + if (!($this->args[2] == '/')) { + $data = array('alias' => $this->args[2]); + } else { + $this->error(__('/ can not be used as an alias!', true), __('\t/ is the root, please supply a sub alias', true)); + } + } + + $data['parent_id'] = $parent; + $object->create(); + + if ($object->save($data)) { + $this->out(sprintf(__("New %s '%s' created.\n", true), $class, $this->args[2]), true); + } else { + $this->err(sprintf(__("There was a problem creating a new %s '%s'.", true), $class, $this->args[2])); + } + } +/** + * Delete an ARO/ACO node. + * + * @access public + */ + function delete() { + $this->_checkArgs(2, 'delete'); + $this->checkNodeType(); + extract($this->__dataVars()); + if (!$this->Acl->{$class}->delete($this->args[1])) { + $this->error(__("Node Not Deleted", true), sprintf(__("There was an error deleting the %s. Check that the node exists", true), $class) . ".\n"); + } + $this->out(sprintf(__("%s deleted", true), $class) . ".\n", true); + } + +/** + * Set parent for an ARO/ACO node. + * + * @access public + */ + function setParent() { + $this->_checkArgs(3, 'setParent'); + $this->checkNodeType(); + extract($this->__dataVars()); + $data = array( + $class => array( + 'id' => $this->args[1], + 'parent_id' => $this->args[2] + ) + ); + $this->Acl->{$class}->create(); + if (!$this->Acl->{$class}->save($data)) { + $this->out(__("Error in setting new parent. Please make sure the parent node exists, and is not a descendant of the node specified.", true), true); + } else { + $this->out(sprintf(__("Node parent set to %s", true), $this->args[2]) . "\n", true); + } + } +/** + * Get path to specified ARO/ACO node. + * + * @access public + */ + function getPath() { + $this->_checkArgs(2, 'getPath'); + $this->checkNodeType(); + extract($this->__dataVars()); + $id = ife(is_numeric($this->args[1]), intval($this->args[1]), $this->args[1]); + $nodes = $this->Acl->{$class}->getPath($id); + if (empty($nodes)) { + $this->error(sprintf(__("Supplied Node '%s' not found", true), $this->args[1]), __("No tree returned.", true)); + } + for ($i = 0; $i < count($nodes); $i++) { + $this->out(str_repeat(' ', $i) . "[" . $nodes[$i][$class]['id'] . "]" . $nodes[$i][$class]['alias'] . "\n"); + } + } +/** + * Check permission for a given ARO to a given ACO. + * + * @access public + */ + function check() { + $this->_checkArgs(3, 'check'); + extract($this->__getParams()); + + if ($this->Acl->check($aro, $aco, $action)) { + $this->out(sprintf(__("%s is allowed.", true), $aro), true); + } else { + $this->out(sprintf(__("%s is not allowed.", true), $aro), true); + } + } +/** + * Grant permission for a given ARO to a given ACO. + * + * @access public + */ + function grant() { + $this->_checkArgs(3, 'grant'); + extract($this->__getParams()); + + if ($this->Acl->allow($aro, $aco, $action)) { + $this->out(__("Permission granted.", true), true); + } else { + $this->out(__("Permission was not granted.", true), true); + } + } +/** + * Deny access for an ARO to an ACO. + * + * @access public + */ + function deny() { + $this->_checkArgs(3, 'deny'); + extract($this->__getParams()); + + if ($this->Acl->deny($aro, $aco, $action)) { + $this->out(__("Permission denied.", true), true); + } else { + $this->out(__("Permission was not denied.", true), true); + } + } +/** + * Set an ARO to inhermit permission to an ACO. + * + * @access public + */ + function inherit() { + $this->_checkArgs(3, 'inherit'); + extract($this->__getParams()); + + if ($this->Acl->inherit($aro, $aco, $action)) { + $this->out(__("Permission inherited.", true), true); + } else { + $this->out(__("Permission was not inherited.", true), true); + } + } +/** + * Show a specific ARO/ACO node. + * + * @access public + */ + function view() { + $this->_checkArgs(1, 'view'); + $this->checkNodeType(); + extract($this->__dataVars()); + if (isset($this->args[1]) && !is_null($this->args[1])) { + $key = ife(is_numeric($this->args[1]), $secondary_id, 'alias'); + $conditions = array($class . '.' . $key => $this->args[1]); + } else { + $conditions = null; + } + $nodes = $this->Acl->{$class}->find('all', array('conditions' => $conditions, 'order' => 'lft ASC')); + if (empty($nodes)) { + if (isset($this->args[1])) { + $this->error(sprintf(__("%s not found", true), $this->args[1]), __("No tree returned.", true)); + } elseif (isset($this->args[0])) { + $this->error(sprintf(__("%s not found", true), $this->args[0]), __("No tree returned.", true)); + } + } + $this->out($class . " tree:"); + $this->hr(); + $stack = array(); + $last = null; + foreach ($nodes as $n) { + $stack[] = $n; + if (!empty($last)) { + $end = end($stack); + if ($end[$class]['rght'] > $last) { + foreach ($stack as $k => $v) { + $end = end($stack); + if ($v[$class]['rght'] < $end[$class]['rght']) { + unset($stack[$k]); + } + } + } + } + $last = $n[$class]['rght']; + $count = count($stack); + $indent = str_repeat(' ', $count); + if ($n[$class]['alias']) { + $this->out($indent . "[" . $n[$class]['id'] . "]" . $n[$class]['alias']."\n"); + } else { + $this->out($indent . "[" . $n[$class]['id'] . "]" . $n[$class]['model'] . '.' . $n[$class]['foreign_key'] . "\n"); + } + } + $this->hr(); + } +/** + * Initialize ACL database. + * + * @access public + */ + function initdb() { + $this->Dispatch->args = array('schema', 'run', 'create', 'DbAcl'); + $this->Dispatch->dispatch(); + } +/** + * Show help screen. + * + * @access public + */ + function help() { + $head = __("Usage: cake acl <command> <arg1> <arg2>...", true) . "\n"; + $head .= "-----------------------------------------------\n"; + $head .= __("Commands:", true) . "\n\n"; + + $commands = array( + 'create' => "\tcreate aro|aco <parent> <node>\n" . + "\t\t" . __("Creates a new ACL object <node> under the parent specified by <parent>, an id/alias.", true) . "\n" . + "\t\t" . __("The <parent> and <node> references can be in one of the following formats:", true) . "\n" . + "\t\t\t- " . __("<model>.<id> - The node will be bound to a specific record of the given model", true) . "\n" . + "\t\t\t- " . __("<alias> - The node will be given a string alias (or path, in the case of <parent>),", true) . "\n" . + "\t\t\t " . __("i.e. 'John'. When used with <parent>, this takes the form of an alias path,", true) . "\n" . + "\t\t\t " . __("i.e. <group>/<subgroup>/<parent>.", true) . "\n" . + "\t\t" . __("To add a node at the root level, enter 'root' or '/' as the <parent> parameter.", true) . "\n", + + 'delete' => "\tdelete aro|aco <node>\n" . + "\t\t" . __("Deletes the ACL object with the given <node> reference (see 'create' for info on node references).", true) . "\n", + + 'setparent' => "\tsetParent aro|aco <node> <parent>\n" . + "\t\t" . __("Moves the ACL object specified by <node> beneath the parent ACL object specified by <parent>.", true) . "\n" . + "\t\t" . __("To identify the node and parent, use the row id.", true) . "\n", + + 'getpath' => "\tgetPath aro|aco <node>\n" . + "\t\t" . __("Returns the path to the ACL object specified by <node>. This command", true) . "\n" . + "\t\t" . __("is useful in determining the inhertiance of permissions for a certain", true) . "\n" . + "\t\t" . __("object in the tree.", true) . "\n" . + "\t\t" . __("For more detailed parameter usage info, see help for the 'create' command.", true) . "\n", + + 'check' => "\tcheck <aro_id> <aco_id> [<aco_action>] " . __("or", true) . " all\n" . + "\t\t" . __("Use this command to check ACL permissions.", true) . "\n" . + "\t\t" . __("For more detailed parameter usage info, see help for the 'create' command.", true) . "\n", + + 'grant' => "\tgrant <aro_id> <aco_id> [<aco_action>] " . __("or", true) . " all\n" . + "\t\t" . __("Use this command to grant ACL permissions. Once executed, the ARO", true) . "\n" . + "\t\t" . __("specified (and its children, if any) will have ALLOW access to the", true) . "\n" . + "\t\t" . __("specified ACO action (and the ACO's children, if any).", true) . "\n" . + "\t\t" . __("For more detailed parameter usage info, see help for the 'create' command.", true) . "\n", + + 'deny' => "\tdeny <aro_id> <aco_id> [<aco_action>]" . __("or", true) . " all\n" . + "\t\t" . __("Use this command to deny ACL permissions. Once executed, the ARO", true) . "\n" . + "\t\t" . __("specified (and its children, if any) will have DENY access to the", true) . "\n" . + "\t\t" . __("specified ACO action (and the ACO's children, if any).", true) . "\n" . + "\t\t" . __("For more detailed parameter usage info, see help for the 'create' command.", true) . "\n", + + 'inherit' => "\tinherit <aro_id> <aco_id> [<aco_action>]" . __("or", true) . " all\n" . + "\t\t" . __("Use this command to force a child ARO object to inherit its", true) . "\n" . + "\t\t" . __("permissions settings from its parent.", true) . "\n" . + "\t\t" . __("For more detailed parameter usage info, see help for the 'create' command.", true) . "\n", + + 'view' => "\tview aro|aco [<node>]\n" . + "\t\t" . __("The view command will return the ARO or ACO tree. The optional", true) . "\n" . + "\t\t" . __("id/alias parameter allows you to return only a portion of the requested tree.", true) . "\n" . + "\t\t" . __("For more detailed parameter usage info, see help for the 'create' command.", true) . "\n", + + 'initdb' => "\tinitdb\n". + "\t\t" . __("Uses this command : cake schema run create DbAcl", true) . "\n", + + 'help' => "\thelp [<command>]\n" . + "\t\t" . __("Displays this help message, or a message on a specific command.", true) . "\n" + ); + + $this->out($head); + if (!isset($this->args[0])) { + foreach ($commands as $cmd) { + $this->out("{$cmd}\n\n"); + } + } elseif (isset($commands[low($this->args[0])])) { + $this->out($commands[low($this->args[0])] . "\n\n"); + } else { + $this->out(sprintf(__("Command '%s' not found", true), $this->args[0])); + } + } +/** + * Check that first argument specifies a valid Node type (ARO/ACO) + * + * @access public + */ + function checkNodeType() { + if (!isset($this->args[0])) { + return false; + } + if ($this->args[0] != 'aco' && $this->args[0] != 'aro') { + $this->error(sprintf(__("Missing/Unknown node type: '%s'", true), $this->args[1]), __('Please specify which ACL object type you wish to create.', true)); + } + } +/** + * Checks that given node exists + * + * @param string $type Node type (ARO/ACO) + * @param integer $id Node id + * @return boolean Success + * @access public + */ + function nodeExists() { + if (!$this->checkNodeType() && !isset($this->args[1])) { + return false; + } + extract($this->__dataVars($this->args[0])); + $key = (ife(is_numeric($this->args[1]), $secondary_id, 'alias')); + $conditions = array($class . '.' . $key => $this->args[1]); + $possibility = $this->Acl->{$class}->find('all', compact('conditions')); + if (empty($possibility)) { + $this->error(sprintf(__("%s not found", true), $this->args[1]), __("No tree returned.", true)); + } + return $possibility; + } +/** + * get params for standard Acl methods + * + * @return array aro, aco, action + * @access private + */ + function __getParams() { + $aro = ife(is_numeric($this->args[0]), intval($this->args[0]), $this->args[0]); + $aco = ife(is_numeric($this->args[1]), intval($this->args[1]), $this->args[1]); + + if (is_string($aro) && preg_match('/^([\w]+)\.(.*)$/', $aro, $matches)) { + $aro = array( + 'model' => $matches[1], + 'foreign_key' => $matches[2], + ); + } + + if (is_string($aco) && preg_match('/^([\w]+)\.(.*)$/', $aco, $matches)) { + $aco = array( + 'model' => $matches[1], + 'foreign_key' => $matches[2], + ); + } + + $action = null; + if (isset($this->args[2])) { + $action = $this->args[2]; + if ($action == '' || $action == 'all') { + $action = '*'; + } + } + return compact('aro', 'aco', 'action'); + } + +/** + * Build data parameters based on node type + * + * @param string $type Node type (ARO/ACO) + * @return array Variables + * @access private + */ + function __dataVars($type = null) { + if ($type == null) { + $type = $this->args[0]; + } + $vars = array(); + $class = ucwords($type); + $vars['secondary_id'] = ife(strtolower($class) == 'aro', 'foreign_key', 'object_id'); + $vars['data_name'] = $type; + $vars['table_name'] = $type . 's'; + $vars['class'] = $class; + return $vars; + } +} +?> \ No newline at end of file diff --git a/cake/console/libs/api.php b/cake/console/libs/api.php new file mode 100755 index 0000000..87aefc1 --- /dev/null +++ b/cake/console/libs/api.php @@ -0,0 +1,216 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * API shell to get CakePHP core method signatures. + * + * Implementation of a Cake Shell to show CakePHP core method signatures. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.console.libs + * @since CakePHP(tm) v 1.2.0.5012 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ + +/** + * API shell to show method signatures of CakePHP core classes. + * + * @package cake + * @subpackage cake.cake.console.libs + */ +class ApiShell extends Shell { +/** + * Map between short name for paths and real paths. + * + * @var array + * @access public + */ + var $paths = array(); +/** + * Override intialize of the Shell + * + * @access public + */ + function initialize () { + $this->paths = array_merge($this->paths, array( + 'behavior' => LIBS . 'model' . DS . 'behaviors' . DS, + 'cache' => LIBS . 'cache' . DS, + 'controller' => LIBS . 'controller' . DS, + 'component' => LIBS . 'controller' . DS . 'components' . DS, + 'helper' => LIBS . 'view' . DS . 'helpers' . DS, + 'model' => LIBS . 'model' . DS, + 'view' => LIBS . 'view' . DS, + 'core' => LIBS + )); + } +/** + * Override main() to handle action + * + * @access public + */ + function main() { + if (empty($this->args)) { + return $this->help(); + } + + $type = strtolower($this->args[0]); + + if (isset($this->paths[$type])) { + $path = $this->paths[$type]; + } else { + $path = $this->paths['core']; + } + + if (count($this->args) == 1) { + $file = $type; + $class = Inflector::camelize($type); + } elseif (count($this->args) > 1) { + $file = Inflector::underscore($this->args[1]); + $class = Inflector::camelize($file); + } + + $objects = Configure::listObjects('class', $path); + if (in_array($class, $objects)) { + if (in_array($type, array('behavior', 'component', 'helper')) && $type !== $file) { + if (!preg_match('/' . Inflector::camelize($type) . '$/', $class)) { + $class .= Inflector::camelize($type); + } + } + + } else { + $this->err(sprintf(__("%s not found", true), $class)); + $this->_stop(); + } + + $parsed = $this->__parseClass($path . $file .'.php'); + + if (!empty($parsed)) { + if (isset($this->params['m'])) { + if (!isset($parsed[$this->params['m']])) { + $this->err(sprintf(__("%s::%s() could not be found", true), $class, $this->params['m'])); + $this->_stop(); + } + $method = $parsed[$this->params['m']]; + $this->out($class .'::'.$method['method'] . $method['parameters']); + $this->hr(); + $this->out($method['comment'], true); + } else { + $this->out(ucwords($class)); + $this->hr(); + $i = 0; + foreach ($parsed as $method) { + $list[] = ++$i . ". " . $method['method'] . $method['parameters']; + } + $this->out($list); + + $methods = array_keys($parsed); + while ($number = strtolower($this->in(__('Select a number to see the more information about a specific method. q to quit. l to list.', true), null, 'q'))) { + if ($number === 'q') { + $this->out(__('Done', true)); + $this->_stop(); + } + + if ($number === 'l') { + $this->out($list); + } + + if (isset($methods[--$number])) { + $method = $parsed[$methods[$number]]; + $this->hr(); + $this->out($class .'::'.$method['method'] . $method['parameters']); + $this->hr(); + $this->out($method['comment'], true); + } + } + } + } + } + +/** + * Show help for this shell. + * + * @access public + */ + function help() { + $head = "Usage: cake api [<type>] <className> [-m <method>]\n"; + $head .= "-----------------------------------------------\n"; + $head .= "Parameters:\n\n"; + + $commands = array( + 'path' => "\t<type>\n" . + "\t\tEither a full path or type of class (model, behavior, controller, component, view, helper).\n". + "\t\tAvailable values:\n\n". + "\t\tbehavior\tLook for class in CakePHP behavior path\n". + "\t\tcache\tLook for class in CakePHP cache path\n". + "\t\tcontroller\tLook for class in CakePHP controller path\n". + "\t\tcomponent\tLook for class in CakePHP component path\n". + "\t\thelper\tLook for class in CakePHP helper path\n". + "\t\tmodel\tLook for class in CakePHP model path\n". + "\t\tview\tLook for class in CakePHP view path\n", + 'className' => "\t<className>\n" . + "\t\tA CakePHP core class name (e.g: Component, HtmlHelper).\n" + ); + + $this->out($head); + if (!isset($this->args[1])) { + foreach ($commands as $cmd) { + $this->out("{$cmd}\n\n"); + } + } elseif (isset($commands[low($this->args[1])])) { + $this->out($commands[low($this->args[1])] . "\n\n"); + } else { + $this->out("Command '" . $this->args[1] . "' not found"); + } + } + +/** + * Parse a given class (located on given file) and get public methods and their + * signatures. + * + * @param object $File File object + * @param string $class Class name + * @return array Methods and signatures indexed by method name + * @access private + */ + function __parseClass($path) { + $parsed = array(); + + $File = new File($path); + if (!$File->exists()) { + $this->err(sprintf(__("%s could not be found", true), $File->name)); + $this->_stop(); + } + + $contents = $File->read(); + + if (preg_match_all('%(/\\*\\*[\\s\\S]*?\\*/)(\\s+function\\s+\\w+)(\\(.*\\))%', $contents, $result, PREG_PATTERN_ORDER)) { + foreach ($result[2] as $key => $method) { + $method = str_replace('function ', '', trim($method)); + + if (strpos($method, '__') === false && $method[0] != '_') { + $parsed[$method] = array( + 'comment' => str_replace(array('/*', '*/', '*'), '', trim($result[1][$key])), + 'method' => $method, + 'parameters' => trim($result[3][$key]) + ); + } + } + } + ksort($parsed); + return $parsed; + } +} +?> \ No newline at end of file diff --git a/cake/console/libs/bake.php b/cake/console/libs/bake.php new file mode 100755 index 0000000..f891ca2 --- /dev/null +++ b/cake/console/libs/bake.php @@ -0,0 +1,211 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Command-line code generation utility to automate programmer chores. + * + * Bake is CakePHP's code generation script, which can help you kickstart + * application development by writing fully functional skeleton controllers, + * models, and views. Going further, Bake can also write Unit Tests for you. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.console.libs + * @since CakePHP(tm) v 1.2.0.5012 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Bake is a command-line code generation utility for automating programmer chores. + * + * @package cake + * @subpackage cake.cake.console.libs + * @link http://book.cakephp.org/view/113/Code-Generation-with-Bake + */ +class BakeShell extends Shell { +/** + * Contains tasks to load and instantiate + * + * @var array + * @access public + */ + var $tasks = array('Project', 'DbConfig', 'Model', 'Controller', 'View', 'Plugin', 'Test'); +/** + * Override loadTasks() to handle paths + * + * @access public + */ + function loadTasks() { + parent::loadTasks(); + $task = Inflector::classify($this->command); + if (isset($this->{$task}) && !in_array($task, array('Project', 'DbConfig'))) { + $path = Inflector::underscore(Inflector::pluralize($this->command)); + $this->{$task}->path = $this->params['working'] . DS . $path . DS; + if (!is_dir($this->{$task}->path)) { + $this->err(sprintf(__("%s directory could not be found.\nBe sure you have created %s", true), $task, $this->{$task}->path)); + $this->_stop(); + } + } + } +/** + * Override main() to handle action + * + * @access public + */ + function main() { + if (!is_dir($this->DbConfig->path)) { + if ($this->Project->execute()) { + $this->DbConfig->path = $this->params['working'] . DS . 'config' . DS; + } + } + + if (!config('database')) { + $this->out(__("Your database configuration was not found. Take a moment to create one.", true)); + $this->args = null; + return $this->DbConfig->execute(); + } + $this->out('Interactive Bake Shell'); + $this->hr(); + $this->out('[D]atabase Configuration'); + $this->out('[M]odel'); + $this->out('[V]iew'); + $this->out('[C]ontroller'); + $this->out('[P]roject'); + $this->out('[Q]uit'); + + $classToBake = strtoupper($this->in(__('What would you like to Bake?', true), array('D', 'M', 'V', 'C', 'P', 'Q'))); + switch ($classToBake) { + case 'D': + $this->DbConfig->execute(); + break; + case 'M': + $this->Model->execute(); + break; + case 'V': + $this->View->execute(); + break; + case 'C': + $this->Controller->execute(); + break; + case 'P': + $this->Project->execute(); + break; + case 'Q': + exit(0); + break; + default: + $this->out(__('You have made an invalid selection. Please choose a type of class to Bake by entering D, M, V, or C.', true)); + } + $this->hr(); + $this->main(); + } +/** + * Quickly bake the MVC + * + * @access public + */ + function all() { + $ds = 'default'; + $this->hr(); + $this->out('Bake All'); + $this->hr(); + + if (isset($this->params['connection'])) { + $ds = $this->params['connection']; + } + + if (empty($this->args)) { + $name = $this->Model->getName($ds); + } + + if (!empty($this->args[0])) { + $name = $this->args[0]; + $this->Model->listAll($ds, false); + } + + $modelExists = false; + $model = $this->_modelName($name); + if (App::import('Model', $model)) { + $object = new $model(); + $modelExists = true; + } else { + App::import('Model'); + $object = new Model(array('name' => $name, 'ds' => $ds)); + } + + $modelBaked = $this->Model->bake($object, false); + + if ($modelBaked && $modelExists === false) { + $this->out(sprintf(__('%s Model was baked.', true), $model)); + if ($this->_checkUnitTest()) { + $this->Model->bakeTest($model); + } + $modelExists = true; + } + + if ($modelExists === true) { + $controller = $this->_controllerName($name); + if ($this->Controller->bake($controller, $this->Controller->bakeActions($controller))) { + $this->out(sprintf(__('%s Controller was baked.', true), $name)); + if ($this->_checkUnitTest()) { + $this->Controller->bakeTest($controller); + } + } + if (App::import('Controller', $controller)) { + $this->View->args = array($controller); + $this->View->execute(); + } + $this->out(__('Bake All complete')); + array_shift($this->args); + } else { + $this->err(__('Bake All could not continue without a valid model', true)); + } + + if (empty($this->args)) { + $this->all(); + } + $this->_stop(); + } + +/** + * Displays help contents + * + * @access public + */ + function help() { + $this->out('CakePHP Bake:'); + $this->hr(); + $this->out('The Bake script generates controllers, views and models for your application.'); + $this->out('If run with no command line arguments, Bake guides the user through the class'); + $this->out('creation process. You can customize the generation process by telling Bake'); + $this->out('where different parts of your application are using command line arguments.'); + $this->hr(); + $this->out("Usage: cake bake <command> <arg1> <arg2>..."); + $this->hr(); + $this->out('Params:'); + $this->out("\t-app <path> Absolute/Relative path to your app folder.\n"); + $this->out('Commands:'); + $this->out("\n\tbake help\n\t\tshows this help message."); + $this->out("\n\tbake all <name>\n\t\tbakes complete MVC. optional <name> of a Model"); + $this->out("\n\tbake project <path>\n\t\tbakes a new app folder in the path supplied\n\t\tor in current directory if no path is specified"); + $this->out("\n\tbake plugin <name>\n\t\tbakes a new plugin folder in the path supplied\n\t\tor in current directory if no path is specified."); + $this->out("\n\tbake db_config\n\t\tbakes a database.php file in config directory."); + $this->out("\n\tbake model\n\t\tbakes a model. run 'bake model help' for more info"); + $this->out("\n\tbake view\n\t\tbakes views. run 'bake view help' for more info"); + $this->out("\n\tbake controller\n\t\tbakes a controller. run 'bake controller help' for more info"); + $this->out(""); + + } +} +?> \ No newline at end of file diff --git a/cake/console/libs/console.php b/cake/console/libs/console.php new file mode 100755 index 0000000..19b0750 --- /dev/null +++ b/cake/console/libs/console.php @@ -0,0 +1,338 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Short description for file. + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.console.libs + * @since CakePHP(tm) v 1.2.0.5012 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * @package cake + * @subpackage cake.cake.console.libs + */ +class ConsoleShell extends Shell { +/** + * Available binding types + * + * @var array + * @access public + */ + var $associations = array('hasOne', 'hasMany', 'belongsTo', 'hasAndBelongsToMany'); +/** + * Chars that describe invalid commands + * + * @var array + * @access public + */ + var $badCommandChars = array('$', ';'); +/** + * Available models + * + * @var array + * @access public + */ + var $models = array(); +/** + * Override intialize of the Shell + * + * @access public + */ + function initialize() { + require_once CAKE . 'dispatcher.php'; + $this->Dispatcher = new Dispatcher(); + $this->models = Configure::listObjects('model'); + App::import('Model', $this->models); + + foreach ($this->models as $model) { + $class = Inflector::camelize(r('.php', '', $model)); + $this->models[$model] = $class; + $this->{$class} =& new $class(); + } + $this->out('Model classes:'); + $this->out('--------------'); + + foreach ($this->models as $model) { + $this->out(" - {$model}"); + } + $this->__loadRoutes(); + } +/** + * Prints the help message + * + * @access public + */ + function help() { + $this->main('help'); + } +/** + * Override main() to handle action + * + * @access public + */ + function main($command = null) { + while (true) { + if (empty($command)) { + $command = trim($this->in('')); + } + + switch ($command) { + case 'help': + $this->out('Console help:'); + $this->out('-------------'); + $this->out('The interactive console is a tool for testing parts of your app before you commit code'); + $this->out(''); + $this->out('Model testing:'); + $this->out('To test model results, use the name of your model without a leading $'); + $this->out('e.g. Foo->find("all")'); + $this->out(''); + $this->out('To dynamically set associations, you can do the following:'); + $this->out("\tModelA bind <association> ModelB"); + $this->out("where the supported assocations are hasOne, hasMany, belongsTo, hasAndBelongsToMany"); + $this->out(''); + $this->out('To dynamically remove associations, you can do the following:'); + $this->out("\t ModelA unbind <association> ModelB"); + $this->out("where the supported associations are the same as above"); + $this->out(''); + $this->out("To save a new field in a model, you can do the following:"); + $this->out("\tModelA->save(array('foo' => 'bar', 'baz' => 0))"); + $this->out("where you are passing a hash of data to be saved in the format"); + $this->out("of field => value pairs"); + $this->out(''); + $this->out("To get column information for a model, use the following:"); + $this->out("\tModelA columns"); + $this->out("which returns a list of columns and their type"); + $this->out(''); + $this->out('Route testing:'); + $this->out('To test URLs against your app\'s route configuration, type:'); + $this->out("\tRoute <url>"); + $this->out("where url is the path to your your action plus any query parameters, minus the"); + $this->out("application's base path"); + $this->out(''); + $this->out('To reload your routes config (config/routes.php), do the following:'); + $this->out("\tRoutes reload"); + $this->out(''); + $this->out(''); + $this->out('To show all connected routes, do the following:'); + $this->out("\tRoutes show"); + $this->out(''); + break; + case 'quit': + case 'exit': + return true; + break; + case 'models': + $this->out('Model classes:'); + $this->hr(); + foreach ($this->models as $model) { + $this->out(" - {$model}"); + } + break; + case (preg_match("/^(\w+) bind (\w+) (\w+)/", $command, $tmp) == true): + foreach ($tmp as $data) { + $data = strip_tags($data); + $data = str_replace($this->badCommandChars, "", $data); + } + + $modelA = $tmp[1]; + $association = $tmp[2]; + $modelB = $tmp[3]; + + if ($this->__isValidModel($modelA) && $this->__isValidModel($modelB) && in_array($association, $this->associations)) { + $this->{$modelA}->bindModel(array($association => array($modelB => array('className' => $modelB))), false); + $this->out("Created $association association between $modelA and $modelB"); + } else { + $this->out("Please verify you are using valid models and association types"); + } + break; + case (preg_match("/^(\w+) unbind (\w+) (\w+)/", $command, $tmp) == true): + foreach ($tmp as $data) { + $data = strip_tags($data); + $data = str_replace($this->badCommandChars, "", $data); + } + + $modelA = $tmp[1]; + $association = $tmp[2]; + $modelB = $tmp[3]; + + // Verify that there is actually an association to unbind + $currentAssociations = $this->{$modelA}->getAssociated(); + $validCurrentAssociation = false; + + foreach ($currentAssociations as $model => $currentAssociation) { + if ($model == $modelB && $association == $currentAssociation) { + $validCurrentAssociation = true; + } + } + + if ($this->__isValidModel($modelA) && $this->__isValidModel($modelB) && in_array($association, $this->associations) && $validCurrentAssociation) { + $this->{$modelA}->unbindModel(array($association => array($modelB))); + $this->out("Removed $association association between $modelA and $modelB"); + } else { + $this->out("Please verify you are using valid models, valid current association, and valid association types"); + } + break; + case (strpos($command, "->find") > 0): + // Remove any bad info + $command = strip_tags($command); + $command = str_replace($this->badCommandChars, "", $command); + + // Do we have a valid model? + list($modelToCheck, $tmp) = explode('->', $command); + + if ($this->__isValidModel($modelToCheck)) { + $findCommand = "\$data = \$this->$command;"; + @eval($findCommand); + + if (is_array($data)) { + foreach ($data as $idx => $results) { + if (is_numeric($idx)) { // findAll() output + foreach ($results as $modelName => $result) { + $this->out("$modelName"); + + foreach ($result as $field => $value) { + if (is_array($value)) { + foreach ($value as $field2 => $value2) { + $this->out("\t$field2: $value2"); + } + + $this->out(""); + } else { + $this->out("\t$field: $value"); + } + } + } + } else { // find() output + $this->out($idx); + + foreach ($results as $field => $value) { + if (is_array($value)) { + foreach ($value as $field2 => $value2) { + $this->out("\t$field2: $value2"); + } + + $this->out(""); + } else { + $this->out("\t$field: $value"); + } + } + } + } + } else { + $this->out("\nNo result set found"); + } + } else { + $this->out("$modelToCheck is not a valid model"); + } + + break; + case (strpos($command, '->save') > 0): + // Validate the model we're trying to save here + $command = strip_tags($command); + $command = str_replace($this->badCommandChars, "", $command); + list($modelToSave, $tmp) = explode("->", $command); + + if ($this->__isValidModel($modelToSave)) { + // Extract the array of data we are trying to build + list($foo, $data) = explode("->save", $command); + $data = preg_replace('/^\(*(array)?\(*(.+?)\)*$/i', '\\2', $data); + $saveCommand = "\$this->{$modelToSave}->save(array('{$modelToSave}' => array({$data})));"; + @eval($saveCommand); + $this->out('Saved record for ' . $modelToSave); + } + break; + case (preg_match("/^(\w+) columns/", $command, $tmp) == true): + $modelToCheck = strip_tags(str_replace($this->badCommandChars, "", $tmp[1])); + + if ($this->__isValidModel($modelToCheck)) { + // Get the column info for this model + $fieldsCommand = "\$data = \$this->{$modelToCheck}->getColumnTypes();"; + @eval($fieldsCommand); + + if (is_array($data)) { + foreach ($data as $field => $type) { + $this->out("\t{$field}: {$type}"); + } + } + } else { + $this->out("Please verify that you selected a valid model"); + } + break; + case (preg_match("/^routes\s+reload/i", $command, $tmp) == true): + $router =& Router::getInstance(); + if (!$this->__loadRoutes()) { + $this->out("There was an error loading the routes config. Please check that the file"); + $this->out("exists and is free of parse errors."); + break; + } + $this->out("Routes configuration reloaded, " . count($router->routes) . " routes connected"); + break; + case (preg_match("/^routes\s+show/i", $command, $tmp) == true): + $router =& Router::getInstance(); + $this->out(join("\n", Set::extract($router->routes, '{n}.0'))); + break; + case (preg_match("/^route\s+(.*)/i", $command, $tmp) == true): + $this->out(var_export(Router::parse($tmp[1]), true)); + break; + default: + $this->out("Invalid command\n"); + break; + } + $command = ''; + } + } +/** + * Tells if the specified model is included in the list of available models + * + * @param string $modelToCheck + * @return boolean true if is an available model, false otherwise + * @access private + */ + function __isValidModel($modelToCheck) { + return in_array($modelToCheck, $this->models); + } +/** + * Reloads the routes configuration from config/routes.php, and compiles + * all routes found + * + * @return boolean True if config reload was a success, otherwise false + * @access private + */ + function __loadRoutes() { + $router =& Router::getInstance(); + + $router->reload(); + extract($router->getNamedExpressions()); + + if (!@include(CONFIGS . 'routes.php')) { + return false; + } + $router->parse('/'); + + foreach (array_keys($router->getNamedExpressions()) as $var) { + unset(${$var}); + } + for ($i = 0; $i < count($router->routes); $i++) { + $router->compile($i); + } + return true; + } +} +?> diff --git a/cake/console/libs/i18n.php b/cake/console/libs/i18n.php new file mode 100755 index 0000000..f0e23b9 --- /dev/null +++ b/cake/console/libs/i18n.php @@ -0,0 +1,129 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Short description for file. + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.console.libs + * @since CakePHP(tm) v 1.2.0.5669 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Shell for I18N management. + * + * @package cake + * @subpackage cake.cake.console.libs + */ +class I18nShell extends Shell { +/** + * Contains database source to use + * + * @var string + * @access public + */ + var $dataSource = 'default'; +/** + * Contains tasks to load and instantiate + * + * @var array + * @access public + */ + var $tasks = array('DbConfig', 'Extract'); +/** + * Override startup of the Shell + * + * @access public + */ + function startup() { + $this->_welcome(); + if (isset($this->params['datasource'])) { + $this->dataSource = $this->params['datasource']; + } + + if ($this->command && !in_array($this->command, array('help'))) { + if (!config('database')) { + $this->out(__('Your database configuration was not found. Take a moment to create one.', true), true); + return $this->DbConfig->execute(); + } + } + } +/** + * Override main() for help message hook + * + * @access public + */ + function main() { + $this->out(__('I18n Shell', true)); + $this->hr(); + $this->out(__('[E]xtract POT file from sources', true)); + $this->out(__('[I]nitialize i18n database table', true)); + $this->out(__('[H]elp', true)); + $this->out(__('[Q]uit', true)); + + $choice = strtolower($this->in(__('What would you like to do?', true), array('E', 'I', 'H', 'Q'))); + switch ($choice) { + case 'e': + $this->Extract->execute(); + break; + case 'i': + $this->initdb(); + break; + case 'h': + $this->help(); + break; + case 'q': + exit(0); + break; + default: + $this->out(__('You have made an invalid selection. Please choose a command to execute by entering E, I, H, or Q.', true)); + } + $this->hr(); + $this->main(); + } +/** + * Initialize I18N database. + * + * @access public + */ + function initdb() { + $this->Dispatch->args = array('schema', 'run', 'create', 'i18n'); + $this->Dispatch->dispatch(); + } +/** + * Show help screen. + * + * @access public + */ + function help() { + $this->hr(); + $this->out(__('I18n Shell:', true)); + $this->hr(); + $this->out(__('I18n Shell initializes i18n database table for your application', true)); + $this->out(__('and generates .pot file(s) with translations.', true)); + $this->hr(); + $this->out(__('usage:', true)); + $this->out(' cake i18n help'); + $this->out(' cake i18n initdb [-datasource custom]'); + $this->out(''); + $this->hr(); + + $this->Extract->help(); + } +} +?> \ No newline at end of file diff --git a/cake/console/libs/schema.php b/cake/console/libs/schema.php new file mode 100755 index 0000000..36974eb --- /dev/null +++ b/cake/console/libs/schema.php @@ -0,0 +1,430 @@ +<?php +/** + * Command-line database management utility to automate programmer chores. + * + * Schema is CakePHP's database management utility. This helps you maintain versions of + * of your database. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2009, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2009, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.console.libs + * @since CakePHP(tm) v 1.2.0.5550 + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +App::import('File'); +App::import('Model', 'Schema'); +/** + * Schema is a command-line database management utility for automating programmer chores. + * + * @package cake + * @subpackage cake.cake.console.libs + * @link http://book.cakephp.org/view/734/Schema-management-and-migrations + */ +class SchemaShell extends Shell { +/** + * is this a dry run? + * + * @var boolean + * @access private + */ + var $__dry = null; +/** + * Override initialize + * + * @access public + */ + function initialize() { + $this->_welcome(); + $this->out('Cake Schema Shell'); + $this->hr(); + } +/** + * Override startup + * + * @access public + */ + function startup() { + $name = null; + if (!empty($this->params['name'])) { + $name = $this->params['name']; + $this->params['file'] = Inflector::underscore($name); + } + + $path = null; + if (!empty($this->params['path'])) { + $path = $this->params['path']; + } + + $file = null; + if (empty($this->params['file'])) { + $this->params['file'] = 'schema.php'; + } + if (strpos($this->params['file'], '.php') === false) { + $this->params['file'] .= '.php'; + } + $file = $this->params['file']; + + $connection = null; + if (!empty($this->params['connection'])) { + $connection = $this->params['connection']; + } + + $this->Schema =& new CakeSchema(compact('name', 'path', 'file', 'connection')); + } +/** + * Override main + * + * @access public + */ + function main() { + $this->help(); + } +/** + * Read and output contents of schema object + * path to read as second arg + * + * @access public + */ + function view() { + $File = new File($this->Schema->path . DS . $this->params['file']); + if ($File->exists()) { + $this->out($File->read()); + $this->_stop(); + } else { + $this->err(__('Schema could not be found', true)); + $this->_stop(); + } + } +/** + * Read database and Write schema object + * accepts a connection as first arg or path to save as second arg + * + * @access public + */ + function generate() { + $this->out(__('Generating Schema...', true)); + $options = array(); + if (isset($this->params['f'])) { + $options = array('models' => false); + } + + $snapshot = false; + if (isset($this->args[0]) && $this->args[0] === 'snapshot') { + $snapshot = true; + } + + if (!$snapshot && file_exists($this->Schema->path . DS . $this->params['file'])) { + $snapshot = true; + $result = strtolower($this->in("Schema file exists.\n [O]verwrite\n [S]napshot\n [Q]uit\nWould you like to do?", array('o', 's', 'q'), 's')); + if ($result === 'q') { + return $this->_stop(); + } + if ($result === 'o') { + $snapshot = false; + } + } + + $content = $this->Schema->read($options); + $content['file'] = $this->params['file']; + + if ($snapshot === true) { + $Folder =& new Folder($this->Schema->path); + $result = $Folder->read(); + + $numToUse = false; + if (isset($this->params['s'])) { + $numToUse = $this->params['s']; + } + + $count = 1; + if (!empty($result[1])) { + foreach ($result[1] as $file) { + if (preg_match('/schema(?:[_\d]*)?\.php$/', $file)) { + $count++; + } + } + } + + if ($numToUse !== false) { + if ($numToUse > $count) { + $count = $numToUse; + } + } + + $fileName = rtrim($this->params['file'], '.php'); + $content['file'] = $fileName . '_' . $count . '.php'; + } + + if ($this->Schema->write($content)) { + $this->out(sprintf(__('Schema file: %s generated', true), $content['file'])); + $this->_stop(); + } else { + $this->err(__('Schema file: %s generated', true)); + $this->_stop(); + } + } +/** + * Dump Schema object to sql file + * if first arg == write, file will be written to sql file + * or it will output sql + * + * @access public + */ + function dump() { + $write = false; + $Schema = $this->Schema->load(); + if (!$Schema) { + $this->err(__('Schema could not be loaded', true)); + $this->_stop(); + } + if (!empty($this->args[0])) { + if ($this->args[0] == 'write') { + $write = Inflector::underscore($this->Schema->name); + } else { + $write = $this->args[0]; + } + } + $db =& ConnectionManager::getDataSource($this->Schema->connection); + $contents = "#" . $Schema->name . " sql generated on: " . date('Y-m-d H:i:s') . " : " . time() . "\n\n"; + $contents .= $db->dropSchema($Schema) . "\n\n". $db->createSchema($Schema); + if ($write) { + if (strpos($write, '.sql') === false) { + $write .= '.sql'; + } + $File = new File($this->Schema->path . DS . $write, true); + if ($File->write($contents)) { + $this->out(sprintf(__('SQL dump file created in %s', true), $File->pwd())); + $this->_stop(); + } else { + $this->err(__('SQL dump could not be created', true)); + $this->_stop(); + } + } + $this->out($contents); + return $contents; + } +/** + * Run database commands: create, update + * + * @access public + */ + function run() { + if (!isset($this->args[0])) { + $this->err(__('Command not found', true)); + $this->_stop(); + } + + $command = $this->args[0]; + + $this->Dispatch->shiftArgs(); + + $name = null; + if (isset($this->args[0])) { + $name = $this->args[0]; + } + if (isset($this->params['name'])) { + $name = $this->params['name']; + } + + if (isset($this->params['dry'])) { + $this->__dry = true; + $this->out(__('Performing a dry run.', true)); + } + + $options = array('name' => $name); + if (isset($this->params['s'])) { + $fileName = rtrim($this->Schema->file, '.php'); + $options['file'] = $fileName . '_' . $this->params['s'] . '.php'; + } + + $Schema = $this->Schema->load($options); + + if (!$Schema) { + $this->err(sprintf(__('%s could not be loaded', true), $this->Schema->file)); + $this->_stop(); + } + + $table = null; + if (isset($this->args[1])) { + $table = $this->args[1]; + } + + switch ($command) { + case 'create': + $this->__create($Schema, $table); + break; + case 'update': + $this->__update($Schema, $table); + break; + default: + $this->err(__('Command not found', true)); + $this->_stop(); + } + } +/** + * Create database from Schema object + * Should be called via the run method + * + * @access private + */ + function __create(&$Schema, $table = null) { + $db =& ConnectionManager::getDataSource($this->Schema->connection); + + $drop = $create = array(); + + if (!$table) { + foreach ($Schema->tables as $table => $fields) { + $drop[$table] = $db->dropSchema($Schema, $table); + $create[$table] = $db->createSchema($Schema, $table); + } + } elseif (isset($Schema->tables[$table])) { + $drop[$table] = $db->dropSchema($Schema, $table); + $create[$table] = $db->createSchema($Schema, $table); + } + if (empty($drop) || empty($create)) { + $this->out(__('Schema is up to date.', true)); + $this->_stop(); + } + + $this->out("\n" . __('The following table(s) will be dropped.', true)); + $this->out(array_keys($drop)); + + if ('y' == $this->in(__('Are you sure you want to drop the table(s)?', true), array('y', 'n'), 'n')) { + $this->out(__('Dropping table(s).', true)); + $this->__run($drop, 'drop', $Schema); + } + + $this->out("\n" . __('The following table(s) will be created.', true)); + $this->out(array_keys($create)); + + if ('y' == $this->in(__('Are you sure you want to create the table(s)?', true), array('y', 'n'), 'y')) { + $this->out(__('Creating table(s).', true)); + $this->__run($create, 'create', $Schema); + } + + $this->out(__('End create.', true)); + } +/** + * Update database with Schema object + * Should be called via the run method + * + * @access private + */ + function __update(&$Schema, $table = null) { + $db =& ConnectionManager::getDataSource($this->Schema->connection); + + $this->out(__('Comparing Database to Schema...', true)); + $options = array(); + if (isset($this->params['f'])) { + $options['models'] = false; + } + $Old = $this->Schema->read($options); + $compare = $this->Schema->compare($Old, $Schema); + + $contents = array(); + + if (empty($table)) { + foreach ($compare as $table => $changes) { + $contents[$table] = $db->alterSchema(array($table => $changes), $table); + } + } elseif (isset($compare[$table])) { + $contents[$table] = $db->alterSchema(array($table => $compare[$table]), $table); + } + + if (empty($contents)) { + $this->out(__('Schema is up to date.', true)); + $this->_stop(); + } + + $this->out("\n" . __('The following statements will run.', true)); + $this->out(array_map('trim', $contents)); + if ('y' == $this->in(__('Are you sure you want to alter the tables?', true), array('y', 'n'), 'n')) { + $this->out(''); + $this->out(__('Updating Database...', true)); + $this->__run($contents, 'update', $Schema); + } + + $this->out(__('End update.', true)); + } +/** + * Runs sql from __create() or __update() + * + * @access private + */ + function __run($contents, $event, &$Schema) { + if (empty($contents)) { + $this->err(__('Sql could not be run', true)); + return; + } + Configure::write('debug', 2); + $db =& ConnectionManager::getDataSource($this->Schema->connection); + $db->fullDebug = true; + + foreach ($contents as $table => $sql) { + if (empty($sql)) { + $this->out(sprintf(__('%s is up to date.', true), $table)); + } else { + if ($this->__dry === true) { + $this->out(sprintf(__('Dry run for %s :', true), $table)); + $this->out($sql); + } else { + if (!$Schema->before(array($event => $table))) { + return false; + } + $error = null; + if (!$db->execute($sql)) { + $error = $table . ': ' . $db->lastError(); + } + + $Schema->after(array($event => $table, 'errors' => $error)); + + if (!empty($error)) { + $this->out($error); + } else { + $this->out(sprintf(__('%s updated.', true), $table)); + } + } + } + } + } +/** + * Displays help contents + * + * @access public + */ + function help() { + $this->out("The Schema Shell generates a schema object from"); + $this->out("the database and updates the database from the schema."); + $this->hr(); + $this->out("Usage: cake schema <command> <arg1> <arg2>..."); + $this->hr(); + $this->out('Params:'); + $this->out("\n\t-connection <config>\n\t\tset db config <config>. uses 'default' if none is specified"); + $this->out("\n\t-path <dir>\n\t\tpath <dir> to read and write schema.php.\n\t\tdefault path: ". $this->Schema->path); + $this->out("\n\t-name <name>\n\t\tclassname to use."); + $this->out("\n\t-file <name>\n\t\tfile <name> to read and write.\n\t\tdefault file: ". $this->Schema->file); + $this->out("\n\t-s <number>\n\t\tsnapshot <number> to use for run."); + $this->out("\n\t-dry\n\t\tPerform a dry run on 'run' commands.\n\t\tQueries will be output to window instead of executed."); + $this->out("\n\t-f\n\t\tforce 'generate' to create a new schema."); + $this->out('Commands:'); + $this->out("\n\tschema help\n\t\tshows this help message."); + $this->out("\n\tschema view\n\t\tread and output contents of schema file"); + $this->out("\n\tschema generate\n\t\treads from 'connection' writes to 'path'\n\t\tTo force generation of all tables into the schema, use the -f param.\n\t\tUse 'schema generate snapshot <number>' to generate snapshots\n\t\twhich you can use with the -s parameter in the other operations."); + $this->out("\n\tschema dump <filename>\n\t\tDump database sql based on schema file to <filename>. \n\t\tIf <filename> is write, schema dump will be written to a file\n\t\tthat has the same name as the app directory."); + $this->out("\n\tschema run create <schema> <table>\n\t\tDrop and create tables based on schema file\n\t\toptional <schema> arg for selecting schema name\n\t\toptional <table> arg for creating only one table\n\t\tpass the -s param with a number to use a snapshot\n\t\tTo see the changes, perform a dry run with the -dry param"); + $this->out("\n\tschema run update <schema> <table>\n\t\talter tables based on schema file\n\t\toptional <schema> arg for selecting schema name.\n\t\toptional <table> arg for altering only one table.\n\t\tTo use a snapshot, pass the -s param with the snapshot number\n\t\tTo see the changes, perform a dry run with the -dry param"); + $this->out(""); + $this->_stop(); + } +} +?> \ No newline at end of file diff --git a/cake/console/libs/shell.php b/cake/console/libs/shell.php new file mode 100755 index 0000000..eed6bff --- /dev/null +++ b/cake/console/libs/shell.php @@ -0,0 +1,615 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Base class for Shells + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.console.libs + * @since CakePHP(tm) v 1.2.0.5012 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Base class for command-line utilities for automating programmer chores. + * + * @package cake + * @subpackage cake.cake.console.libs + */ +class Shell extends Object { +/** + * An instance of the ShellDispatcher object that loaded this script + * + * @var ShellDispatcher + * @access public + */ + var $Dispatch = null; +/** + * If true, the script will ask for permission to perform actions. + * + * @var boolean + * @access public + */ + var $interactive = true; +/** + * Holds the DATABASE_CONFIG object for the app. Null if database.php could not be found, + * or the app does not exist. + * + * @var DATABASE_CONFIG + * @access public + */ + var $DbConfig = null; +/** + * Contains command switches parsed from the command line. + * + * @var array + * @access public + */ + var $params = array(); +/** + * Contains arguments parsed from the command line. + * + * @var array + * @access public + */ + var $args = array(); +/** + * The file name of the shell that was invoked. + * + * @var string + * @access public + */ + var $shell = null; +/** + * The class name of the shell that was invoked. + * + * @var string + * @access public + */ + var $className = null; +/** + * The command called if public methods are available. + * + * @var string + * @access public + */ + var $command = null; +/** + * The name of the shell in camelized. + * + * @var string + * @access public + */ + var $name = null; +/** + * An alias for the shell + * + * @var string + * @access public + */ + var $alias = null; +/** + * Contains tasks to load and instantiate + * + * @var array + * @access public + */ + var $tasks = array(); +/** + * Contains the loaded tasks + * + * @var array + * @access public + */ + var $taskNames = array(); +/** + * Contains models to load and instantiate + * + * @var array + * @access public + */ + var $uses = array(); +/** + * Constructs this Shell instance. + * + */ + function __construct(&$dispatch) { + $vars = array('params', 'args', 'shell', 'shellCommand' => 'command'); + foreach ($vars as $key => $var) { + if (is_string($key)) { + $this->{$var} =& $dispatch->{$key}; + } else { + $this->{$var} =& $dispatch->{$var}; + } + } + + if ($this->name == null) { + $this->name = get_class($this); + } + + if ($this->alias == null) { + $this->alias = $this->name; + } + + ClassRegistry::addObject($this->name, $this); + ClassRegistry::map($this->name, $this->alias); + + if (!PHP5 && isset($this->args[0])) { + if (strpos($this->name, strtolower(Inflector::camelize($this->args[0]))) !== false) { + $dispatch->shiftArgs(); + } + if (strtolower($this->command) == strtolower(Inflector::variable($this->args[0])) && method_exists($this, $this->command)) { + $dispatch->shiftArgs(); + } + } + + $this->Dispatch =& $dispatch; + } +/** + * Initializes the Shell + * acts as constructor for subclasses + * allows configuration of tasks prior to shell execution + * + * @access public + */ + function initialize() { + $this->_loadModels(); + } +/** + * Starts up the the Shell + * allows for checking and configuring prior to command or main execution + * can be overriden in subclasses + * + * @access public + */ + function startup() { + $this->_welcome(); + } +/** + * Displays a header for the shell + * + * @access protected + */ + function _welcome() { + $this->out("\nWelcome to CakePHP v" . Configure::version() . " Console"); + $this->out("---------------------------------------------------------------"); + $this->out('App : '. $this->params['app']); + $this->out('Path: '. $this->params['working']); + $this->hr(); + } +/** + * Loads database file and constructs DATABASE_CONFIG class + * makes $this->DbConfig available to subclasses + * + * @return bool + * @access protected + */ + function _loadDbConfig() { + if (config('database') && class_exists('DATABASE_CONFIG')) { + $this->DbConfig =& new DATABASE_CONFIG(); + return true; + } + $this->err('Database config could not be loaded'); + $this->out('Run \'bake\' to create the database configuration'); + return false; + } +/** + * if var $uses = true + * Loads AppModel file and constructs AppModel class + * makes $this->AppModel available to subclasses + * if var $uses is an array of models will load those models + * + * @return bool + * @access protected + */ + function _loadModels() { + if ($this->uses === null || $this->uses === false) { + return; + } + + if ($this->uses === true && App::import('Model', 'AppModel')) { + $this->AppModel =& new AppModel(false, false, false); + return true; + } + + if ($this->uses !== true && !empty($this->uses)) { + $uses = is_array($this->uses) ? $this->uses : array($this->uses); + + $modelClassName = $uses[0]; + if (strpos($uses[0], '.') !== false) { + list($plugin, $modelClassName) = explode('.', $uses[0]); + } + $this->modelClass = $modelClassName; + + foreach ($uses as $modelClass) { + $plugin = null; + if (strpos($modelClass, '.') !== false) { + list($plugin, $modelClass) = explode('.', $modelClass); + $plugin = $plugin . '.'; + } + if (PHP5) { + $this->{$modelClass} = ClassRegistry::init($plugin . $modelClass); + } else { + $this->{$modelClass} =& ClassRegistry::init($plugin . $modelClass); + } + } + return true; + } + return false; + } +/** + * Loads tasks defined in var $tasks + * + * @return bool + * @access public + */ + function loadTasks() { + if ($this->tasks === null || $this->tasks === false || $this->tasks === true || empty($this->tasks)) { + return true; + } + + $tasks = $this->tasks; + if (!is_array($tasks)) { + $tasks = array($tasks); + } + + foreach ($tasks as $taskName) { + $task = Inflector::underscore($taskName); + $taskClass = Inflector::camelize($taskName . 'Task'); + + if (!class_exists($taskClass)) { + foreach ($this->Dispatch->shellPaths as $path) { + $taskPath = $path . 'tasks' . DS . $task.'.php'; + if (file_exists($taskPath)) { + require_once $taskPath; + break; + } + } + } + if (ClassRegistry::isKeySet($taskClass)) { + $this->taskNames[] = $taskName; + if (!PHP5) { + $this->{$taskName} =& ClassRegistry::getObject($taskClass); + } else { + $this->{$taskName} = ClassRegistry::getObject($taskClass); + } + } else { + $this->taskNames[] = $taskName; + if (!PHP5) { + $this->{$taskName} =& new $taskClass($this->Dispatch); + } else { + $this->{$taskName} = new $taskClass($this->Dispatch); + } + } + + if (!isset($this->{$taskName})) { + $this->err("Task '" . $taskName . "' could not be loaded"); + $this->_stop(); + } + } + + return true; + } +/** + * Prompts the user for input, and returns it. + * + * @param string $prompt Prompt text. + * @param mixed $options Array or string of options. + * @param string $default Default input value. + * @return Either the default value, or the user-provided input. + * @access public + */ + function in($prompt, $options = null, $default = null) { + if (!$this->interactive) { + return $default; + } + $in = $this->Dispatch->getInput($prompt, $options, $default); + + if ($options && is_string($options)) { + if (strpos($options, ',')) { + $options = explode(',', $options); + } elseif (strpos($options, '/')) { + $options = explode('/', $options); + } else { + $options = array($options); + } + } + if (is_array($options)) { + while ($in == '' || ($in && (!in_array(strtolower($in), $options) && !in_array(strtoupper($in), $options)) && !in_array($in, $options))) { + $in = $this->Dispatch->getInput($prompt, $options, $default); + } + } + if ($in) { + return $in; + } + } +/** + * Outputs to the stdout filehandle. + * + * @param string $string String to output. + * @param boolean $newline If true, the outputs gets an added newline. + * @access public + */ + function out($string, $newline = true) { + if (is_array($string)) { + $str = ''; + foreach ($string as $message) { + $str .= $message ."\n"; + } + $string = $str; + } + return $this->Dispatch->stdout($string, $newline); + } +/** + * Outputs to the stderr filehandle. + * + * @param string $string Error text to output. + * @access public + */ + function err($string) { + if (is_array($string)) { + $str = ''; + foreach ($string as $message) { + $str .= $message ."\n"; + } + $string = $str; + } + return $this->Dispatch->stderr($string."\n"); + } +/** + * Outputs a series of minus characters to the standard output, acts as a visual separator. + * + * @param boolean $newline If true, the outputs gets an added newline. + * @access public + */ + function hr($newline = false) { + if ($newline) { + $this->out("\n"); + } + $this->out('---------------------------------------------------------------'); + if ($newline) { + $this->out("\n"); + } + } +/** + * Displays a formatted error message and exits the application + * + * @param string $title Title of the error message + * @param string $msg Error message + * @access public + */ + function error($title, $msg) { + $out = "$title\n"; + $out .= "$msg\n"; + $out .= "\n"; + $this->err($out); + $this->_stop(); + } +/** + * Will check the number args matches otherwise throw an error + * + * @param integer $expectedNum Expected number of paramters + * @param string $command Command + * @access protected + */ + function _checkArgs($expectedNum, $command = null) { + if (!$command) { + $command = $this->command; + } + if (count($this->args) < $expectedNum) { + $this->error("Wrong number of parameters: ".count($this->args), "Expected: {$expectedNum}\nPlease type 'cake {$this->shell} help' for help on usage of the {$this->name} {$command}"); + } + } +/** + * Creates a file at given path + * + * @param string $path Where to put the file. + * @param string $contents Content to put in the file. + * @return boolean Success + * @access public + */ + function createFile ($path, $contents) { + $path = str_replace(DS . DS, DS, $path); + $this->out("\n" . sprintf(__("Creating file %s", true), $path)); + if (is_file($path) && $this->interactive === true) { + $key = $this->in(__("File exists, overwrite?", true). " {$path}", array('y', 'n', 'q'), 'n'); + if (strtolower($key) == 'q') { + $this->out(__("Quitting.", true) ."\n"); + exit; + } elseif (strtolower($key) != 'y') { + $this->out(__("Skip", true) ." {$path}\n"); + return false; + } + } + if (!class_exists('File')) { + uses('file'); + } + + if ($File = new File($path, true)) { + $data = $File->prepare($contents); + $File->write($data); + $this->out(__("Wrote", true) ." {$path}"); + return true; + } else { + $this->err(__("Error! Could not write to", true)." {$path}.\n"); + return false; + } + } +/** + * Outputs usage text on the standard output. Implement it in subclasses. + * + * @access public + */ + function help() { + if ($this->command != null) { + $this->err("Unknown {$this->name} command '$this->command'.\nFor usage, try 'cake {$this->shell} help'.\n\n"); + } else { + $this->Dispatch->help(); + } + } +/** + * Action to create a Unit Test + * + * @return boolean Success + * @access protected + */ + function _checkUnitTest() { + if (App::import('vendor', 'simpletest' . DS . 'simpletest')) { + return true; + } + $unitTest = $this->in('SimpleTest is not installed. Do you want to bake unit test files anyway?', array('y','n'), 'y'); + $result = strtolower($unitTest) == 'y' || strtolower($unitTest) == 'yes'; + + if ($result) { + $this->out("\nYou can download SimpleTest from http://simpletest.org", true); + } + return $result; + } +/** + * Makes absolute file path easier to read + * + * @param string $file Absolute file path + * @return sting short path + * @access public + */ + function shortPath($file) { + $shortPath = str_replace(ROOT, null, $file); + $shortPath = str_replace('..' . DS, '', $shortPath); + return str_replace(DS . DS, DS, $shortPath); + } +/** + * Checks for Configure::read('Routing.admin') and forces user to input it if not enabled + * + * @return string Admin route to use + * @access public + */ + function getAdmin() { + $admin = ''; + $cakeAdmin = null; + $adminRoute = Configure::read('Routing.admin'); + if (!empty($adminRoute)) { + $cakeAdmin = $adminRoute . '_'; + } else { + $this->out('You need to enable Configure::write(\'Routing.admin\',\'admin\') in /app/config/core.php to use admin routing.'); + $this->out('What would you like the admin route to be?'); + $this->out('Example: www.example.com/admin/controller'); + while ($admin == '') { + $admin = $this->in("What would you like the admin route to be?", null, 'admin'); + } + if ($this->Project->cakeAdmin($admin) !== true) { + $this->out('Unable to write to /app/config/core.php.'); + $this->out('You need to enable Configure::write(\'Routing.admin\',\'admin\') in /app/config/core.php to use admin routing.'); + $this->_stop(); + } else { + $cakeAdmin = $admin . '_'; + } + } + return $cakeAdmin; + } +/** + * Creates the proper controller path for the specified controller class name + * + * @param string $name Controller class name + * @return string Path to controller + * @access protected + */ + function _controllerPath($name) { + return strtolower(Inflector::underscore($name)); + } +/** + * Creates the proper controller plural name for the specified controller class name + * + * @param string $name Controller class name + * @return string Controller plural name + * @access protected + */ + function _controllerName($name) { + return Inflector::pluralize(Inflector::camelize($name)); + } +/** + * Creates the proper controller camelized name (singularized) for the specified name + * + * @param string $name Name + * @return string Camelized and singularized controller name + * @access protected + */ + function _modelName($name) { + return Inflector::camelize(Inflector::singularize($name)); + } +/** + * Creates the proper singular model key for associations + * + * @param string $name Controller class name + * @return string Singular model key + * @access protected + */ + function _modelKey($name) { + return Inflector::underscore(Inflector::singularize($name)).'_id'; + } +/** + * Creates the proper model name from a foreign key + * + * @param string $key Foreign key + * @return string Model name + * @access protected + */ + function _modelNameFromKey($key) { + $name = str_replace('_id', '',$key); + return Inflector::camelize($name); + } +/** + * creates the singular name for use in views. + * + * @param string $name + * @return string $name + * @access protected + */ + function _singularName($name) { + return Inflector::variable(Inflector::singularize($name)); + } +/** + * Creates the plural name for views + * + * @param string $name Name to use + * @return string Plural name for views + * @access protected + */ + function _pluralName($name) { + return Inflector::variable(Inflector::pluralize($name)); + } +/** + * Creates the singular human name used in views + * + * @param string $name Controller name + * @return string Singular human name + * @access protected + */ + function _singularHumanName($name) { + return Inflector::humanize(Inflector::underscore(Inflector::singularize($name))); + } +/** + * Creates the plural human name used in views + * + * @param string $name Controller name + * @return string Plural human name + * @access protected + */ + function _pluralHumanName($name) { + return Inflector::humanize(Inflector::underscore(Inflector::pluralize($name))); + } +} +?> \ No newline at end of file diff --git a/cake/console/libs/tasks/controller.php b/cake/console/libs/tasks/controller.php new file mode 100755 index 0000000..736522a --- /dev/null +++ b/cake/console/libs/tasks/controller.php @@ -0,0 +1,579 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * The ControllerTask handles creating and updating controller files. + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.console.libs.tasks + * @since CakePHP(tm) v 1.2 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Task class for creating and updating controller files. + * + * @package cake + * @subpackage cake.cake.console.libs.tasks + */ +class ControllerTask extends Shell { +/** + * Name of plugin + * + * @var string + * @access public + */ + var $plugin = null; +/** + * Tasks to be loaded by this Task + * + * @var array + * @access public + */ + var $tasks = array('Project'); +/** + * path to CONTROLLERS directory + * + * @var array + * @access public + */ + var $path = CONTROLLERS; +/** + * Override initialize + * + * @access public + */ + function initialize() { + } +/** + * Execution method always used for tasks + * + * @access public + */ + function execute() { + if (empty($this->args)) { + $this->__interactive(); + } + + if (isset($this->args[0])) { + $controller = Inflector::camelize($this->args[0]); + $actions = null; + if (isset($this->args[1]) && $this->args[1] == 'scaffold') { + $this->out('Baking scaffold for ' . $controller); + $actions = $this->bakeActions($controller); + } else { + $actions = 'scaffold'; + } + if ((isset($this->args[1]) && $this->args[1] == 'admin') || (isset($this->args[2]) && $this->args[2] == 'admin')) { + if ($admin = $this->getAdmin()) { + $this->out('Adding ' . Configure::read('Routing.admin') .' methods'); + if ($actions == 'scaffold') { + $actions = $this->bakeActions($controller, $admin); + } else { + $actions .= $this->bakeActions($controller, $admin); + } + } + } + if ($this->bake($controller, $actions)) { + if ($this->_checkUnitTest()) { + $this->bakeTest($controller); + } + } + } + } +/** + * Interactive + * + * @access private + */ + function __interactive($controllerName = false) { + if (!$controllerName) { + $this->interactive = true; + $this->hr(); + $this->out(sprintf("Bake Controller\nPath: %s", $this->path)); + $this->hr(); + $actions = ''; + $uses = array(); + $helpers = array(); + $components = array(); + $wannaUseSession = 'y'; + $wannaDoAdmin = 'n'; + $wannaUseScaffold = 'n'; + $wannaDoScaffolding = 'y'; + $controllerName = $this->getName(); + } + $this->hr(); + $this->out("Baking {$controllerName}Controller"); + $this->hr(); + + $controllerFile = strtolower(Inflector::underscore($controllerName)); + + $question[] = __("Would you like to build your controller interactively?", true); + if (file_exists($this->path . $controllerFile .'_controller.php')) { + $question[] = sprintf(__("Warning: Choosing no will overwrite the %sController.", true), $controllerName); + } + $doItInteractive = $this->in(join("\n", $question), array('y','n'), 'y'); + + if (strtolower($doItInteractive) == 'y' || strtolower($doItInteractive) == 'yes') { + $this->interactive = true; + + $wannaUseScaffold = $this->in(__("Would you like to use scaffolding?", true), array('y','n'), 'n'); + + if (strtolower($wannaUseScaffold) == 'n' || strtolower($wannaUseScaffold) == 'no') { + + $wannaDoScaffolding = $this->in(__("Would you like to include some basic class methods (index(), add(), view(), edit())?", true), array('y','n'), 'n'); + + if (strtolower($wannaDoScaffolding) == 'y' || strtolower($wannaDoScaffolding) == 'yes') { + $wannaDoAdmin = $this->in(__("Would you like to create the methods for admin routing?", true), array('y','n'), 'n'); + } + + $wannaDoHelpers = $this->in(__("Would you like this controller to use other helpers besides HtmlHelper and FormHelper?", true), array('y','n'), 'n'); + + if (strtolower($wannaDoHelpers) == 'y' || strtolower($wannaDoHelpers) == 'yes') { + $helpersList = $this->in(__("Please provide a comma separated list of the other helper names you'd like to use.\nExample: 'Ajax, Javascript, Time'", true)); + $helpersListTrimmed = str_replace(' ', '', $helpersList); + $helpers = explode(',', $helpersListTrimmed); + } + $wannaDoComponents = $this->in(__("Would you like this controller to use any components?", true), array('y','n'), 'n'); + + if (strtolower($wannaDoComponents) == 'y' || strtolower($wannaDoComponents) == 'yes') { + $componentsList = $this->in(__("Please provide a comma separated list of the component names you'd like to use.\nExample: 'Acl, Security, RequestHandler'", true)); + $componentsListTrimmed = str_replace(' ', '', $componentsList); + $components = explode(',', $componentsListTrimmed); + } + + $wannaUseSession = $this->in(__("Would you like to use Sessions?", true), array('y','n'), 'y'); + } else { + $wannaDoScaffolding = 'n'; + } + } else { + $wannaDoScaffolding = $this->in(__("Would you like to include some basic class methods (index(), add(), view(), edit())?", true), array('y','n'), 'y'); + + if (strtolower($wannaDoScaffolding) == 'y' || strtolower($wannaDoScaffolding) == 'yes') { + $wannaDoAdmin = $this->in(__("Would you like to create the methods for admin routing?", true), array('y','n'), 'y'); + } + } + $admin = false; + + if ((strtolower($wannaDoAdmin) == 'y' || strtolower($wannaDoAdmin) == 'yes')) { + $admin = $this->getAdmin(); + } + + if (strtolower($wannaDoScaffolding) == 'y' || strtolower($wannaDoScaffolding) == 'yes') { + $actions = $this->bakeActions($controllerName, null, in_array(strtolower($wannaUseSession), array('y', 'yes'))); + if ($admin) { + $actions .= $this->bakeActions($controllerName, $admin, in_array(strtolower($wannaUseSession), array('y', 'yes'))); + } + } + + if ($this->interactive === true) { + $this->out(''); + $this->hr(); + $this->out('The following controller will be created:'); + $this->hr(); + $this->out("Controller Name: $controllerName"); + + if (strtolower($wannaUseScaffold) == 'y' || strtolower($wannaUseScaffold) == 'yes') { + $this->out(" var \$scaffold;"); + $actions = 'scaffold'; + } + + if (count($helpers)) { + $this->out("Helpers: ", false); + + foreach ($helpers as $help) { + if ($help != $helpers[count($helpers) - 1]) { + $this->out(ucfirst($help) . ", ", false); + } else { + $this->out(ucfirst($help)); + } + } + } + + if (count($components)) { + $this->out("Components: ", false); + + foreach ($components as $comp) { + if ($comp != $components[count($components) - 1]) { + $this->out(ucfirst($comp) . ", ", false); + } else { + $this->out(ucfirst($comp)); + } + } + } + $this->hr(); + $looksGood = $this->in(__('Look okay?', true), array('y','n'), 'y'); + + if (strtolower($looksGood) == 'y' || strtolower($looksGood) == 'yes') { + $baked = $this->bake($controllerName, $actions, $helpers, $components, $uses); + if ($baked && $this->_checkUnitTest()) { + $this->bakeTest($controllerName); + } + } else { + $this->__interactive($controllerName); + } + } else { + $baked = $this->bake($controllerName, $actions, $helpers, $components, $uses); + if ($baked && $this->_checkUnitTest()) { + $this->bakeTest($controllerName); + } + } + } +/** + * Bake scaffold actions + * + * @param string $controllerName Controller name + * @param string $admin Admin route to use + * @param boolean $wannaUseSession Set to true to use sessions, false otherwise + * @return string Baked actions + * @access private + */ + function bakeActions($controllerName, $admin = null, $wannaUseSession = true) { + $currentModelName = $modelImport = $this->_modelName($controllerName); + if ($this->plugin) { + $modelImport = $this->plugin . '.' . $modelImport; + } + if (!App::import('Model', $modelImport)) { + $this->err(__('You must have a model for this class to build scaffold methods. Please try again.', true)); + exit; + } + $actions = null; + $modelObj =& new $currentModelName(); + $controllerPath = $this->_controllerPath($controllerName); + $pluralName = $this->_pluralName($currentModelName); + $singularName = Inflector::variable($currentModelName); + $singularHumanName = Inflector::humanize($currentModelName); + $pluralHumanName = Inflector::humanize($controllerName); + $actions .= "\n"; + $actions .= "\tfunction {$admin}index() {\n"; + $actions .= "\t\t\$this->{$currentModelName}->recursive = 0;\n"; + $actions .= "\t\t\$this->set('{$pluralName}', \$this->paginate());\n"; + $actions .= "\t}\n"; + $actions .= "\n"; + $actions .= "\tfunction {$admin}view(\$id = null) {\n"; + $actions .= "\t\tif (!\$id) {\n"; + if ($wannaUseSession) { + $actions .= "\t\t\t\$this->Session->setFlash(__('Invalid {$singularHumanName}.', true));\n"; + $actions .= "\t\t\t\$this->redirect(array('action'=>'index'));\n"; + } else { + $actions .= "\t\t\t\$this->flash(__('Invalid {$singularHumanName}', true), array('action'=>'index'));\n"; + } + $actions .= "\t\t}\n"; + $actions .= "\t\t\$this->set('" . $singularName . "', \$this->{$currentModelName}->read(null, \$id));\n"; + $actions .= "\t}\n"; + $actions .= "\n"; + + /* ADD ACTION */ + $compact = array(); + $actions .= "\tfunction {$admin}add() {\n"; + $actions .= "\t\tif (!empty(\$this->data)) {\n"; + $actions .= "\t\t\t\$this->{$currentModelName}->create();\n"; + $actions .= "\t\t\tif (\$this->{$currentModelName}->save(\$this->data)) {\n"; + if ($wannaUseSession) { + $actions .= "\t\t\t\t\$this->Session->setFlash(__('The " . $singularHumanName . " has been saved', true));\n"; + $actions .= "\t\t\t\t\$this->redirect(array('action'=>'index'));\n"; + } else { + $actions .= "\t\t\t\t\$this->flash(__('{$currentModelName} saved.', true), array('action'=>'index'));\n"; + } + $actions .= "\t\t\t} else {\n"; + if ($wannaUseSession) { + $actions .= "\t\t\t\t\$this->Session->setFlash(__('The {$singularHumanName} could not be saved. Please, try again.', true));\n"; + } + $actions .= "\t\t\t}\n"; + $actions .= "\t\t}\n"; + foreach ($modelObj->hasAndBelongsToMany as $associationName => $relation) { + if (!empty($associationName)) { + $habtmModelName = $this->_modelName($associationName); + $habtmSingularName = $this->_singularName($associationName); + $habtmPluralName = $this->_pluralName($associationName); + $actions .= "\t\t\${$habtmPluralName} = \$this->{$currentModelName}->{$habtmModelName}->find('list');\n"; + $compact[] = "'{$habtmPluralName}'"; + } + } + foreach ($modelObj->belongsTo as $associationName => $relation) { + if (!empty($associationName)) { + $belongsToModelName = $this->_modelName($associationName); + $belongsToPluralName = $this->_pluralName($associationName); + $actions .= "\t\t\${$belongsToPluralName} = \$this->{$currentModelName}->{$belongsToModelName}->find('list');\n"; + $compact[] = "'{$belongsToPluralName}'"; + } + } + if (!empty($compact)) { + $actions .= "\t\t\$this->set(compact(" . join(', ', $compact) . "));\n"; + } + $actions .= "\t}\n"; + $actions .= "\n"; + + /* EDIT ACTION */ + $compact = array(); + $actions .= "\tfunction {$admin}edit(\$id = null) {\n"; + $actions .= "\t\tif (!\$id && empty(\$this->data)) {\n"; + if ($wannaUseSession) { + $actions .= "\t\t\t\$this->Session->setFlash(__('Invalid {$singularHumanName}', true));\n"; + $actions .= "\t\t\t\$this->redirect(array('action'=>'index'));\n"; + } else { + $actions .= "\t\t\t\$this->flash(__('Invalid {$singularHumanName}', true), array('action'=>'index'));\n"; + } + $actions .= "\t\t}\n"; + $actions .= "\t\tif (!empty(\$this->data)) {\n"; + $actions .= "\t\t\tif (\$this->{$currentModelName}->save(\$this->data)) {\n"; + if ($wannaUseSession) { + $actions .= "\t\t\t\t\$this->Session->setFlash(__('The " . $singularHumanName . " has been saved', true));\n"; + $actions .= "\t\t\t\t\$this->redirect(array('action'=>'index'));\n"; + } else { + $actions .= "\t\t\t\t\$this->flash(__('The " . $singularHumanName . " has been saved.', true), array('action'=>'index'));\n"; + } + $actions .= "\t\t\t} else {\n"; + if ($wannaUseSession) { + $actions .= "\t\t\t\t\$this->Session->setFlash(__('The {$singularHumanName} could not be saved. Please, try again.', true));\n"; + } + $actions .= "\t\t\t}\n"; + $actions .= "\t\t}\n"; + $actions .= "\t\tif (empty(\$this->data)) {\n"; + $actions .= "\t\t\t\$this->data = \$this->{$currentModelName}->read(null, \$id);\n"; + $actions .= "\t\t}\n"; + + foreach ($modelObj->hasAndBelongsToMany as $associationName => $relation) { + if (!empty($associationName)) { + $habtmModelName = $this->_modelName($associationName); + $habtmSingularName = $this->_singularName($associationName); + $habtmPluralName = $this->_pluralName($associationName); + $actions .= "\t\t\${$habtmPluralName} = \$this->{$currentModelName}->{$habtmModelName}->find('list');\n"; + $compact[] = "'{$habtmPluralName}'"; + } + } + foreach ($modelObj->belongsTo as $associationName => $relation) { + if (!empty($associationName)) { + $belongsToModelName = $this->_modelName($associationName); + $belongsToPluralName = $this->_pluralName($associationName); + $actions .= "\t\t\${$belongsToPluralName} = \$this->{$currentModelName}->{$belongsToModelName}->find('list');\n"; + $compact[] = "'{$belongsToPluralName}'"; + } + } + if (!empty($compact)) { + $actions .= "\t\t\$this->set(compact(" . join(',', $compact) . "));\n"; + } + $actions .= "\t}\n"; + $actions .= "\n"; + $actions .= "\tfunction {$admin}delete(\$id = null) {\n"; + $actions .= "\t\tif (!\$id) {\n"; + if ($wannaUseSession) { + $actions .= "\t\t\t\$this->Session->setFlash(__('Invalid id for {$singularHumanName}', true));\n"; + $actions .= "\t\t\t\$this->redirect(array('action'=>'index'));\n"; + } else { + $actions .= "\t\t\t\$this->flash(__('Invalid {$singularHumanName}', true), array('action'=>'index'));\n"; + } + $actions .= "\t\t}\n"; + $actions .= "\t\tif (\$this->{$currentModelName}->del(\$id)) {\n"; + if ($wannaUseSession) { + $actions .= "\t\t\t\$this->Session->setFlash(__('{$singularHumanName} deleted', true));\n"; + $actions .= "\t\t\t\$this->redirect(array('action'=>'index'));\n"; + } else { + $actions .= "\t\t\t\$this->flash(__('{$singularHumanName} deleted', true), array('action'=>'index'));\n"; + } + $actions .= "\t\t}\n"; + $actions .= "\t}\n"; + $actions .= "\n"; + return $actions; + } + + +/** + * Assembles and writes a Controller file + * + * @param string $controllerName Controller name + * @param string $actions Actions to add, or set the whole controller to use $scaffold (set $actions to 'scaffold') + * @param array $helpers Helpers to use in controller + * @param array $components Components to use in controller + * @param array $uses Models to use in controller + * @return string Baked controller + * @access private + */ + function bake($controllerName, $actions = '', $helpers = null, $components = null, $uses = null) { + $out = "<?php\n"; + $out .= "class $controllerName" . "Controller extends {$this->plugin}AppController {\n\n"; + $out .= "\tvar \$name = '$controllerName';\n"; + + if (strtolower($actions) == 'scaffold') { + $out .= "\tvar \$scaffold;\n"; + } else { + if (count($uses)) { + $out .= "\tvar \$uses = array('" . $this->_modelName($controllerName) . "', "; + + foreach ($uses as $use) { + if ($use != $uses[count($uses) - 1]) { + $out .= "'" . $this->_modelName($use) . "', "; + } else { + $out .= "'" . $this->_modelName($use) . "'"; + } + } + $out .= ");\n"; + } + + $out .= "\tvar \$helpers = array('Html', 'Form'"; + if (count($helpers)) { + foreach ($helpers as $help) { + $out .= ", '" . Inflector::camelize($help) . "'"; + } + } + $out .= ");\n"; + + if (count($components)) { + $out .= "\tvar \$components = array("; + + foreach ($components as $comp) { + if ($comp != $components[count($components) - 1]) { + $out .= "'" . Inflector::camelize($comp) . "', "; + } else { + $out .= "'" . Inflector::camelize($comp) . "'"; + } + } + $out .= ");\n"; + } + $out .= $actions; + } + $out .= "}\n"; + $out .= "?>"; + $filename = $this->path . $this->_controllerPath($controllerName) . '_controller.php'; + return $this->createFile($filename, $out); + } +/** + * Assembles and writes a unit test file + * + * @param string $className Controller class name + * @return string Baked test + * @access private + */ + function bakeTest($className) { + $import = $className; + if ($this->plugin) { + $import = $this->plugin . '.' . $className; + } + $out = "App::import('Controller', '$import');\n\n"; + $out .= "class Test{$className} extends {$className}Controller {\n"; + $out .= "\tvar \$autoRender = false;\n}\n\n"; + $out .= "class {$className}ControllerTest extends CakeTestCase {\n"; + $out .= "\tvar \${$className} = null;\n\n"; + $out .= "\tfunction startTest() {\n\t\t\$this->{$className} = new Test{$className}();"; + $out .= "\n\t\t\$this->{$className}->constructClasses();\n\t}\n\n"; + $out .= "\tfunction test{$className}ControllerInstance() {\n"; + $out .= "\t\t\$this->assertTrue(is_a(\$this->{$className}, '{$className}Controller'));\n\t}\n\n"; + $out .= "\tfunction endTest() {\n\t\tunset(\$this->{$className});\n\t}\n}\n"; + + $path = CONTROLLER_TESTS; + if (isset($this->plugin)) { + $pluginPath = 'plugins' . DS . Inflector::underscore($this->plugin) . DS; + $path = APP . $pluginPath . 'tests' . DS . 'cases' . DS . 'controllers' . DS; + } + + $filename = Inflector::underscore($className).'_controller.test.php'; + $this->out("\nBaking unit test for $className..."); + + $header = '$Id'; + $content = "<?php \n/* SVN FILE: $header$ */\n/* " . $className . "Controller Test cases generated on: " . date('Y-m-d H:i:s') . " : ". time() . "*/\n{$out}?>"; + return $this->createFile($path . $filename, $content); + } +/** + * Outputs and gets the list of possible models or controllers from database + * + * @param string $useDbConfig Database configuration name + * @return array Set of controllers + * @access public + */ + function listAll($useDbConfig = 'default') { + $db =& ConnectionManager::getDataSource($useDbConfig); + $usePrefix = empty($db->config['prefix']) ? '' : $db->config['prefix']; + if ($usePrefix) { + $tables = array(); + foreach ($db->listSources() as $table) { + if (!strncmp($table, $usePrefix, strlen($usePrefix))) { + $tables[] = substr($table, strlen($usePrefix)); + } + } + } else { + $tables = $db->listSources(); + } + + if (empty($tables)) { + $this->err(__('Your database does not have any tables.', true)); + $this->_stop(); + } + + $this->__tables = $tables; + $this->out('Possible Controllers based on your current database:'); + $this->_controllerNames = array(); + $count = count($tables); + for ($i = 0; $i < $count; $i++) { + $this->_controllerNames[] = $this->_controllerName($this->_modelName($tables[$i])); + $this->out($i + 1 . ". " . $this->_controllerNames[$i]); + } + return $this->_controllerNames; + } + +/** + * Forces the user to specify the controller he wants to bake, and returns the selected controller name. + * + * @return string Controller name + * @access public + */ + function getName() { + $useDbConfig = 'default'; + $controllers = $this->listAll($useDbConfig, 'Controllers'); + $enteredController = ''; + + while ($enteredController == '') { + $enteredController = $this->in(__("Enter a number from the list above, type in the name of another controller, or 'q' to exit", true), null, 'q'); + + if ($enteredController === 'q') { + $this->out(__("Exit", true)); + $this->_stop(); + } + + if ($enteredController == '' || intval($enteredController) > count($controllers)) { + $this->out(__('Error:', true)); + $this->out(__("The Controller name you supplied was empty, or the number \nyou selected was not an option. Please try again.", true)); + $enteredController = ''; + } + } + + if (intval($enteredController) > 0 && intval($enteredController) <= count($controllers) ) { + $controllerName = $controllers[intval($enteredController) - 1]; + } else { + $controllerName = Inflector::camelize($enteredController); + } + + return $controllerName; + } +/** + * Displays help contents + * + * @access public + */ + function help() { + $this->hr(); + $this->out("Usage: cake bake controller <arg1> <arg2>..."); + $this->hr(); + $this->out('Commands:'); + $this->out("\n\tcontroller <name>\n\t\tbakes controller with var \$scaffold"); + $this->out("\n\tcontroller <name> scaffold\n\t\tbakes controller with scaffold actions.\n\t\t(index, view, add, edit, delete)"); + $this->out("\n\tcontroller <name> scaffold admin\n\t\tbakes a controller with scaffold actions for both public and Configure::read('Routing.admin')"); + $this->out("\n\tcontroller <name> admin\n\t\tbakes a controller with scaffold actions only for Configure::read('Routing.admin')"); + $this->out(""); + $this->_stop(); + } +} +?> diff --git a/cake/console/libs/tasks/db_config.php b/cake/console/libs/tasks/db_config.php new file mode 100755 index 0000000..d2f0539 --- /dev/null +++ b/cake/console/libs/tasks/db_config.php @@ -0,0 +1,353 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * The DbConfig Task handles creating and updating the database.php + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.console.libs.tasks + * @since CakePHP(tm) v 1.2 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +if (!class_exists('File')) { + uses('file'); +} +/** + * Task class for creating and updating the database configuration file. + * + * @package cake + * @subpackage cake.cake.console.libs.tasks + */ +class DbConfigTask extends Shell { +/** + * path to CONFIG directory + * + * @var string + * @access public + */ + var $path = null; +/** + * Default configuration settings to use + * + * @var array + * @access private + */ + var $__defaultConfig = array( + 'name' => 'default', 'driver'=> 'mysql', 'persistent'=> 'false', 'host'=> 'localhost', + 'login'=> 'root', 'password'=> 'password', 'database'=> 'project_name', + 'schema'=> null, 'prefix'=> null, 'encoding' => null, 'port' => null + ); +/** + * initialization callback + * + * @var string + * @access public + */ + function initialize() { + $this->path = $this->params['working'] . DS . 'config' . DS; + } +/** + * Execution method always used for tasks + * + * @access public + */ + function execute() { + if (empty($this->args)) { + $this->__interactive(); + $this->_stop(); + } + } +/** + * Interactive interface + * + * @access private + */ + function __interactive() { + $this->hr(); + $this->out('Database Configuration:'); + $this->hr(); + $done = false; + $dbConfigs = array(); + + while ($done == false) { + $name = ''; + + while ($name == '') { + $name = $this->in("Name:", null, 'default'); + if (preg_match('/[^a-z0-9_]/i', $name)) { + $name = ''; + $this->out('The name may only contain unaccented latin characters, numbers or underscores'); + } + else if (preg_match('/^[^a-z_]/i', $name)) { + $name = ''; + $this->out('The name must start with an unaccented latin character or an underscore'); + } + } + $driver = ''; + + while ($driver == '') { + $driver = $this->in('Driver:', array('db2', 'firebird', 'mssql', 'mysql', 'mysqli', 'odbc', 'oracle', 'postgres', 'sqlite', 'sybase'), 'mysql'); + } + $persistent = ''; + + while ($persistent == '') { + $persistent = $this->in('Persistent Connection?', array('y', 'n'), 'n'); + } + + if (low($persistent) == 'n') { + $persistent = 'false'; + } else { + $persistent = 'true'; + } + $host = ''; + + while ($host == '') { + $host = $this->in('Database Host:', null, 'localhost'); + } + $port = ''; + + while ($port == '') { + $port = $this->in('Port?', null, 'n'); + } + + if (low($port) == 'n') { + $port = null; + } + $login = ''; + + while ($login == '') { + $login = $this->in('User:', null, 'root'); + } + $password = ''; + $blankPassword = false; + + while ($password == '' && $blankPassword == false) { + $password = $this->in('Password:'); + + if ($password == '') { + $blank = $this->in('The password you supplied was empty. Use an empty password?', array('y', 'n'), 'n'); + if ($blank == 'y') + { + $blankPassword = true; + } + } + } + $database = ''; + + while ($database == '') { + $database = $this->in('Database Name:', null, 'cake'); + } + $prefix = ''; + + while ($prefix == '') { + $prefix = $this->in('Table Prefix?', null, 'n'); + } + + if (low($prefix) == 'n') { + $prefix = null; + } + $encoding = ''; + + while ($encoding == '') { + $encoding = $this->in('Table encoding?', null, 'n'); + } + + if (low($encoding) == 'n') { + $encoding = null; + } + $schema = ''; + + if ($driver == 'postgres') { + while ($schema == '') { + $schema = $this->in('Table schema?', null, 'n'); + } + } + + if (low($schema) == 'n') { + $schema = null; + } + + $config = compact('name', 'driver', 'persistent', 'host', 'login', 'password', 'database', 'prefix', 'encoding', 'port', 'schema'); + + while ($this->__verify($config) == false) { + $this->__interactive(); + } + $dbConfigs[] = $config; + $doneYet = $this->in('Do you wish to add another database configuration?', null, 'n'); + + if (low($doneYet == 'n')) { + $done = true; + } + } + + $this->bake($dbConfigs); + config('database'); + return true; + } +/** + * Output verification message and bake if it looks good + * + * @return boolean True if user says it looks good, false otherwise + * @access private + */ + function __verify($config) { + $config = array_merge($this->__defaultConfig, $config); + extract($config); + $this->out(''); + $this->hr(); + $this->out('The following database configuration will be created:'); + $this->hr(); + $this->out("Name: $name"); + $this->out("Driver: $driver"); + $this->out("Persistent: $persistent"); + $this->out("Host: $host"); + + if ($port) { + $this->out("Port: $port"); + } + + $this->out("User: $login"); + $this->out("Pass: " . str_repeat('*', strlen($password))); + $this->out("Database: $database"); + + if ($prefix) { + $this->out("Table prefix: $prefix"); + } + + if ($schema) { + $this->out("Schema: $schema"); + } + + if ($encoding) { + $this->out("Encoding: $encoding"); + } + + $this->hr(); + $looksGood = $this->in('Look okay?', array('y', 'n'), 'y'); + + if (strtolower($looksGood) == 'y' || strtolower($looksGood) == 'yes') { + return $config; + } + return false; + } +/** + * Assembles and writes database.php + * + * @param array $configs Configuration settings to use + * @return boolean Success + * @access public + */ + function bake($configs) { + if (!is_dir($this->path)) { + $this->err($this->path . ' not found'); + return false; + } + + $filename = $this->path . 'database.php'; + $oldConfigs = array(); + + if (file_exists($filename)) { + $db = new DATABASE_CONFIG; + $temp = get_class_vars(get_class($db)); + + foreach ($temp as $configName => $info) { + $info = array_merge($this->__defaultConfig, $info); + + if (!isset($info['schema'])) { + $info['schema'] = null; + } + if (!isset($info['encoding'])) { + $info['encoding'] = null; + } + if (!isset($info['port'])) { + $info['port'] = null; + } + + if ($info['persistent'] === false) { + $info['persistent'] = 'false'; + } else { + $info['persistent'] = ($info['persistent'] == true) ? 'true' : 'false'; + } + + $oldConfigs[] = array( + 'name' => $configName, + 'driver' => $info['driver'], + 'persistent' => $info['persistent'], + 'host' => $info['host'], + 'port' => $info['port'], + 'login' => $info['login'], + 'password' => $info['password'], + 'database' => $info['database'], + 'prefix' => $info['prefix'], + 'schema' => $info['schema'], + 'encoding' => $info['encoding'] + ); + } + } + + foreach ($oldConfigs as $key => $oldConfig) { + foreach ($configs as $key1 => $config) { + if ($oldConfig['name'] == $config['name']) { + unset($oldConfigs[$key]); + } + } + } + + $configs = array_merge($oldConfigs, $configs); + $out = "<?php\n"; + $out .= "class DATABASE_CONFIG {\n\n"; + + foreach ($configs as $config) { + $config = array_merge($this->__defaultConfig, $config); + extract($config); + + $out .= "\tvar \${$name} = array(\n"; + $out .= "\t\t'driver' => '{$driver}',\n"; + $out .= "\t\t'persistent' => {$persistent},\n"; + $out .= "\t\t'host' => '{$host}',\n"; + + if ($port) { + $out .= "\t\t'port' => {$port},\n"; + } + + $out .= "\t\t'login' => '{$login}',\n"; + $out .= "\t\t'password' => '{$password}',\n"; + $out .= "\t\t'database' => '{$database}',\n"; + + if ($schema) { + $out .= "\t\t'schema' => '{$schema}',\n"; + } + + if ($prefix) { + $out .= "\t\t'prefix' => '{$prefix}',\n"; + } + + if ($encoding) { + $out .= "\t\t'encoding' => '{$encoding}'\n"; + } + + $out .= "\t);\n"; + } + + $out .= "}\n"; + $out .= "?>"; + $filename = $this->path.'database.php'; + return $this->createFile($filename, $out); + } +} +?> \ No newline at end of file diff --git a/cake/console/libs/tasks/extract.php b/cake/console/libs/tasks/extract.php new file mode 100755 index 0000000..7918e8c --- /dev/null +++ b/cake/console/libs/tasks/extract.php @@ -0,0 +1,685 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Short description for file. + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.console.libs + * @since CakePHP(tm) v 1.2.0.5012 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Only used when -debug option + */ + ob_start(); + + $singularReturn = __('Singular string return __()', true); + $singularEcho = __('Singular string echo __()'); + + $pluralReturn = __n('% apple in the bowl (plural string return __n())', '% apples in the blowl (plural string 2 return __n())', 3, true); + $pluralEcho = __n('% apple in the bowl (plural string 2 echo __n())', '% apples in the blowl (plural string 2 echo __n()', 3); + + $singularDomainReturn = __d('controllers', 'Singular string domain lookup return __d()', true); + $singularDomainEcho = __d('controllers', 'Singular string domain lookup echo __d()'); + + $pluralDomainReturn = __dn('controllers', '% pears in the bowl (plural string domain lookup return __dn())', '% pears in the blowl (plural string domain lookup return __dn())', 3, true); + $pluralDomainEcho = __dn('controllers', '% pears in the bowl (plural string domain lookup echo __dn())', '% pears in the blowl (plural string domain lookup echo __dn())', 3); + + $singularDomainCategoryReturn = __dc('controllers', 'Singular string domain and category lookup return __dc()', 5, true); + $singularDomainCategoryEcho = __dc('controllers', 'Singular string domain and category lookup echo __dc()', 5); + + $pluralDomainCategoryReturn = __dcn('controllers', '% apple in the bowl (plural string 1 domain and category lookup return __dcn())', '% apples in the blowl (plural string 2 domain and category lookup return __dcn())', 3, 5, true); + $pluralDomainCategoryEcho = __dcn('controllers', '% apple in the bowl (plural string 1 domain and category lookup echo __dcn())', '% apples in the blowl (plural string 2 domain and category lookup echo __dcn())', 3, 5); + + $categoryReturn = __c('Category string lookup line return __c()', 5, true); + $categoryEcho = __c('Category string lookup line echo __c()', 5); + + ob_end_clean(); +/** + * Language string extractor + * + * @package cake + * @subpackage cake.cake.console.libs + */ +class ExtractTask extends Shell{ +/** + * Path to use when looking for strings + * + * @var string + * @access public + */ + var $path = null; +/** + * Files from where to extract + * + * @var array + * @access public + */ + var $files = array(); +/** + * Filename where to deposit translations + * + * @var string + * @access private + */ + var $__filename = 'default'; +/** + * True if all strings should be merged into one file + * + * @var boolean + * @access private + */ + var $__oneFile = true; +/** + * Current file being processed + * + * @var string + * @access private + */ + var $__file = null; +/** + * Extracted tokens + * + * @var array + * @access private + */ + var $__tokens = array(); +/** + * Extracted strings + * + * @var array + * @access private + */ + var $__strings = array(); +/** + * History of file versions + * + * @var array + * @access private + */ + var $__fileVersions = array(); +/** + * Destination path + * + * @var string + * @access private + */ + var $__output = null; +/** + * Execution method always used for tasks + * + * @access public + */ + function execute() { + if (isset($this->params['files']) && !is_array($this->params['files'])) { + $this->files = explode(',', $this->params['files']); + } + if (isset($this->params['path'])) { + $this->path = $this->params['path']; + } else { + $response = ''; + while ($response == '') { + $response = $this->in("What is the full path you would like to extract?\nExample: " . $this->params['root'] . DS . "myapp\n[Q]uit", null, $this->params['working']); + if (strtoupper($response) === 'Q') { + $this->out('Extract Aborted'); + $this->_stop(); + } + } + + if (is_dir($response)) { + $this->path = $response; + } else { + $this->err('The directory path you supplied was not found. Please try again.'); + $this->execute(); + } + } + + if (isset($this->params['debug'])) { + $this->path = ROOT; + $this->files = array(__FILE__); + } + + if (isset($this->params['output'])) { + $this->__output = $this->params['output']; + } else { + $response = ''; + while ($response == '') { + $response = $this->in("What is the full path you would like to output?\nExample: " . $this->path . DS . "locale\n[Q]uit", null, $this->path . DS . "locale"); + if (strtoupper($response) === 'Q') { + $this->out('Extract Aborted'); + $this->_stop(); + } + } + + if (is_dir($response)) { + $this->__output = $response . DS; + } else { + $this->err('The directory path you supplied was not found. Please try again.'); + $this->execute(); + } + } + + if (empty($this->files)) { + $this->files = $this->__searchDirectory(); + } + $this->__extract(); + } +/** + * Extract text + * + * @access private + */ + function __extract() { + $this->out(''); + $this->out(''); + $this->out(__('Extracting...', true)); + $this->hr(); + $this->out(__('Path: ', true). $this->path); + $this->out(__('Output Directory: ', true). $this->__output); + $this->hr(); + + $response = ''; + $filename = ''; + while ($response == '') { + $response = $this->in(__('Would you like to merge all translations into one file?', true), array('y','n'), 'y'); + if (strtolower($response) == 'n') { + $this->__oneFile = false; + } else { + while ($filename == '') { + $filename = $this->in(__('What should we name this file?', true), null, $this->__filename); + if ($filename == '') { + $this->out(__('The filesname you supplied was empty. Please try again.', true)); + } + } + $this->__filename = $filename; + } + } + $this->__extractTokens(); + } +/** + * Show help options + * + * @access public + */ + function help() { + $this->out(__('CakePHP Language String Extraction:', true)); + $this->hr(); + $this->out(__('The Extract script generates .pot file(s) with translations', true)); + $this->out(__('By default the .pot file(s) will be place in the locale directory of -app', true)); + $this->out(__('By default -app is ROOT/app', true)); + $this->hr(); + $this->out(__('usage: cake i18n extract [command] [path...]', true)); + $this->out(''); + $this->out(__('commands:', true)); + $this->out(__(' -app [path...]: directory where your application is located', true)); + $this->out(__(' -root [path...]: path to install', true)); + $this->out(__(' -core [path...]: path to cake directory', true)); + $this->out(__(' -path [path...]: Full path to directory to extract strings', true)); + $this->out(__(' -output [path...]: Full path to output directory', true)); + $this->out(__(' -files: [comma separated list of files, full path to file is needed]', true)); + $this->out(__(' cake i18n extract help: Shows this help message.', true)); + $this->out(__(' -debug: Perform self test.', true)); + $this->out(''); + } +/** + * Extract tokens out of all files to be processed + * + * @access private + */ + function __extractTokens() { + foreach ($this->files as $file) { + $this->__file = $file; + $this->out(sprintf(__('Processing %s...', true), $file)); + + $code = file_get_contents($file); + + $this->__findVersion($code, $file); + $allTokens = token_get_all($code); + $this->__tokens = array(); + $lineNumber = 1; + + foreach ($allTokens as $token) { + if ((!is_array($token)) || (($token[0] != T_WHITESPACE) && ($token[0] != T_INLINE_HTML))) { + if (is_array($token)) { + $token[] = $lineNumber; + } + $this->__tokens[] = $token; + } + + if (is_array($token)) { + $lineNumber += count(split("\n", $token[1])) - 1; + } else { + $lineNumber += count(split("\n", $token)) - 1; + } + } + unset($allTokens); + $this->basic(); + $this->basic('__c'); + $this->extended(); + $this->extended('__dc', 2); + $this->extended('__n', 0, true); + $this->extended('__dn', 2, true); + $this->extended('__dcn', 4, true); + } + $this->__buildFiles(); + $this->__writeFiles(); + $this->out('Done.'); + } +/** + * Will parse __(), __c() functions + * + * @param string $functionName Function name that indicates translatable string (e.g: '__') + * @access public + */ + function basic($functionName = '__') { + $count = 0; + $tokenCount = count($this->__tokens); + + while (($tokenCount - $count) > 3) { + list($countToken, $parenthesis, $middle, $right) = array($this->__tokens[$count], $this->__tokens[$count + 1], $this->__tokens[$count + 2], $this->__tokens[$count + 3]); + if (!is_array($countToken)) { + $count++; + continue; + } + + list($type, $string, $line) = $countToken; + if (($type == T_STRING) && ($string == $functionName) && ($parenthesis == '(')) { + + if (in_array($right, array(')', ',')) + && (is_array($middle) && ($middle[0] == T_CONSTANT_ENCAPSED_STRING))) { + + if ($this->__oneFile === true) { + $this->__strings[$this->__formatString($middle[1])][$this->__file][] = $line; + } else { + $this->__strings[$this->__file][$this->__formatString($middle[1])][] = $line; + } + } else { + $this->__markerError($this->__file, $line, $functionName, $count); + } + } + $count++; + } + } +/** + * Will parse __d(), __dc(), __n(), __dn(), __dcn() + * + * @param string $functionName Function name that indicates translatable string (e.g: '__') + * @param integer $shift Number of parameters to shift to find translateable string + * @param boolean $plural Set to true if function supports plural format, false otherwise + * @access public + */ + function extended($functionName = '__d', $shift = 0, $plural = false) { + $count = 0; + $tokenCount = count($this->__tokens); + + while (($tokenCount - $count) > 7) { + list($countToken, $firstParenthesis) = array($this->__tokens[$count], $this->__tokens[$count + 1]); + if (!is_array($countToken)) { + $count++; + continue; + } + + list($type, $string, $line) = $countToken; + if (($type == T_STRING) && ($string == $functionName) && ($firstParenthesis == '(')) { + $position = $count; + $depth = 0; + + while ($depth == 0) { + if ($this->__tokens[$position] == '(') { + $depth++; + } elseif ($this->__tokens[$position] == ')') { + $depth--; + } + $position++; + } + + if ($plural) { + $end = $position + $shift + 7; + + if ($this->__tokens[$position + $shift + 5] === ')') { + $end = $position + $shift + 5; + } + + if (empty($shift)) { + list($singular, $firstComma, $plural, $seoncdComma, $endParenthesis) = array($this->__tokens[$position], $this->__tokens[$position + 1], $this->__tokens[$position + 2], $this->__tokens[$position + 3], $this->__tokens[$end]); + $condition = ($seoncdComma == ','); + } else { + list($domain, $firstComma, $singular, $seoncdComma, $plural, $comma3, $endParenthesis) = array($this->__tokens[$position], $this->__tokens[$position + 1], $this->__tokens[$position + 2], $this->__tokens[$position + 3], $this->__tokens[$position + 4], $this->__tokens[$position + 5], $this->__tokens[$end]); + $condition = ($comma3 == ','); + } + $condition = $condition && + (is_array($singular) && ($singular[0] == T_CONSTANT_ENCAPSED_STRING)) && + (is_array($plural) && ($plural[0] == T_CONSTANT_ENCAPSED_STRING)); + } else { + if ($this->__tokens[$position + $shift + 5] === ')') { + $comma = $this->__tokens[$position + $shift + 3]; + $end = $position + $shift + 5; + } else { + $comma = null; + $end = $position + $shift + 3; + } + + list($domain, $firstComma, $text, $seoncdComma, $endParenthesis) = array($this->__tokens[$position], $this->__tokens[$position + 1], $this->__tokens[$position + 2], $comma, $this->__tokens[$end]); + $condition = ($seoncdComma == ',' || $seoncdComma === null) && + (is_array($domain) && ($domain[0] == T_CONSTANT_ENCAPSED_STRING)) && + (is_array($text) && ($text[0] == T_CONSTANT_ENCAPSED_STRING)); + } + + if (($endParenthesis == ')') && $condition) { + if ($this->__oneFile === true) { + if ($plural) { + $this->__strings[$this->__formatString($singular[1]) . "\0" . $this->__formatString($plural[1])][$this->__file][] = $line; + } else { + $this->__strings[$this->__formatString($text[1])][$this->__file][] = $line; + } + } else { + if ($plural) { + $this->__strings[$this->__file][$this->__formatString($singular[1]) . "\0" . $this->__formatString($plural[1])][] = $line; + } else { + $this->__strings[$this->__file][$this->__formatString($text[1])][] = $line; + } + } + } else { + $this->__markerError($this->__file, $line, $functionName, $count); + } + } + $count++; + } + } +/** + * Build the translate template file contents out of obtained strings + * + * @access private + */ + function __buildFiles() { + foreach ($this->__strings as $str => $fileInfo) { + $output = ''; + $occured = $fileList = array(); + + if ($this->__oneFile === true) { + foreach ($fileInfo as $file => $lines) { + $occured[] = "$file:" . join(';', $lines); + + if (isset($this->__fileVersions[$file])) { + $fileList[] = $this->__fileVersions[$file]; + } + } + $occurances = join("\n#: ", $occured); + $occurances = str_replace($this->path, '', $occurances); + $output = "#: $occurances\n"; + $filename = $this->__filename; + + if (strpos($str, "\0") === false) { + $output .= "msgid \"$str\"\n"; + $output .= "msgstr \"\"\n"; + } else { + list($singular, $plural) = explode("\0", $str); + $output .= "msgid \"$singular\"\n"; + $output .= "msgid_plural \"$plural\"\n"; + $output .= "msgstr[0] \"\"\n"; + $output .= "msgstr[1] \"\"\n"; + } + $output .= "\n"; + } else { + foreach ($fileInfo as $file => $lines) { + $filename = $str; + $occured = array("$str:" . join(';', $lines)); + + if (isset($this->__fileVersions[$str])) { + $fileList[] = $this->__fileVersions[$str]; + } + $occurances = join("\n#: ", $occured); + $occurances = str_replace($this->path, '', $occurances); + $output .= "#: $occurances\n"; + + if (strpos($file, "\0") === false) { + $output .= "msgid \"$file\"\n"; + $output .= "msgstr \"\"\n"; + } else { + list($singular, $plural) = explode("\0", $file); + $output .= "msgid \"$singular\"\n"; + $output .= "msgid_plural \"$plural\"\n"; + $output .= "msgstr[0] \"\"\n"; + $output .= "msgstr[1] \"\"\n"; + } + $output .= "\n"; + } + } + $this->__store($filename, $output, $fileList); + } + } +/** + * Prepare a file to be stored + * + * @param string $file Filename + * @param string $input What to store + * @param array $fileList File list + * @param integer $get Set to 1 to get files to store, false to set + * @return mixed If $get == 1, files to store, otherwise void + * @access private + */ + function __store($file = 0, $input = 0, $fileList = array(), $get = 0) { + static $storage = array(); + + if (!$get) { + if (isset($storage[$file])) { + $storage[$file][1] = array_unique(array_merge($storage[$file][1], $fileList)); + $storage[$file][] = $input; + } else { + $storage[$file] = array(); + $storage[$file][0] = $this->__writeHeader(); + $storage[$file][1] = $fileList; + $storage[$file][2] = $input; + } + } else { + return $storage; + } + } +/** + * Write the files that need to be stored + * + * @access private + */ + function __writeFiles() { + $output = $this->__store(0, 0, array(), 1); + $output = $this->__mergeFiles($output); + + foreach ($output as $file => $content) { + $tmp = str_replace(array($this->path, '.php','.ctp','.thtml', '.inc','.tpl' ), '', $file); + $tmp = str_replace(DS, '.', $tmp); + $file = str_replace('.', '-', $tmp) .'.pot'; + $fileList = $content[1]; + + unset($content[1]); + + $fileList = str_replace(array($this->path), '', $fileList); + + if (count($fileList) > 1) { + $fileList = "Generated from files:\n# " . join("\n# ", $fileList); + } elseif (count($fileList) == 1) { + $fileList = 'Generated from file: ' . join('', $fileList); + } else { + $fileList = 'No version information was available in the source files.'; + } + + if (is_file($this->__output . $file)) { + $response = ''; + while ($response == '') { + $response = $this->in("\n\nError: ".$file . ' already exists in this location. Overwrite?', array('y','n', 'q'), 'n'); + if (strtoupper($response) === 'Q') { + $this->out('Extract Aborted'); + $this->_stop(); + } elseif (strtoupper($response) === 'N') { + $response = ''; + while ($response == '') { + $response = $this->in("What would you like to name this file?\nExample: new_" . $file, null, "new_" . $file); + $file = $response; + } + } + } + } + $fp = fopen($this->__output . $file, 'w'); + fwrite($fp, str_replace('--VERSIONS--', $fileList, join('', $content))); + fclose($fp); + } + } +/** + * Merge output files + * + * @param array $output Output to merge + * @return array Merged output + * @access private + */ + function __mergeFiles($output) { + foreach ($output as $file => $content) { + if (count($content) <= 1 && $file != $this->__filename) { + @$output[$this->__filename][1] = array_unique(array_merge($output[$this->__filename][1], $content[1])); + + if (!isset($output[$this->__filename][0])) { + $output[$this->__filename][0] = $content[0]; + } + unset($content[0]); + unset($content[1]); + + foreach ($content as $msgid) { + $output[$this->__filename][] = $msgid; + } + unset($output[$file]); + } + } + return $output; + } +/** + * Build the translation template header + * + * @return string Translation template header + * @access private + */ + function __writeHeader() { + $output = "# LANGUAGE translation of CakePHP Application\n"; + $output .= "# Copyright YEAR NAME <EMAIL@ADDRESS>\n"; + $output .= "# --VERSIONS--\n"; + $output .= "#\n"; + $output .= "#, fuzzy\n"; + $output .= "msgid \"\"\n"; + $output .= "msgstr \"\"\n"; + $output .= "\"Project-Id-Version: PROJECT VERSION\\n\"\n"; + $output .= "\"POT-Creation-Date: " . date("Y-m-d H:iO") . "\\n\"\n"; + $output .= "\"PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\\n\"\n"; + $output .= "\"Last-Translator: NAME <EMAIL@ADDRESS>\\n\"\n"; + $output .= "\"Language-Team: LANGUAGE <EMAIL@ADDRESS>\\n\"\n"; + $output .= "\"MIME-Version: 1.0\\n\"\n"; + $output .= "\"Content-Type: text/plain; charset=utf-8\\n\"\n"; + $output .= "\"Content-Transfer-Encoding: 8bit\\n\"\n"; + $output .= "\"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\\n\"\n\n"; + return $output; + } +/** + * Find the version number of a file looking for SVN commands + * + * @param string $code Source code of file + * @param string $file File + * @access private + */ + function __findVersion($code, $file) { + $header = '$Id' . ':'; + if (preg_match('/\\' . $header . ' [\\w.]* ([\\d]*)/', $code, $versionInfo)) { + $version = str_replace(ROOT, '', 'Revision: ' . $versionInfo[1] . ' ' .$file); + $this->__fileVersions[$file] = $version; + } + } +/** + * Format a string to be added as a translateable string + * + * @param string $string String to format + * @return string Formatted string + * @access private + */ + function __formatString($string) { + $quote = substr($string, 0, 1); + $string = substr($string, 1, -1); + if ($quote == '"') { + $string = stripcslashes($string); + } else { + $string = strtr($string, array("\\'" => "'", "\\\\" => "\\")); + } + $string = str_replace("\r\n", "\n", $string); + return addcslashes($string, "\0..\37\\\""); + } +/** + * Indicate an invalid marker on a processed file + * + * @param string $file File where invalid marker resides + * @param integer $line Line number + * @param string $marker Marker found + * @param integer $count Count + * @access private + */ + function __markerError($file, $line, $marker, $count) { + $this->out("Invalid marker content in $file:$line\n* $marker(", true); + $count += 2; + $tokenCount = count($this->__tokens); + $parenthesis = 1; + + while ((($tokenCount - $count) > 0) && $parenthesis) { + if (is_array($this->__tokens[$count])) { + $this->out($this->__tokens[$count][1], false); + } else { + $this->out($this->__tokens[$count], false); + if ($this->__tokens[$count] == '(') { + $parenthesis++; + } + + if ($this->__tokens[$count] == ')') { + $parenthesis--; + } + } + $count++; + } + $this->out("\n", true); + } +/** + * Search the specified path for files that may contain translateable strings + * + * @param string $path Path (or set to null to use current) + * @return array Files + * @access private + */ + function __searchDirectory($path = null) { + if ($path === null) { + $path = $this->path .DS; + } + $files = glob("$path*.{php,ctp,thtml,inc,tpl}", GLOB_BRACE); + $dirs = glob("$path*", GLOB_ONLYDIR); + + $files = $files ? $files : array(); + $dirs = $dirs ? $dirs : array(); + + foreach ($dirs as $dir) { + if (!preg_match("!(^|.+/)(CVS|.svn)$!", $dir)) { + $files = array_merge($files, $this->__searchDirectory("$dir" . DS)); + if (($id = array_search($dir . DS . 'extract.php', $files)) !== FALSE) { + unset($files[$id]); + } + } + } + return $files; + } +} +?> \ No newline at end of file diff --git a/cake/console/libs/tasks/model.php b/cake/console/libs/tasks/model.php new file mode 100755 index 0000000..779d088 --- /dev/null +++ b/cake/console/libs/tasks/model.php @@ -0,0 +1,938 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * The ModelTask handles creating and updating models files. + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.console.libs.tasks + * @since CakePHP(tm) v 1.2 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +App::import('Model', 'ConnectionManager'); +/** + * Task class for creating and updating model files. + * + * @package cake + * @subpackage cake.cake.console.libs.tasks + */ +class ModelTask extends Shell { +/** + * Name of plugin + * + * @var string + * @access public + */ + var $plugin = null; +/** + * path to MODELS directory + * + * @var string + * @access public + */ + var $path = MODELS; +/** + * tasks + * + * @var array + * @access public + */ + var $tasks = array('DbConfig'); +/** + * Execution method always used for tasks + * + * @access public + */ + function execute() { + if (empty($this->args)) { + $this->__interactive(); + } + + if (!empty($this->args[0])) { + $model = Inflector::camelize($this->args[0]); + if ($this->bake($model)) { + if ($this->_checkUnitTest()) { + $this->bakeTest($model); + } + } + } + } +/** + * Handles interactive baking + * + * @access private + */ + function __interactive() { + $this->hr(); + $this->out(sprintf("Bake Model\nPath: %s", $this->path)); + $this->hr(); + $this->interactive = true; + + $useTable = null; + $primaryKey = 'id'; + $validate = array(); + $associations = array('belongsTo'=> array(), 'hasOne'=> array(), 'hasMany' => array(), 'hasAndBelongsToMany'=> array()); + + $useDbConfig = 'default'; + $configs = get_class_vars('DATABASE_CONFIG'); + + if (!is_array($configs)) { + return $this->DbConfig->execute(); + } + + $connections = array_keys($configs); + if (count($connections) > 1) { + $useDbConfig = $this->in(__('Use Database Config', true) .':', $connections, 'default'); + } + + $currentModelName = $this->getName($useDbConfig); + $db =& ConnectionManager::getDataSource($useDbConfig); + $useTable = Inflector::tableize($currentModelName); + $fullTableName = $db->fullTableName($useTable, false); + $tableIsGood = false; + + if (array_search($useTable, $this->__tables) === false) { + $this->out(''); + $this->out(sprintf(__("Given your model named '%s', Cake would expect a database table named %s", true), $currentModelName, $fullTableName)); + $tableIsGood = $this->in(__('Do you want to use this table?', true), array('y','n'), 'y'); + } + + if (strtolower($tableIsGood) == 'n' || strtolower($tableIsGood) == 'no') { + $useTable = $this->in(__('What is the name of the table (enter "null" to use NO table)?', true)); + } + + while ($tableIsGood == false && strtolower($useTable) != 'null') { + if (is_array($this->__tables) && !in_array($useTable, $this->__tables)) { + $fullTableName = $db->fullTableName($useTable, false); + $this->out($fullTableName . ' does not exist.'); + $useTable = $this->in(__('What is the name of the table (enter "null" to use NO table)?', true)); + $tableIsGood = false; + } else { + $tableIsGood = true; + } + } + + $wannaDoValidation = $this->in(__('Would you like to supply validation criteria for the fields in your model?', true), array('y','n'), 'y'); + + if (in_array($useTable, $this->__tables)) { + App::import('Model'); + $tempModel = new Model(array('name' => $currentModelName, 'table' => $useTable, 'ds' => $useDbConfig)); + + $fields = $tempModel->schema(); + if (!array_key_exists('id', $fields)) { + foreach ($fields as $name => $field) { + if (isset($field['key']) && $field['key'] == 'primary') { + break; + } + } + $primaryKey = $this->in(__('What is the primaryKey?', true), null, $name); + } + } + + if (array_search($useTable, $this->__tables) !== false && (strtolower($wannaDoValidation) == 'y' || strtolower($wannaDoValidation) == 'yes')) { + $validate = $this->doValidation($tempModel); + } + + $wannaDoAssoc = $this->in(__('Would you like to define model associations (hasMany, hasOne, belongsTo, etc.)?', true), array('y','n'), 'y'); + if ((strtolower($wannaDoAssoc) == 'y' || strtolower($wannaDoAssoc) == 'yes')) { + $associations = $this->doAssociations($tempModel); + } + + $this->out(''); + $this->hr(); + $this->out(__('The following Model will be created:', true)); + $this->hr(); + $this->out("Name: " . $currentModelName); + + if ($useDbConfig !== 'default') { + $this->out("DB Config: " . $useDbConfig); + } + if ($fullTableName !== Inflector::tableize($currentModelName)) { + $this->out("DB Table: " . $fullTableName); + } + if ($primaryKey != 'id') { + $this->out("Primary Key: " . $primaryKey); + } + if (!empty($validate)) { + $this->out("Validation: " . print_r($validate, true)); + } + if (!empty($associations)) { + $this->out("Associations:"); + + if (!empty($associations['belongsTo'])) { + for ($i = 0; $i < count($associations['belongsTo']); $i++) { + $this->out(" $currentModelName belongsTo {$associations['belongsTo'][$i]['alias']}"); + } + } + + if (!empty($associations['hasOne'])) { + for ($i = 0; $i < count($associations['hasOne']); $i++) { + $this->out(" $currentModelName hasOne {$associations['hasOne'][$i]['alias']}"); + } + } + + if (!empty($associations['hasMany'])) { + for ($i = 0; $i < count($associations['hasMany']); $i++) { + $this->out(" $currentModelName hasMany {$associations['hasMany'][$i]['alias']}"); + } + } + + if (!empty($associations['hasAndBelongsToMany'])) { + for ($i = 0; $i < count($associations['hasAndBelongsToMany']); $i++) { + $this->out(" $currentModelName hasAndBelongsToMany {$associations['hasAndBelongsToMany'][$i]['alias']}"); + } + } + } + $this->hr(); + $looksGood = $this->in(__('Look okay?', true), array('y','n'), 'y'); + + if (strtolower($looksGood) == 'y' || strtolower($looksGood) == 'yes') { + if ($this->bake($currentModelName, $associations, $validate, $primaryKey, $useTable, $useDbConfig)) { + if ($this->_checkUnitTest()) { + $this->bakeTest($currentModelName, $useTable, $associations); + } + } + } else { + return false; + } + } +/** + * Handles associations + * + * @param object $model + * @param boolean $interactive + * @return array $validate + * @access public + */ + function doValidation(&$model, $interactive = true) { + if (!is_object($model)) { + return false; + } + $fields = $model->schema(); + + if (empty($fields)) { + return false; + } + + $validate = array(); + + $options = array(); + + if (class_exists('Validation')) { + $parent = get_class_methods(get_parent_class('Validation')); + $options = array_diff(get_class_methods('Validation'), $parent); + } + + foreach ($fields as $fieldName => $field) { + $prompt = 'Field: ' . $fieldName . "\n"; + $prompt .= 'Type: ' . $field['type'] . "\n"; + $prompt .= '---------------------------------------------------------------'."\n"; + $prompt .= 'Please select one of the following validation options:'."\n"; + $prompt .= '---------------------------------------------------------------'."\n"; + + sort($options); + + $skip = 1; + foreach ($options as $key => $option) { + if ($option{0} != '_' && strtolower($option) != 'getinstance') { + $prompt .= "{$skip} - {$option}\n"; + $choices[$skip] = strtolower($option); + $skip++; + } + } + + $methods = array_flip($choices); + + $prompt .= "{$skip} - Do not do any validation on this field.\n"; + $prompt .= "... or enter in a valid regex validation string.\n"; + + $guess = $skip; + if ($field['null'] != 1 && $fieldName != $model->primaryKey && !in_array($fieldName, array('created', 'modified', 'updated'))) { + if ($fieldName == 'email') { + $guess = $methods['email']; + } elseif ($field['type'] == 'string') { + $guess = $methods['notempty']; + } elseif ($field['type'] == 'integer') { + $guess = $methods['numeric']; + } elseif ($field['type'] == 'boolean') { + $guess = $methods['numeric']; + } elseif ($field['type'] == 'datetime') { + $guess = $methods['date']; + } + } + + if ($interactive === true) { + $this->out(''); + $choice = $this->in($prompt, null, $guess); + } else { + $choice = $guess; + } + if ($choice != $skip) { + if (is_numeric($choice) && isset($choices[$choice])) { + $validate[$fieldName] = $choices[$choice]; + } else { + $validate[$fieldName] = $choice; + } + } + } + return $validate; + } + +/** + * Handles associations + * + * @param object $model + * @param boolean $interactive + * @return array $assocaitons + * @access public + */ + function doAssociations(&$model, $interactive = true) { + + if (!is_object($model)) { + return false; + } + $this->out(__('One moment while the associations are detected.', true)); + + $fields = $model->schema(); + + if (empty($fields)) { + return false; + } + + $primaryKey = $model->primaryKey; + $foreignKey = $this->_modelKey($model->name); + + $associations = array('belongsTo' => array(), 'hasMany' => array(), 'hasOne'=> array(), 'hasAndBelongsToMany' => array()); + $possibleKeys = array(); + + //Look for belongsTo + $i = 0; + foreach ($fields as $fieldName => $field) { + $offset = strpos($fieldName, '_id'); + if ($fieldName != $model->primaryKey && $offset !== false) { + $tmpModelName = $this->_modelNameFromKey($fieldName); + $associations['belongsTo'][$i]['alias'] = $tmpModelName; + $associations['belongsTo'][$i]['className'] = $tmpModelName; + $associations['belongsTo'][$i]['foreignKey'] = $fieldName; + $i++; + } + } + //Look for hasOne and hasMany and hasAndBelongsToMany + $i = $j = 0; + + foreach ($this->__tables as $otherTable) { + App::import('Model'); + $tmpModelName = $this->_modelName($otherTable); + $tempOtherModel = & new Model(array('name' => $tmpModelName, 'table' => $otherTable, 'ds' => $model->useDbConfig)); + $modelFieldsTemp = $tempOtherModel->schema(); + + $offset = strpos($otherTable, $model->table . '_'); + $otherOffset = strpos($otherTable, '_' . $model->table); + + foreach ($modelFieldsTemp as $fieldName => $field) { + if ($field['type'] == 'integer' || $field['type'] == 'string') { + $possibleKeys[$otherTable][] = $fieldName; + } + if ($fieldName != $model->primaryKey && $fieldName == $foreignKey && $offset === false && $otherOffset === false) { + $associations['hasOne'][$j]['alias'] = $tempOtherModel->name; + $associations['hasOne'][$j]['className'] = $tempOtherModel->name; + $associations['hasOne'][$j]['foreignKey'] = $fieldName; + + $associations['hasMany'][$j]['alias'] = $tempOtherModel->name; + $associations['hasMany'][$j]['className'] = $tempOtherModel->name; + $associations['hasMany'][$j]['foreignKey'] = $fieldName; + $j++; + } + } + + if ($offset !== false) { + $offset = strlen($model->table . '_'); + $tmpModelName = $this->_modelName(substr($otherTable, $offset)); + $associations['hasAndBelongsToMany'][$i]['alias'] = $tmpModelName; + $associations['hasAndBelongsToMany'][$i]['className'] = $tmpModelName; + $associations['hasAndBelongsToMany'][$i]['foreignKey'] = $foreignKey; + $associations['hasAndBelongsToMany'][$i]['associationForeignKey'] = $this->_modelKey($tmpModelName); + $associations['hasAndBelongsToMany'][$i]['joinTable'] = $otherTable; + $i++; + } + + if ($otherOffset !== false) { + $tmpModelName = $this->_modelName(substr($otherTable, 0, $otherOffset)); + $associations['hasAndBelongsToMany'][$i]['alias'] = $tmpModelName; + $associations['hasAndBelongsToMany'][$i]['className'] = $tmpModelName; + $associations['hasAndBelongsToMany'][$i]['foreignKey'] = $foreignKey; + $associations['hasAndBelongsToMany'][$i]['associationForeignKey'] = $this->_modelKey($tmpModelName); + $associations['hasAndBelongsToMany'][$i]['joinTable'] = $otherTable; + $i++; + } + } + + if ($interactive !== true) { + unset($associations['hasOne']); + } + + if ($interactive === true) { + $this->hr(); + if (empty($associations)) { + $this->out(__('None found.', true)); + } else { + $this->out(__('Please confirm the following associations:', true)); + $this->hr(); + foreach ($associations as $type => $settings) { + if (!empty($associations[$type])) { + $count = count($associations[$type]); + $response = 'y'; + for ($i = 0; $i < $count; $i++) { + $prompt = "{$model->name} {$type} {$associations[$type][$i]['alias']}"; + $response = $this->in("{$prompt}?", array('y','n'), 'y'); + + if ('n' == strtolower($response) || 'no' == strtolower($response)) { + unset($associations[$type][$i]); + } else { + if ($model->name === $associations[$type][$i]['alias']) { + if ($type === 'belongsTo') { + $alias = 'Parent' . $associations[$type][$i]['alias']; + } + if ($type === 'hasOne' || $type === 'hasMany') { + $alias = 'Child' . $associations[$type][$i]['alias']; + } + + $alternateAlias = $this->in(sprintf(__('This is a self join. Use %s as the alias', true), $alias), array('y', 'n'), 'y'); + + if ('n' == strtolower($alternateAlias) || 'no' == strtolower($alternateAlias)) { + $associations[$type][$i]['alias'] = $this->in(__('Specify an alternate alias.', true)); + } else { + $associations[$type][$i]['alias'] = $alias; + } + } + } + } + $associations[$type] = array_merge($associations[$type]); + } + } + } + + $wannaDoMoreAssoc = $this->in(__('Would you like to define some additional model associations?', true), array('y','n'), 'n'); + + while ((strtolower($wannaDoMoreAssoc) == 'y' || strtolower($wannaDoMoreAssoc) == 'yes')) { + $assocs = array(1 => 'belongsTo', 2 => 'hasOne', 3 => 'hasMany', 4 => 'hasAndBelongsToMany'); + $bad = true; + while ($bad) { + $this->out(__('What is the association type?', true)); + $prompt = "1. belongsTo\n"; + $prompt .= "2. hasOne\n"; + $prompt .= "3. hasMany\n"; + $prompt .= "4. hasAndBelongsToMany\n"; + $assocType = intval($this->in($prompt, null, __("Enter a number", true))); + + if (intval($assocType) < 1 || intval($assocType) > 4) { + $this->out(__('The selection you entered was invalid. Please enter a number between 1 and 4.', true)); + } else { + $bad = false; + } + } + $this->out(__('For the following options be very careful to match your setup exactly. Any spelling mistakes will cause errors.', true)); + $this->hr(); + $alias = $this->in(__('What is the alias for this association?', true)); + $className = $this->in(sprintf(__('What className will %s use?', true), $alias), null, $alias ); + $suggestedForeignKey = null; + if ($assocType == '1') { + $showKeys = $possibleKeys[$model->table]; + $suggestedForeignKey = $this->_modelKey($alias); + } else { + $otherTable = Inflector::tableize($className); + if (in_array($otherTable, $this->__tables)) { + if ($assocType < '4') { + $showKeys = $possibleKeys[$otherTable]; + } else { + $showKeys = null; + } + } else { + $otherTable = $this->in(__('What is the table for this model?', true)); + $showKeys = $possibleKeys[$otherTable]; + } + $suggestedForeignKey = $this->_modelKey($model->name); + } + if (!empty($showKeys)) { + $this->out(__('A helpful List of possible keys', true)); + for ($i = 0; $i < count($showKeys); $i++) { + $this->out($i + 1 . ". " . $showKeys[$i]); + } + $foreignKey = $this->in(__('What is the foreignKey?', true), null, __("Enter a number", true)); + if (intval($foreignKey) > 0 && intval($foreignKey) <= $i ) { + $foreignKey = $showKeys[intval($foreignKey) - 1]; + } + } + if (!isset($foreignKey)) { + $foreignKey = $this->in(__('What is the foreignKey? Specify your own.', true), null, $suggestedForeignKey); + } + if ($assocType == '4') { + $associationForeignKey = $this->in(__('What is the associationForeignKey?', true), null, $this->_modelKey($model->name)); + $joinTable = $this->in(__('What is the joinTable?', true)); + } + $associations[$assocs[$assocType]] = array_values((array)$associations[$assocs[$assocType]]); + $count = count($associations[$assocs[$assocType]]); + $i = ($count > 0) ? $count : 0; + $associations[$assocs[$assocType]][$i]['alias'] = $alias; + $associations[$assocs[$assocType]][$i]['className'] = $className; + $associations[$assocs[$assocType]][$i]['foreignKey'] = $foreignKey; + if ($assocType == '4') { + $associations[$assocs[$assocType]][$i]['associationForeignKey'] = $associationForeignKey; + $associations[$assocs[$assocType]][$i]['joinTable'] = $joinTable; + } + $wannaDoMoreAssoc = $this->in(__('Define another association?', true), array('y','n'), 'y'); + } + } + return $associations; + } +/** + * Assembles and writes a Model file. + * + * @param mixed $name Model name or object + * @param mixed $associations if array and $name is not an object assume Model associations array otherwise boolean interactive + * @param array $validate Validation rules + * @param string $primaryKey Primary key to use + * @param string $useTable Table to use + * @param string $useDbConfig Database configuration setting to use + * @access private + */ + function bake($name, $associations = array(), $validate = array(), $primaryKey = 'id', $useTable = null, $useDbConfig = 'default') { + + if (is_object($name)) { + if (!is_array($associations)) { + $associations = $this->doAssociations($name, $associations); + $validate = $this->doValidation($name, $associations); + } + $primaryKey = $name->primaryKey; + $useTable = $name->table; + $useDbConfig = $name->useDbConfig; + $name = $name->name; + } + + $out = "<?php\n"; + $out .= "class {$name} extends {$this->plugin}AppModel {\n\n"; + $out .= "\tvar \$name = '{$name}';\n"; + + if ($useDbConfig !== 'default') { + $out .= "\tvar \$useDbConfig = '$useDbConfig';\n"; + } + + if (($useTable && $useTable !== Inflector::tableize($name)) || $useTable === false) { + $table = "'$useTable'"; + if (!$useTable) { + $table = 'false'; + } + $out .= "\tvar \$useTable = $table;\n"; + } + + if ($primaryKey !== 'id') { + $out .= "\tvar \$primaryKey = '$primaryKey';\n"; + } + + $validateCount = count($validate); + if (is_array($validate) && $validateCount > 0) { + $out .= "\tvar \$validate = array(\n"; + $keys = array_keys($validate); + for ($i = 0; $i < $validateCount; $i++) { + $val = "'" . $validate[$keys[$i]] . "'"; + $out .= "\t\t'" . $keys[$i] . "' => array({$val})"; + if ($i + 1 < $validateCount) { + $out .= ","; + } + $out .= "\n"; + } + $out .= "\t);\n"; + } + $out .= "\n"; + + if (!empty($associations)) { + if (!empty($associations['belongsTo']) || !empty($associations['hasOne']) || !empty($associations['hasMany']) || !empty($associations['hasAndBelongsToMany'])) { + $out.= "\t//The Associations below have been created with all possible keys, those that are not needed can be removed\n"; + } + + if (!empty($associations['belongsTo'])) { + $out .= "\tvar \$belongsTo = array(\n"; + $belongsToCount = count($associations['belongsTo']); + + for ($i = 0; $i < $belongsToCount; $i++) { + $out .= "\t\t'{$associations['belongsTo'][$i]['alias']}' => array(\n"; + $out .= "\t\t\t'className' => '{$associations['belongsTo'][$i]['className']}',\n"; + $out .= "\t\t\t'foreignKey' => '{$associations['belongsTo'][$i]['foreignKey']}',\n"; + $out .= "\t\t\t'conditions' => '',\n"; + $out .= "\t\t\t'fields' => '',\n"; + $out .= "\t\t\t'order' => ''\n"; + $out .= "\t\t)"; + if ($i + 1 < $belongsToCount) { + $out .= ","; + } + $out .= "\n"; + + } + $out .= "\t);\n\n"; + } + + if (!empty($associations['hasOne'])) { + $out .= "\tvar \$hasOne = array(\n"; + $hasOneCount = count($associations['hasOne']); + + for ($i = 0; $i < $hasOneCount; $i++) { + $out .= "\t\t'{$associations['hasOne'][$i]['alias']}' => array(\n"; + $out .= "\t\t\t'className' => '{$associations['hasOne'][$i]['className']}',\n"; + $out .= "\t\t\t'foreignKey' => '{$associations['hasOne'][$i]['foreignKey']}',\n"; + $out .= "\t\t\t'dependent' => false,\n"; + $out .= "\t\t\t'conditions' => '',\n"; + $out .= "\t\t\t'fields' => '',\n"; + $out .= "\t\t\t'order' => ''\n"; + $out .= "\t\t)"; + if ($i + 1 < $hasOneCount) { + $out .= ","; + } + $out .= "\n"; + + } + $out .= "\t);\n\n"; + } + + if (!empty($associations['hasMany'])) { + $out .= "\tvar \$hasMany = array(\n"; + $hasManyCount = count($associations['hasMany']); + + for ($i = 0; $i < $hasManyCount; $i++) { + $out .= "\t\t'{$associations['hasMany'][$i]['alias']}' => array(\n"; + $out .= "\t\t\t'className' => '{$associations['hasMany'][$i]['className']}',\n"; + $out .= "\t\t\t'foreignKey' => '{$associations['hasMany'][$i]['foreignKey']}',\n"; + $out .= "\t\t\t'dependent' => false,\n"; + $out .= "\t\t\t'conditions' => '',\n"; + $out .= "\t\t\t'fields' => '',\n"; + $out .= "\t\t\t'order' => '',\n"; + $out .= "\t\t\t'limit' => '',\n"; + $out .= "\t\t\t'offset' => '',\n"; + $out .= "\t\t\t'exclusive' => '',\n"; + $out .= "\t\t\t'finderQuery' => '',\n"; + $out .= "\t\t\t'counterQuery' => ''\n"; + $out .= "\t\t)"; + if ($i + 1 < $hasManyCount) { + $out .= ","; + } + $out .= "\n"; + } + $out .= "\t);\n\n"; + } + + if (!empty($associations['hasAndBelongsToMany'])) { + $out .= "\tvar \$hasAndBelongsToMany = array(\n"; + $hasAndBelongsToManyCount = count($associations['hasAndBelongsToMany']); + + for ($i = 0; $i < $hasAndBelongsToManyCount; $i++) { + $out .= "\t\t'{$associations['hasAndBelongsToMany'][$i]['alias']}' => array(\n"; + $out .= "\t\t\t'className' => '{$associations['hasAndBelongsToMany'][$i]['className']}',\n"; + $out .= "\t\t\t'joinTable' => '{$associations['hasAndBelongsToMany'][$i]['joinTable']}',\n"; + $out .= "\t\t\t'foreignKey' => '{$associations['hasAndBelongsToMany'][$i]['foreignKey']}',\n"; + $out .= "\t\t\t'associationForeignKey' => '{$associations['hasAndBelongsToMany'][$i]['associationForeignKey']}',\n"; + $out .= "\t\t\t'unique' => true,\n"; + $out .= "\t\t\t'conditions' => '',\n"; + $out .= "\t\t\t'fields' => '',\n"; + $out .= "\t\t\t'order' => '',\n"; + $out .= "\t\t\t'limit' => '',\n"; + $out .= "\t\t\t'offset' => '',\n"; + $out .= "\t\t\t'finderQuery' => '',\n"; + $out .= "\t\t\t'deleteQuery' => '',\n"; + $out .= "\t\t\t'insertQuery' => ''\n"; + $out .= "\t\t)"; + if ($i + 1 < $hasAndBelongsToManyCount) { + $out .= ","; + } + $out .= "\n"; + } + $out .= "\t);\n\n"; + } + } + $out .= "}\n"; + $out .= "?>"; + $filename = $this->path . Inflector::underscore($name) . '.php'; + $this->out("\nBaking model class for $name..."); + return $this->createFile($filename, $out); + } + +/** + * Assembles and writes a unit test file + * + * @param string $className Model class name + * @access private + */ + function bakeTest($className, $useTable = null, $associations = array()) { + $results = $this->fixture($className, $useTable); + + if ($results) { + $fixtureInc = 'app'; + if ($this->plugin) { + $fixtureInc = 'plugin.'.Inflector::underscore($this->plugin); + } + + $fixture[] = "'{$fixtureInc}." . Inflector::underscore($className) ."'"; + + if (!empty($associations)) { + $assoc[] = Set::extract($associations, 'belongsTo.{n}.className'); + $assoc[] = Set::extract($associations, 'hasOne.{n}.className'); + $assoc[] = Set::extract($associations, 'hasMany.{n}.className'); + foreach ($assoc as $key => $value) { + if (is_array($value)) { + foreach ($value as $class) { + $fixture[] = "'{$fixtureInc}." . Inflector::underscore($class) ."'"; + } + } + } + } + $fixture = join(", ", $fixture); + + $import = $className; + if (isset($this->plugin)) { + $import = $this->plugin . '.' . $className; + } + + $out = "App::import('Model', '$import');\n\n"; + $out .= "class {$className}TestCase extends CakeTestCase {\n"; + $out .= "\tvar \${$className} = null;\n"; + $out .= "\tvar \$fixtures = array($fixture);\n\n"; + $out .= "\tfunction startTest() {\n"; + $out .= "\t\t\$this->{$className} =& ClassRegistry::init('{$className}');\n"; + $out .= "\t}\n\n"; + $out .= "\tfunction test{$className}Instance() {\n"; + $out .= "\t\t\$this->assertTrue(is_a(\$this->{$className}, '{$className}'));\n"; + $out .= "\t}\n\n"; + $out .= "\tfunction test{$className}Find() {\n"; + $out .= "\t\t\$this->{$className}->recursive = -1;\n"; + $out .= "\t\t\$results = \$this->{$className}->find('first');\n\t\t\$this->assertTrue(!empty(\$results));\n\n"; + $out .= "\t\t\$expected = array('$className' => array(\n$results\n\t\t));\n"; + $out .= "\t\t\$this->assertEqual(\$results, \$expected);\n"; + $out .= "\t}\n"; + $out .= "}\n"; + + $path = MODEL_TESTS; + if (isset($this->plugin)) { + $pluginPath = 'plugins' . DS . Inflector::underscore($this->plugin) . DS; + $path = APP . $pluginPath . 'tests' . DS . 'cases' . DS . 'models' . DS; + } + + $filename = Inflector::underscore($className).'.test.php'; + $this->out("\nBaking unit test for $className..."); + + $header = '$Id'; + $content = "<?php \n/* SVN FILE: $header$ */\n/* " . $className . " Test cases generated on: " . date('Y-m-d H:i:s') . " : " . time() . "*/\n{$out}?>"; + return $this->createFile($path . $filename, $content); + } + return false; + } +/** + * outputs the a list of possible models or controllers from database + * + * @param string $useDbConfig Database configuration name + * @access public + */ + function listAll($useDbConfig = 'default', $interactive = true) { + $db =& ConnectionManager::getDataSource($useDbConfig); + $usePrefix = empty($db->config['prefix']) ? '' : $db->config['prefix']; + if ($usePrefix) { + $tables = array(); + foreach ($db->listSources() as $table) { + if (!strncmp($table, $usePrefix, strlen($usePrefix))) { + $tables[] = substr($table, strlen($usePrefix)); + } + } + } else { + $tables = $db->listSources(); + } + if (empty($tables)) { + $this->err(__('Your database does not have any tables.', true)); + $this->_stop(); + } + + $this->__tables = $tables; + + if ($interactive === true) { + $this->out(__('Possible Models based on your current database:', true)); + $this->_modelNames = array(); + $count = count($tables); + for ($i = 0; $i < $count; $i++) { + $this->_modelNames[] = $this->_modelName($tables[$i]); + $this->out($i + 1 . ". " . $this->_modelNames[$i]); + } + } + } +/** + * Forces the user to specify the model he wants to bake, and returns the selected model name. + * + * @return string the model name + * @access public + */ + function getName($useDbConfig) { + $this->listAll($useDbConfig); + + $enteredModel = ''; + + while ($enteredModel == '') { + $enteredModel = $this->in(__("Enter a number from the list above, type in the name of another model, or 'q' to exit", true), null, 'q'); + + if ($enteredModel === 'q') { + $this->out(__("Exit", true)); + $this->_stop(); + } + + if ($enteredModel == '' || intval($enteredModel) > count($this->_modelNames)) { + $this->err(__("The model name you supplied was empty, or the number you selected was not an option. Please try again.", true)); + $enteredModel = ''; + } + } + + if (intval($enteredModel) > 0 && intval($enteredModel) <= count($this->_modelNames)) { + $currentModelName = $this->_modelNames[intval($enteredModel) - 1]; + } else { + $currentModelName = $enteredModel; + } + + return $currentModelName; + } +/** + * Displays help contents + * + * @access public + */ + function help() { + $this->hr(); + $this->out("Usage: cake bake model <arg1>"); + $this->hr(); + $this->out('Commands:'); + $this->out("\n\tmodel\n\t\tbakes model in interactive mode."); + $this->out("\n\tmodel <name>\n\t\tbakes model file with no associations or validation"); + $this->out(""); + $this->_stop(); + } +/** + * Builds the tests fixtures for the model and create the file + * + * @param string $model the name of the model + * @param string $useTable table name + * @return array $records, used in ModelTask::bakeTest() to create $expected + * @todo move this to a task + */ + function fixture($model, $useTable = null) { + if (!class_exists('CakeSchema')) { + App::import('Model', 'Schema'); + } + $out = "\nclass {$model}Fixture extends CakeTestFixture {\n"; + $out .= "\tvar \$name = '$model';\n"; + + if (!$useTable) { + $useTable = Inflector::tableize($model); + } else { + $out .= "\tvar \$table = '$useTable';\n"; + } + $schema = new CakeSchema(); + $data = $schema->read(array('models' => false)); + + if (!isset($data['tables'][$useTable])) { + return false; + } + $tables[$model] = $data['tables'][$useTable]; + + foreach ($tables as $table => $fields) { + if (!is_numeric($table) && $table !== 'missing') { + $out .= "\tvar \$fields = array(\n"; + $records = array(); + if (is_array($fields)) { + $cols = array(); + foreach ($fields as $field => $value) { + if ($field != 'indexes') { + if (is_string($value)) { + $type = $value; + $value = array('type'=> $type); + } + $col = "\t\t'{$field}' => array('type'=>'" . $value['type'] . "', "; + + switch ($value['type']) { + case 'integer': + $insert = 1; + break; + case 'string'; + $insert = "Lorem ipsum dolor sit amet"; + if (!empty($value['length'])) { + $insert = substr($insert, 0, (int)$value['length'] - 2); + } + $insert = "'$insert'"; + break; + case 'datetime': + $ts = date('Y-m-d H:i:s'); + $insert = "'$ts'"; + break; + case 'date': + $ts = date('Y-m-d'); + $insert = "'$ts'"; + break; + case 'time': + $ts = date('H:i:s'); + $insert = "'$ts'"; + break; + case 'boolean': + $insert = 1; + break; + case 'text': + $insert = + "'Lorem ipsum dolor sit amet, aliquet feugiat. Convallis morbi fringilla gravida,"; + $insert .= "phasellus feugiat dapibus velit nunc, pulvinar eget sollicitudin venenatis cum nullam,"; + $insert .= "vivamus ut a sed, mollitia lectus. Nulla vestibulum massa neque ut et, id hendrerit sit,"; + $insert .= "feugiat in taciti enim proin nibh, tempor dignissim, rhoncus duis vestibulum nunc mattis convallis.'"; + break; + } + $records[] = "\t\t'$field' => $insert"; + unset($value['type']); + $col .= join(', ', $schema->__values($value)); + } else { + $col = "\t\t'indexes' => array("; + $props = array(); + foreach ((array)$value as $key => $index) { + $props[] = "'{$key}' => array(" . join(', ', $schema->__values($index)) . ")"; + } + $col .= join(', ', $props); + } + $col .= ")"; + $cols[] = $col; + } + $out .= join(",\n", $cols); + } + $out .= "\n\t);\n"; + } + } + $records = join(",\n", $records); + $out .= "\tvar \$records = array(array(\n$records\n\t));\n"; + $out .= "}\n"; + $path = TESTS . DS . 'fixtures' . DS; + if (isset($this->plugin)) { + $pluginPath = 'plugins' . DS . Inflector::underscore($this->plugin) . DS; + $path = APP . $pluginPath . 'tests' . DS . 'fixtures' . DS; + } + $filename = Inflector::underscore($model) . '_fixture.php'; + $header = '$Id'; + $content = "<?php \n/* SVN FILE: $header$ */\n/* " . $model . " Fixture generated on: " . date('Y-m-d H:i:s') . " : " . time() . "*/\n{$out}?>"; + $this->out("\nBaking test fixture for $model..."); + if ($this->createFile($path . $filename, $content)) { + return str_replace("\t\t", "\t\t\t", $records); + } + return false; + } +} +?> \ No newline at end of file diff --git a/cake/console/libs/tasks/plugin.php b/cake/console/libs/tasks/plugin.php new file mode 100755 index 0000000..a231813 --- /dev/null +++ b/cake/console/libs/tasks/plugin.php @@ -0,0 +1,202 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * The Plugin Task handles creating an empty plugin, ready to be used + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.console.libs.tasks + * @since CakePHP(tm) v 1.2 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +if (!class_exists('File')) { + uses('file'); +} +/** + * Task class for creating a plugin + * + * @package cake + * @subpackage cake.cake.console.libs.tasks + */ +class PluginTask extends Shell { +/** + * Tasks + * + */ + var $tasks = array('Model', 'Controller', 'View'); +/** + * path to CONTROLLERS directory + * + * @var array + * @access public + */ + var $path = null; +/** + * initialize + * + * @return void + */ + function initialize() { + $this->path = APP . 'plugins' . DS; + } +/** + * Execution method always used for tasks + * + * @return void + */ + function execute() { + if (empty($this->params['skel'])) { + $this->params['skel'] = ''; + if (is_dir(CAKE_CORE_INCLUDE_PATH.DS.'cake'.DS.'console'.DS.'libs'.DS.'templates'.DS.'skel') === true) { + $this->params['skel'] = CAKE_CORE_INCLUDE_PATH.DS.'cake'.DS.'console'.DS.'libs'.DS.'templates'.DS.'skel'; + } + } + + $plugin = null; + + if (isset($this->args[0])) { + $plugin = Inflector::camelize($this->args[0]); + $pluginPath = Inflector::underscore($plugin) . DS; + $this->Dispatch->shiftArgs(); + if (is_dir($this->path . $pluginPath)) { + $this->out(sprintf('Plugin: %s', $plugin)); + $this->out(sprintf('Path: %s', $this->path . $pluginPath)); + $this->hr(); + } elseif (isset($this->args[0])) { + $this->err(sprintf('%s in path %s not found.', $plugin, $this->path . $pluginPath)); + $this->_stop(); + } else { + $this->__interactive($plugin); + } + } + + if (isset($this->args[0])) { + $task = Inflector::classify($this->args[0]); + $this->Dispatch->shiftArgs(); + if (in_array($task, $this->tasks)) { + $this->{$task}->plugin = $plugin; + $this->{$task}->path = $this->path . $pluginPath . Inflector::underscore(Inflector::pluralize($task)) . DS; + + if (!is_dir($this->{$task}->path)) { + $this->err(sprintf(__("%s directory could not be found.\nBe sure you have created %s", true), $task, $this->{$task}->path)); + } + $this->{$task}->loadTasks(); + $this->{$task}->execute(); + } + } + } + +/** + * Interactive interface + * + * @access private + * @return void + */ + function __interactive($plugin = null) { + while ($plugin === null) { + $plugin = $this->in(__('Enter the name of the plugin in CamelCase format', true)); + } + + if (!$this->bake($plugin)) { + $this->err(sprintf(__("An error occured trying to bake: %s in %s", true), $plugin, $this->path . $pluginPath)); + } + } + +/** + * Bake the plugin, create directories and files + * + * @params $plugin name of the plugin in CamelCased format + * @access public + * @return bool + */ + function bake($plugin) { + + $pluginPath = Inflector::underscore($plugin); + + $this->hr(); + $this->out("Plugin Name: $plugin"); + $this->out("Plugin Directory: {$this->path}{$pluginPath}"); + $this->hr(); + + + $looksGood = $this->in('Look okay?', array('y', 'n', 'q'), 'y'); + + if (strtolower($looksGood) == 'y' || strtolower($looksGood) == 'yes') { + $verbose = $this->in(__('Do you want verbose output?', true), array('y', 'n'), 'n'); + + $Folder = new Folder($this->path . $pluginPath); + $directories = array('models' . DS . 'behaviors', 'controllers' . DS . 'components', 'views' . DS . 'helpers'); + + foreach ($directories as $directory) { + $Folder->create($this->path . $pluginPath . DS . $directory); + } + + if (strtolower($verbose) == 'y' || strtolower($verbose) == 'yes') { + foreach ($Folder->messages() as $message) { + $this->out($message); + } + } + + $errors = $Folder->errors(); + if (!empty($errors)) { + return false; + } + + $controllerFileName = $pluginPath . '_app_controller.php'; + + $out = "<?php\n\n"; + $out .= "class {$plugin}AppController extends AppController {\n\n"; + $out .= "}\n\n"; + $out .= "?>"; + $this->createFile($this->path . $pluginPath. DS . $controllerFileName, $out); + + $modelFileName = $pluginPath . '_app_model.php'; + + $out = "<?php\n\n"; + $out .= "class {$plugin}AppModel extends AppModel {\n\n"; + $out .= "}\n\n"; + $out .= "?>"; + $this->createFile($this->path . $pluginPath . DS . $modelFileName, $out); + + $this->hr(); + $this->out(sprintf(__("Created: %s in %s", true), $plugin, $this->path . $pluginPath)); + $this->hr(); + } + + return true; + } +/** + * Help + * + * @return void + * @access public + */ + function help() { + $this->hr(); + $this->out("Usage: cake bake plugin <arg1> <arg2>..."); + $this->hr(); + $this->out('Commands:'); + $this->out("\n\tplugin <name>\n\t\tbakes plugin directory structure"); + $this->out("\n\tplugin <name> model\n\t\tbakes model. Run 'cake bake model help' for more info."); + $this->out("\n\tplugin <name> controller\n\t\tbakes controller. Run 'cake bake controller help' for more info."); + $this->out("\n\tplugin <name> view\n\t\tbakes view. Run 'cake bake view help' for more info."); + $this->out(""); + $this->_stop(); + } +} +?> \ No newline at end of file diff --git a/cake/console/libs/tasks/project.php b/cake/console/libs/tasks/project.php new file mode 100755 index 0000000..f7a0c30 --- /dev/null +++ b/cake/console/libs/tasks/project.php @@ -0,0 +1,282 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * The Project Task handles creating the base application + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.scripts.bake + * @since CakePHP(tm) v 1.2 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +if (!class_exists('File')) { + uses('file'); +} +/** + * Task class for creating new project apps and plugins + * + * @package cake + * @subpackage cake.cake.console.libs.tasks + */ +class ProjectTask extends Shell { +/** + * Checks that given project path does not already exist, and + * finds the app directory in it. Then it calls bake() with that information. + * + * @param string $project Project path + * @access public + */ + function execute($project = null) { + if ($project === null) { + if (isset($this->args[0])) { + $project = $this->args[0]; + $this->Dispatch->shiftArgs(); + } + } + + if ($project) { + $this->Dispatch->parseParams(array('-app', $project)); + $project = $this->params['working']; + } + + if (empty($this->params['skel'])) { + $this->params['skel'] = ''; + if (is_dir(CAKE_CORE_INCLUDE_PATH.DS.'cake'.DS.'console'.DS.'libs'.DS.'templates'.DS.'skel') === true) { + $this->params['skel'] = CAKE_CORE_INCLUDE_PATH.DS.'cake'.DS.'console'.DS.'libs'.DS.'templates'.DS.'skel'; + } + } + + while (!$project) { + $project = $this->in("What is the full path for this app including the app directory name?\nExample: ".$this->params['working'] . DS . "myapp", null, $this->params['working'] . DS . 'myapp'); + } + + if ($project) { + $response = false; + while ($response == false && is_dir($project) === true && file_exists($project . 'config' . 'core.php')) { + $response = $this->in('A project already exists in this location: '.$project.' Overwrite?', array('y','n'), 'n'); + if (strtolower($response) === 'n') { + $response = $project = false; + } + } + } + + if ($this->bake($project)) { + $path = Folder::slashTerm($project); + if ($this->createHome($path)) { + $this->out(__('Welcome page created', true)); + } else { + $this->out(__('The Welcome page was NOT created', true)); + } + + if ($this->securitySalt($path) === true ) { + $this->out(__('Random hash key created for \'Security.salt\'', true)); + } else { + $this->err(sprintf(__('Unable to generate random hash for \'Security.salt\', you should change it in %s', true), CONFIGS . 'core.php')); + } + + $corePath = $this->corePath($path); + if ($corePath === true ) { + $this->out(sprintf(__('CAKE_CORE_INCLUDE_PATH set to %s in webroot/index.php', true), CAKE_CORE_INCLUDE_PATH)); + $this->out(sprintf(__('CAKE_CORE_INCLUDE_PATH set to %s in webroot/test.php', true), CAKE_CORE_INCLUDE_PATH)); + $this->out(__('Remember to check these value after moving to production server', true)); + } elseif ($corePath === false) { + $this->err(sprintf(__('Unable to set CAKE_CORE_INCLUDE_PATH, you should change it in %s', true), $path . 'webroot' .DS .'index.php')); + } + $Folder = new Folder($path); + if (!$Folder->chmod($path . 'tmp', 0777)) { + $this->err(sprintf(__('Could not set permissions on %s', true), $path . DS .'tmp')); + $this->out(sprintf(__('chmod -R 0777 %s', true), $path . DS .'tmp')); + } + + $this->params['working'] = $path; + $this->params['app'] = basename($path); + return true; + } + } +/** + * Looks for a skeleton template of a Cake application, + * and if not found asks the user for a path. When there is a path + * this method will make a deep copy of the skeleton to the project directory. + * A default home page will be added, and the tmp file storage will be chmod'ed to 0777. + * + * @param string $path Project path + * @param string $skel Path to copy from + * @param string $skip array of directories to skip when copying + * @access private + */ + function bake($path, $skel = null, $skip = array('empty')) { + if (!$skel) { + $skel = $this->params['skel']; + } + + while (!$skel) { + $skel = $this->in(sprintf(__("What is the path to the directory layout you wish to copy?\nExample: %s"), APP, null, ROOT . DS . 'myapp' . DS)); + if ($skel == '') { + $this->out(__('The directory path you supplied was empty. Please try again.', true)); + } else { + while (is_dir($skel) === false) { + $skel = $this->in(__('Directory path does not exist please choose another:', true)); + } + } + } + + $app = basename($path); + + $this->out('Bake Project'); + $this->out("Skel Directory: $skel"); + $this->out("Will be copied to: {$path}"); + $this->hr(); + + $looksGood = $this->in('Look okay?', array('y', 'n', 'q'), 'y'); + + if (strtolower($looksGood) == 'y' || strtolower($looksGood) == 'yes') { + $verbose = $this->in(__('Do you want verbose output?', true), array('y', 'n'), 'n'); + + $Folder = new Folder($skel); + if ($Folder->copy(array('to' => $path, 'skip' => $skip))) { + $this->hr(); + $this->out(sprintf(__("Created: %s in %s", true), $app, $path)); + $this->hr(); + } else { + $this->err(" '" . $app . "' could not be created properly"); + return false; + } + + if (strtolower($verbose) == 'y' || strtolower($verbose) == 'yes') { + foreach ($Folder->messages() as $message) { + $this->out($message); + } + } + + return true; + } elseif (strtolower($looksGood) == 'q' || strtolower($looksGood) == 'quit') { + $this->out('Bake Aborted.'); + } else { + $this->execute(false); + return false; + } + } +/** + * Writes a file with a default home page to the project. + * + * @param string $dir Path to project + * @return boolean Success + * @access public + */ + function createHome($dir) { + $app = basename($dir); + $path = $dir . 'views' . DS . 'pages' . DS; + include(CAKE_CORE_INCLUDE_PATH.DS.'cake'.DS.'console'.DS.'libs'.DS.'templates'.DS.'views'.DS.'home.ctp'); + return $this->createFile($path.'home.ctp', $output); + } +/** + * Generates and writes 'Security.salt' + * + * @param string $path Project path + * @return boolean Success + * @access public + */ + function securitySalt($path) { + $File =& new File($path . 'config' . DS . 'core.php'); + $contents = $File->read(); + if (preg_match('/([\\t\\x20]*Configure::write\\(\\\'Security.salt\\\',[\\t\\x20\'A-z0-9]*\\);)/', $contents, $match)) { + if (!class_exists('Security')) { + uses('Security'); + } + $string = Security::generateAuthKey(); + $result = str_replace($match[0], "\t" . 'Configure::write(\'Security.salt\', \''.$string.'\');', $contents); + if ($File->write($result)) { + return true; + } + return false; + } + return false; + } +/** + * Generates and writes CAKE_CORE_INCLUDE_PATH + * + * @param string $path Project path + * @return boolean Success + * @access public + */ + function corePath($path) { + if (dirname($path) !== CAKE_CORE_INCLUDE_PATH) { + $File =& new File($path . 'webroot' . DS . 'index.php'); + $contents = $File->read(); + if (preg_match('/([\\t\\x20]*define\\(\\\'CAKE_CORE_INCLUDE_PATH\\\',[\\t\\x20\'A-z0-9]*\\);)/', $contents, $match)) { + $result = str_replace($match[0], "\t\tdefine('CAKE_CORE_INCLUDE_PATH', '" . CAKE_CORE_INCLUDE_PATH . "');", $contents); + if (!$File->write($result)) { + return false; + } + } else { + return false; + } + + $File =& new File($path . 'webroot' . DS . 'test.php'); + $contents = $File->read(); + if (preg_match('/([\\t\\x20]*define\\(\\\'CAKE_CORE_INCLUDE_PATH\\\',[\\t\\x20\'A-z0-9]*\\);)/', $contents, $match)) { + $result = str_replace($match[0], "\t\tdefine('CAKE_CORE_INCLUDE_PATH', '" . CAKE_CORE_INCLUDE_PATH . "');", $contents); + if (!$File->write($result)) { + return false; + } + } else { + return false; + } + return true; + } + } +/** + * Enables Configure::read('Routing.admin') in /app/config/core.php + * + * @param string $name Name to use as admin routing + * @return boolean Success + * @access public + */ + function cakeAdmin($name) { + $File =& new File(CONFIGS . 'core.php'); + $contents = $File->read(); + if (preg_match('%([/\\t\\x20]*Configure::write\(\'Routing.admin\',[\\t\\x20\'a-z]*\\);)%', $contents, $match)) { + $result = str_replace($match[0], "\t" . 'Configure::write(\'Routing.admin\', \''.$name.'\');', $contents); + if ($File->write($result)) { + Configure::write('Routing.admin', $name); + return true; + } else { + return false; + } + } else { + return false; + } + } +/** + * Help + * + * @return void + * @access public + */ + function help() { + $this->hr(); + $this->out("Usage: cake bake project <arg1>"); + $this->hr(); + $this->out('Commands:'); + $this->out("\n\tproject <name>\n\t\tbakes app directory structure.\n\t\tif <name> begins with '/' path is absolute."); + $this->out(""); + $this->_stop(); + } + +} +?> \ No newline at end of file diff --git a/cake/console/libs/tasks/test.php b/cake/console/libs/tasks/test.php new file mode 100755 index 0000000..416d3bd --- /dev/null +++ b/cake/console/libs/tasks/test.php @@ -0,0 +1,203 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * The TestTask handles creating and updating test files. + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.console.libs.tasks + * @since CakePHP(tm) v 1.2 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Task class for creating and updating test files. + * + * @package cake + * @subpackage cake.cake.console.libs.tasks + */ +class TestTask extends Shell { +/** + * Name of plugin + * + * @var string + * @access public + */ + var $plugin = null; +/** + * path to TESTS directory + * + * @var string + * @access public + */ + var $path = TESTS; +/** + * Execution method always used for tasks + * + * @access public + */ + function execute() { + if (empty($this->args)) { + $this->__interactive(); + } + + if (count($this->args) == 1) { + $this->__interactive($this->args[0]); + } + + if (count($this->args) > 1) { + $class = Inflector::underscore($this->args[0]); + if ($this->bake($class, $this->args[1])) { + $this->out('done'); + } + } + } +/** + * Handles interactive baking + * + * @access private + */ + function __interactive($class = null) { + $this->hr(); + $this->out(sprintf("Bake Tests\nPath: %s", $this->path)); + $this->hr(); + + $key = null; + $options = array('Behavior', 'Helper', 'Component', 'Model', 'Controller'); + + if ($class !== null) { + $class = Inflector::camelize($class); + if (in_array($class, $options)) { + $key = array_search($class); + } + } + + while ($class == null) { + $cases = array(); + $this->hr(); + $this->out("Select a class:"); + $this->hr(); + + $keys = array(); + foreach ($options as $key => $option) { + $this->out(++$key . '. ' . $option); + $keys[] = $key; + } + $keys[] = 'q'; + + $key = $this->in(__("Enter the class to test or (q)uit", true), $keys, 'q'); + + if ($key != 'q') { + if (isset($options[--$key])) { + $class = $options[$key]; + } + + if ($class) { + $name = $this->in(__("Enter the name for the test or (q)uit", true), null, 'q'); + if ($name !== 'q') { + $case = null; + while ($case !== 'q') { + $case = $this->in(__("Enter a test case or (q)uit", true), null, 'q'); + if ($case !== 'q') { + $cases[] = $case; + } + } + if ($this->bake($class, $name, $cases)) { + $this->out(__("Test baked\n", true)); + $type = null; + } + $class = null; + } + } + } else { + $this->_stop(); + } + } + } +/** + * Writes File + * + * @access public + */ + function bake($class, $name = null, $cases = array()) { + if (!$name) { + return false; + } + + if (!is_array($cases)) { + $cases = array($cases); + } + + if (strpos($this->path, $class) === false) { + $this->filePath = $this->path . 'cases' . DS . Inflector::tableize($class) . DS; + } + + $class = Inflector::classify($class); + $name = Inflector::classify($name); + + $import = $name; + if (isset($this->plugin)) { + $import = $this->plugin . '.' . $name; + } + $extras = $this->__extras($class); + $out = "App::import('$class', '$import');\n"; + if ($class == 'Model') { + $class = null; + } + $out .= "class Test{$name} extends {$name}{$class} {\n"; + $out .= "{$extras}"; + $out .= "}\n\n"; + $out .= "class {$name}{$class}Test extends CakeTestCase {\n"; + $out .= "\n\tfunction startTest() {"; + $out .= "\n\t\t\$this->{$name} = new Test{$name}();"; + $out .= "\n\t}\n"; + $out .= "\n\tfunction test{$name}Instance() {\n"; + $out .= "\t\t\$this->assertTrue(is_a(\$this->{$name}, '{$name}{$class}'));\n\t}\n"; + foreach ($cases as $case) { + $case = Inflector::classify($case); + $out .= "\n\tfunction test{$case}() {\n\n\t}\n"; + } + $out .= "}\n"; + + $this->out("Baking unit test for $name..."); + $this->out($out); + $ok = $this->in(__('Is this correct?', true), array('y', 'n'), 'y'); + if ($ok == 'n') { + return false; + } + + $header = '$Id'; + $content = "<?php \n/* SVN FILE: $header$ */\n/* " . $name . " Test cases generated on: " . date('Y-m-d H:i:s') . " : ". time() . "*/\n{$out}?>"; + return $this->createFile($this->filePath . Inflector::underscore($name) . '.test.php', $content); + } +/** + * Handles the extra stuff needed + * + * @access private + */ + function __extras($class) { + $extras = null; + switch ($class) { + case 'Model': + $extras = "\n\tvar \$cacheSources = false;"; + $extras .= "\n\tvar \$useDbConfig = 'test_suite';\n"; + break; + } + return $extras; + } +} +?> \ No newline at end of file diff --git a/cake/console/libs/tasks/view.php b/cake/console/libs/tasks/view.php new file mode 100755 index 0000000..cd60df6 --- /dev/null +++ b/cake/console/libs/tasks/view.php @@ -0,0 +1,389 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * The View Tasks handles creating and updating view files. + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.console.libs.tasks + * @since CakePHP(tm) v 1.2 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +App::import('Core', 'Controller'); +/** + * Task class for creating and updating view files. + * + * @package cake + * @subpackage cake.cake.console.libs.tasks + */ +class ViewTask extends Shell { +/** + * Name of plugin + * + * @var string + * @access public + */ + var $plugin = null; +/** + * Tasks to be loaded by this Task + * + * @var array + * @access public + */ + var $tasks = array('Project', 'Controller'); +/** + * path to VIEWS directory + * + * @var array + * @access public + */ + var $path = VIEWS; +/** + * Name of the controller being used + * + * @var string + * @access public + */ + var $controllerName = null; +/** + * Path to controller to put views + * + * @var string + * @access public + */ + var $controllerPath = null; +/** + * The template file to use + * + * @var string + * @access public + */ + var $template = null; +/** + * Actions to use for scaffolding + * + * @var array + * @access public + */ + var $scaffoldActions = array('index', 'view', 'add', 'edit'); +/** + * Override initialize + * + * @access public + */ + function initialize() { + } +/** + * Execution method always used for tasks + * + * @access public + */ + function execute() { + if (empty($this->args)) { + $this->__interactive(); + } + + if (isset($this->args[0])) { + $controller = $action = $alias = null; + $this->controllerName = Inflector::camelize($this->args[0]); + $this->controllerPath = Inflector::underscore($this->controllerName); + + if (isset($this->args[1])) { + $this->template = $this->args[1]; + } + + if (isset($this->args[2])) { + $action = $this->args[2]; + } + + if (!$action) { + $action = $this->template; + } + + if (in_array($action, $this->scaffoldActions)) { + $this->bake($action, true); + } elseif ($action) { + $this->bake($action, true); + } else { + $vars = $this->__loadController(); + if ($vars) { + + $methods = array_diff( + array_map('strtolower', get_class_methods($this->controllerName . 'Controller')), + array_map('strtolower', get_class_methods('appcontroller')) + ); + if (empty($methods)) { + $methods = $this->scaffoldActions; + } + $adminDelete = null; + + $adminRoute = Configure::read('Routing.admin'); + if (!empty($adminRoute)) { + $adminDelete = $adminRoute.'_delete'; + } + foreach ($methods as $method) { + if ($method{0} != '_' && !in_array($method, array('delete', $adminDelete))) { + $content = $this->getContent($method, $vars); + $this->bake($method, $content); + } + } + } + } + } + } +/** + * Handles interactive baking + * + * @access private + */ + function __interactive() { + $this->hr(); + $this->out(sprintf("Bake View\nPath: %s", $this->path)); + $this->hr(); + $wannaDoAdmin = 'n'; + $wannaDoScaffold = 'y'; + $this->interactive = false; + + $this->controllerName = $this->Controller->getName(); + + $this->controllerPath = strtolower(Inflector::underscore($this->controllerName)); + + $interactive = $this->in("Would you like bake to build your views interactively?\nWarning: Choosing no will overwrite {$this->controllerName} views if it exist.", array('y','n'), 'y'); + + if (strtolower($interactive) == 'y' || strtolower($interactive) == 'yes') { + $this->interactive = true; + $wannaDoScaffold = $this->in("Would you like to create some scaffolded views (index, add, view, edit) for this controller?\nNOTE: Before doing so, you'll need to create your controller and model classes (including associated models).", array('y','n'), 'n'); + } + + if (strtolower($wannaDoScaffold) == 'y' || strtolower($wannaDoScaffold) == 'yes') { + $wannaDoAdmin = $this->in("Would you like to create the views for admin routing?", array('y','n'), 'y'); + } + $admin = false; + + if ((strtolower($wannaDoAdmin) == 'y' || strtolower($wannaDoAdmin) == 'yes')) { + $admin = $this->getAdmin(); + } + + if (strtolower($wannaDoScaffold) == 'y' || strtolower($wannaDoScaffold) == 'yes') { + $actions = $this->scaffoldActions; + if ($admin) { + foreach ($actions as $action) { + $actions[] = $admin . $action; + } + } + $vars = $this->__loadController(); + if ($vars) { + foreach ($actions as $action) { + $content = $this->getContent($action, $vars); + $this->bake($action, $content); + } + } + $this->hr(); + $this->out(''); + $this->out('View Scaffolding Complete.'."\n"); + } else { + $action = ''; + while ($action == '') { + $action = $this->in('Action Name? (use camelCased function name)'); + if ($action == '') { + $this->out('The action name you supplied was empty. Please try again.'); + } + } + $this->out(''); + $this->hr(); + $this->out('The following view will be created:'); + $this->hr(); + $this->out("Controller Name: {$this->controllerName}"); + $this->out("Action Name: {$action}"); + $this->out("Path: ".$this->params['app'] . DS . $this->controllerPath . DS . Inflector::underscore($action) . ".ctp"); + $this->hr(); + $looksGood = $this->in('Look okay?', array('y','n'), 'y'); + if (strtolower($looksGood) == 'y' || strtolower($looksGood) == 'yes') { + $this->bake($action); + $this->_stop(); + } else { + $this->out('Bake Aborted.'); + } + } + } +/** + * Loads Controller and sets variables for the template + * Available template variables + * 'modelClass', 'primaryKey', 'displayField', 'singularVar', 'pluralVar', + * 'singularHumanName', 'pluralHumanName', 'fields', 'foreignKeys', + * 'belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany' + * + * @return array Returns an variables to be made available to a view template + * @access private + */ + function __loadController() { + if (!$this->controllerName) { + $this->err(__('Controller not found', true)); + } + + $import = $this->controllerName; + if ($this->plugin) { + $import = $this->plugin . '.' . $this->controllerName; + } + + if (!App::import('Controller', $import)) { + $file = $this->controllerPath . '_controller.php'; + $this->err(sprintf(__("The file '%s' could not be found.\nIn order to bake a view, you'll need to first create the controller.", true), $file)); + $this->_stop(); + } + $controllerClassName = $this->controllerName . 'Controller'; + $controllerObj = & new $controllerClassName(); + $controllerObj->constructClasses(); + $modelClass = $controllerObj->modelClass; + $modelObj =& ClassRegistry::getObject($controllerObj->modelKey); + + if ($modelObj) { + $primaryKey = $modelObj->primaryKey; + $displayField = $modelObj->displayField; + $singularVar = Inflector::variable($modelClass); + $pluralVar = Inflector::variable($this->controllerName); + $singularHumanName = Inflector::humanize($modelClass); + $pluralHumanName = Inflector::humanize($this->controllerName); + $schema = $modelObj->schema(); + $fields = array_keys($schema); + $associations = $this->__associations($modelObj); + } else { + $primaryKey = null; + $displayField = null; + $singularVar = Inflector::variable(Inflector::singularize($this->controllerName)); + $pluralVar = Inflector::variable($this->controllerName); + $singularHumanName = Inflector::humanize(Inflector::singularize($this->controllerName)); + $pluralHumanName = Inflector::humanize($this->controllerName); + $fields = array(); + $schema = array(); + $associations = array(); + } + + return compact('modelClass', 'schema', 'primaryKey', 'displayField', 'singularVar', 'pluralVar', + 'singularHumanName', 'pluralHumanName', 'fields','associations'); + } +/** + * Assembles and writes bakes the view file. + * + * @param string $action Action to bake + * @param string $content Content to write + * @return boolean Success + * @access public + */ + function bake($action, $content = '') { + if ($content === true) { + $content = $this->getContent(); + } + $filename = $this->path . $this->controllerPath . DS . Inflector::underscore($action) . '.ctp'; + $Folder =& new Folder($this->path . $this->controllerPath, true); + $errors = $Folder->errors(); + if (empty($errors)) { + $path = $Folder->slashTerm($Folder->pwd()); + return $this->createFile($filename, $content); + } else { + foreach ($errors as $error) { + $this->err($error); + } + } + return false; + } +/** + * Builds content from template and variables + * + * @param string $template file to use + * @param array $vars passed for use in templates + * @return string content from template + * @access public + */ + function getContent($template = null, $vars = null) { + if (!$template) { + $template = $this->template; + } + $action = $template; + + $adminRoute = Configure::read('Routing.admin'); + if (!empty($adminRoute) && strpos($template, $adminRoute) !== false) { + $template = str_replace($adminRoute.'_', '', $template); + } + if (in_array($template, array('add', 'edit'))) { + $action = $template; + $template = 'form'; + } + $loaded = false; + foreach ($this->Dispatch->shellPaths as $path) { + $templatePath = $path . 'templates' . DS . 'views' . DS .Inflector::underscore($template).'.ctp'; + if (file_exists($templatePath) && is_file($templatePath)) { + $loaded = true; + break; + } + } + if (!$vars) { + $vars = $this->__loadController(); + } + if ($loaded) { + extract($vars); + ob_start(); + ob_implicit_flush(0); + include($templatePath); + $content = ob_get_clean(); + return $content; + } + $this->hr(); + $this->err(sprintf(__('Template for %s could not be found', true), $template)); + return false; + } +/** + * Displays help contents + * + * @access public + */ + function help() { + $this->hr(); + $this->out("Usage: cake bake view <arg1> <arg2>..."); + $this->hr(); + $this->out('Commands:'); + $this->out("\n\tview <controller>\n\t\twill read the given controller for methods\n\t\tand bake corresponding views.\n\t\tIf var scaffold is found it will bake the scaffolded actions\n\t\t(index,view,add,edit)"); + $this->out("\n\tview <controller> <action>\n\t\twill bake a template. core templates: (index, add, edit, view)"); + $this->out("\n\tview <controller> <template> <alias>\n\t\twill use the template specified but name the file based on the alias"); + $this->out(""); + $this->_stop(); + } +/** + * Returns associations for controllers models. + * + * @return array $associations + * @access private + */ + function __associations($model) { + $keys = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany'); + $associations = array(); + + foreach ($keys as $key => $type) { + foreach ($model->{$type} as $assocKey => $assocData) { + $associations[$type][$assocKey]['primaryKey'] = $model->{$assocKey}->primaryKey; + $associations[$type][$assocKey]['displayField'] = $model->{$assocKey}->displayField; + $associations[$type][$assocKey]['foreignKey'] = $assocData['foreignKey']; + $associations[$type][$assocKey]['controller'] = Inflector::pluralize(Inflector::underscore($assocData['className'])); + $associations[$type][$assocKey]['fields'] = array_keys($model->{$assocKey}->schema()); + } + } + return $associations; + } +} + +?> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/.htaccess b/cake/console/libs/templates/skel/.htaccess new file mode 100755 index 0000000..0ed8662 --- /dev/null +++ b/cake/console/libs/templates/skel/.htaccess @@ -0,0 +1,5 @@ +<IfModule mod_rewrite.c> + RewriteEngine on + RewriteRule ^$ webroot/ [L] + RewriteRule (.*) webroot/$1 [L] + </IfModule> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/app_controller.php b/cake/console/libs/templates/skel/app_controller.php new file mode 100755 index 0000000..2412447 --- /dev/null +++ b/cake/console/libs/templates/skel/app_controller.php @@ -0,0 +1,39 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Short description for file. + * + * 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://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.app + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Short description for class. + * + * Add your application-wide methods in the class below, your controllers + * will inherit them. + * + * @package cake + * @subpackage cake.app + */ +class AppController extends Controller { +} +?> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/app_helper.php b/cake/console/libs/templates/skel/app_helper.php new file mode 100755 index 0000000..c57f438 --- /dev/null +++ b/cake/console/libs/templates/skel/app_helper.php @@ -0,0 +1,41 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Short description for file. + * + * 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://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +App::import('Core', 'Helper'); +/** + * 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 { +} +?> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/app_model.php b/cake/console/libs/templates/skel/app_model.php new file mode 100755 index 0000000..8093882 --- /dev/null +++ b/cake/console/libs/templates/skel/app_model.php @@ -0,0 +1,41 @@ +<?php +/* SVN FILE: $Id$ */ + +/** + * 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://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.app + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ + +/** + * Application model for Cake. + * + * Add your application-wide methods in the class below, your models + * will inherit them. + * + * @package cake + * @subpackage cake.app + */ +class AppModel extends Model { +} +?> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/config/acl.ini.php b/cake/console/libs/templates/skel/config/acl.ini.php new file mode 100755 index 0000000..a9868e6 --- /dev/null +++ b/cake/console/libs/templates/skel/config/acl.ini.php @@ -0,0 +1,74 @@ +;<?php die() ?> +; SVN FILE: $Id$ +;/** +; * Short description for file. +; * +; * +; * PHP versions 4 and 5 +; * +; * CakePHP(tm) : Rapid Development Framework http://www.cakephp.org/ +; * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) +; * +; * Licensed under The MIT License +; * Redistributions of files must retain the above copyright notice. +; * +; * @filesource +; * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) +; * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project +; * @package cake +; * @subpackage cake.app.config +; * @since CakePHP(tm) v 0.10.0.1076 +; * @version $Revision$ +; * @modifiedby $LastChangedBy$ +; * @lastmodified $Date$ +; * @license http://www.opensource.org/licenses/mit-license.php The MIT License +; */ + +; acl.ini.php - Cake ACL Configuration +; --------------------------------------------------------------------- +; Use this file to specify user permissions. +; aco = access control object (something in your application) +; aro = access request object (something requesting access) +; +; User records are added as follows: +; +; [uid] +; groups = group1, group2, group3 +; allow = aco1, aco2, aco3 +; deny = aco4, aco5, aco6 +; +; Group records are added in a similar manner: +; +; [gid] +; allow = aco1, aco2, aco3 +; deny = aco4, aco5, aco6 +; +; The allow, deny, and groups sections are all optional. +; NOTE: groups names *cannot* ever be the same as usernames! +; +; ACL permissions are checked in the following order: +; 1. Check for user denies (and DENY if specified) +; 2. Check for user allows (and ALLOW if specified) +; 3. Gather user's groups +; 4. Check group denies (and DENY if specified) +; 5. Check group allows (and ALLOW if specified) +; 6. If no aro, aco, or group information is found, DENY +; +; --------------------------------------------------------------------- + +;------------------------------------- +;Users +;------------------------------------- + +[username-goes-here] +groups = group1, group2 +deny = aco1, aco2 +allow = aco3, aco4 + +;------------------------------------- +;Groups +;------------------------------------- + +[groupname-goes-here] +deny = aco5, aco6 +allow = aco7, aco8 \ No newline at end of file diff --git a/cake/console/libs/templates/skel/config/bootstrap.php b/cake/console/libs/templates/skel/config/bootstrap.php new file mode 100755 index 0000000..b817172 --- /dev/null +++ b/cake/console/libs/templates/skel/config/bootstrap.php @@ -0,0 +1,44 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Short description for file. + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.app.config + * @since CakePHP(tm) v 0.10.8.2117 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * + * This file is loaded automatically by the app/webroot/index.php file after the core bootstrap.php is loaded + * This is an application wide file to load any function that is not used within a class define. + * You can also use this to include or require any files in your application. + * + */ +/** + * The settings below can be used to set additional paths to models, views and controllers. + * This is related to Ticket #470 (https://trac.cakephp.org/ticket/470) + * + * $modelPaths = array('full path to models', 'second full path to models', 'etc...'); + * $viewPaths = array('this path to views', 'second full path to views', 'etc...'); + * $controllerPaths = array('this path to controllers', 'second full path to controllers', 'etc...'); + * + */ +//EOF +?> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/config/core.php b/cake/console/libs/templates/skel/config/core.php new file mode 100755 index 0000000..fbd02e7 --- /dev/null +++ b/cake/console/libs/templates/skel/config/core.php @@ -0,0 +1,232 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * This is core configuration file. + * + * Use it to configure core behavior of Cake. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.app.config + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * CakePHP Debug Level: + * + * Production Mode: + * 0: No error messages, errors, or warnings shown. Flash messages redirect. + * + * Development Mode: + * 1: Errors and warnings shown, model caches refreshed, flash messages halted. + * 2: As in 1, but also with full debug messages and SQL output. + * 3: As in 2, but also with full controller dump. + * + * In production mode, flash messages redirect after a time interval. + * In development mode, you need to click the flash message to continue. + */ + Configure::write('debug', 2); +/** + * Application wide charset encoding + */ + Configure::write('App.encoding', 'UTF-8'); +/** + * To configure CakePHP *not* to use mod_rewrite and to + * use CakePHP pretty URLs, remove these .htaccess + * files: + * + * /.htaccess + * /app/.htaccess + * /app/webroot/.htaccess + * + * And uncomment the App.baseUrl below: + */ + //Configure::write('App.baseUrl', env('SCRIPT_NAME')); +/** + * Uncomment the define below to use CakePHP admin routes. + * + * The value of the define determines the name of the route + * and its associated controller actions: + * + * 'admin' -> admin_index() and /admin/controller/index + * 'superuser' -> superuser_index() and /superuser/controller/index + */ + //Configure::write('Routing.admin', 'admin'); + +/** + * Turn off all caching application-wide. + * + */ + //Configure::write('Cache.disable', true); +/** + * Enable cache checking. + * + * If set to true, for view caching you must still use the controller + * var $cacheAction inside your controllers to define caching settings. + * You can either set it controller-wide by setting var $cacheAction = true, + * or in each action using $this->cacheAction = true. + * + */ + //Configure::write('Cache.check', true); +/** + * Defines the default error type when using the log() function. Used for + * differentiating error logging and debugging. Currently PHP supports LOG_DEBUG. + */ + define('LOG_ERROR', 2); +/** + * The preferred session handling method. Valid values: + * + * 'php' Uses settings defined in your php.ini. + * 'cake' Saves session files in CakePHP's /tmp directory. + * 'database' Uses CakePHP's database sessions. + * + * To define a custom session handler, save it at /app/config/<name>.php. + * Set the value of 'Session.save' to <name> to utilize it in CakePHP. + * + * To use database sessions, execute the SQL file found at /app/config/sql/sessions.sql. + * + */ + Configure::write('Session.save', 'php'); +/** + * The name of the table used to store CakePHP database sessions. + * + * 'Session.save' must be set to 'database' in order to utilize this constant. + * + * The table name set here should *not* include any table prefix defined elsewhere. + */ + //Configure::write('Session.table', 'cake_sessions'); +/** + * The DATABASE_CONFIG::$var to use for database session handling. + * + * 'Session.save' must be set to 'database' in order to utilize this constant. + */ + //Configure::write('Session.database', 'default'); +/** + * The name of CakePHP's session cookie. + */ + Configure::write('Session.cookie', 'CAKEPHP'); +/** + * Session time out time (in seconds). + * Actual value depends on 'Security.level' setting. + */ + Configure::write('Session.timeout', '120'); +/** + * If set to false, sessions are not automatically started. + */ + Configure::write('Session.start', true); +/** + * When set to false, HTTP_USER_AGENT will not be checked + * in the session + */ + Configure::write('Session.checkAgent', true); +/** + * The level of CakePHP security. The session timeout time defined + * in 'Session.timeout' is multiplied according to the settings here. + * Valid values: + * + * 'high' Session timeout in 'Session.timeout' x 10 + * 'medium' Session timeout in 'Session.timeout' x 100 + * 'low' Session timeout in 'Session.timeout' x 300 + * + * CakePHP session IDs are also regenerated between requests if + * 'Security.level' is set to 'high'. + */ + Configure::write('Security.level', 'high'); +/** + * A random string used in security hashing methods. + */ + Configure::write('Security.salt', 'DYhG93b0qyJfIxfs2guVoUubWwvniR2G0FgaC9mi'); +/** + * Compress CSS output by removing comments, whitespace, repeating tags, etc. + * This requires a/var/cache directory to be writable by the web server for caching. + * and /vendors/csspp/csspp.php + * + * To use, prefix the CSS link URL with '/ccss/' instead of '/css/' or use HtmlHelper::css(). + */ + //Configure::write('Asset.filter.css', 'css.php'); +/** + * Plug in your own custom JavaScript compressor by dropping a script in your webroot to handle the + * output, and setting the config below to the name of the script. + * + * To use, prefix your JavaScript link URLs with '/cjs/' instead of '/js/' or use JavaScriptHelper::link(). + */ + //Configure::write('Asset.filter.js', 'custom_javascript_output_filter.php'); +/** + * The classname and database used in CakePHP's + * access control lists. + */ + Configure::write('Acl.classname', 'DbAcl'); + Configure::write('Acl.database', 'default'); +/** + * If you are on PHP 5.3 uncomment this line and correct your server timezone + * to fix the date & time related errors. + */ + //date_default_timezone_set('UTC'); +/** + * + * Cache Engine Configuration + * Default settings provided below + * + * File storage engine. + * + * Cache::config('default', array( + * 'engine' => 'File', //[required] + * 'duration'=> 3600, //[optional] + * 'probability'=> 100, //[optional] + * 'path' => CACHE, //[optional] use system tmp directory - remember to use absolute path + * 'prefix' => 'cake_', //[optional] prefix every cache file with this string + * 'lock' => false, //[optional] use file locking + * 'serialize' => true, [optional] + * )); + * + * + * APC (http://pecl.php.net/package/APC) + * + * Cache::config('default', array( + * 'engine' => 'Apc', //[required] + * 'duration'=> 3600, //[optional] + * 'probability'=> 100, //[optional] + * 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string + * )); + * + * Xcache (http://xcache.lighttpd.net/) + * + * Cache::config('default', array( + * 'engine' => 'Xcache', //[required] + * 'duration'=> 3600, //[optional] + * 'probability'=> 100, //[optional] + * 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string + * 'user' => 'user', //user from xcache.admin.user settings + * 'password' => 'password', //plaintext password (xcache.admin.pass) + * )); + * + * + * Memcache (http://www.danga.com/memcached/) + * + * Cache::config('default', array( + * 'engine' => 'Memcache', //[required] + * 'duration'=> 3600, //[optional] + * 'probability'=> 100, //[optional] + * 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string + * 'servers' => array( + * '127.0.0.1:11211' // localhost, default port 11211 + * ), //[optional] + * 'compress' => false, // [optional] compress data in Memcache (slower, but uses less memory) + * )); + * + */ + Cache::config('default', array('engine' => 'File')); +?> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/config/database.php.default b/cake/console/libs/templates/skel/config/database.php.default new file mode 100755 index 0000000..5c20804 --- /dev/null +++ b/cake/console/libs/templates/skel/config/database.php.default @@ -0,0 +1,101 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * This is core configuration file. + * + * Use it to configure core behaviour ofCake. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.app.config + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * In this file you set up your database connection details. + * + * @package cake + * @subpackage cake.config + */ +/** + * Database configuration class. + * You can specify multiple configurations for production, development and testing. + * + * driver => The name of a supported driver; valid options are as follows: + * mysql - MySQL 4 & 5, + * mysqli - MySQL 4 & 5 Improved Interface (PHP5 only), + * sqlite - SQLite (PHP5 only), + * postgres - PostgreSQL 7 and higher, + * mssql - Microsoft SQL Server 2000 and higher, + * db2 - IBM DB2, Cloudscape, and Apache Derby (http://php.net/ibm-db2) + * oracle - Oracle 8 and higher + * firebird - Firebird/Interbase + * sybase - Sybase ASE + * adodb-[drivername] - ADOdb interface wrapper (see below), + * odbc - ODBC DBO driver + * + * You can add custom database drivers (or override existing drivers) by adding the + * appropriate file to app/models/datasources/dbo. Drivers should be named 'dbo_x.php', + * where 'x' is the name of the database. + * + * persistent => true / false + * Determines whether or not the database should use a persistent connection + * + * connect => + * ADOdb set the connect to one of these + * (http://phplens.com/adodb/supported.databases.html) and + * append it '|p' for persistent connection. (mssql|p for example, or just mssql for not persistent) + * For all other databases, this setting is deprecated. + * + * host => + * the host you connect to the database. To add a socket or port number, use 'port' => # + * + * prefix => + * Uses the given prefix for all the tables in this database. This setting can be overridden + * on a per-table basis with the Model::$tablePrefix property. + * + * schema => + * For Postgres and DB2, specifies which schema you would like to use the tables in. Postgres defaults to + * 'public', DB2 defaults to empty. + * + * encoding => + * For MySQL, MySQLi, Postgres and DB2, specifies the character encoding to use when connecting to the + * database. Uses database default. + * + */ +class DATABASE_CONFIG { + + var $default = array( + 'driver' => 'mysql', + 'persistent' => false, + 'host' => 'localhost', + 'login' => 'user', + 'password' => 'password', + 'database' => 'database_name', + 'prefix' => '', + ); + + var $test = array( + 'driver' => 'mysql', + 'persistent' => false, + 'host' => 'localhost', + 'login' => 'user', + 'password' => 'password', + 'database' => 'test_database_name', + 'prefix' => '', + ); +} +?> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/config/inflections.php b/cake/console/libs/templates/skel/config/inflections.php new file mode 100755 index 0000000..ed8c08b --- /dev/null +++ b/cake/console/libs/templates/skel/config/inflections.php @@ -0,0 +1,70 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Custom Inflected Words. + * + * This file is used to hold words that are not matched in the normail Inflector::pluralize() and + * Inflector::singularize() + * + * PHP versions 4 and % + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.app.config + * @since CakePHP(tm) v 1.0.0.2312 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * This is a key => value array of regex used to match words. + * If key matches then the value is returned. + * + * $pluralRules = array('/(s)tatus$/i' => '\1\2tatuses', '/^(ox)$/i' => '\1\2en', '/([m|l])ouse$/i' => '\1ice'); + */ + $pluralRules = array(); +/** + * This is a key only array of plural words that should not be inflected. + * Notice the last comma + * + * $uninflectedPlural = array('.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox'); + */ + $uninflectedPlural = array(); +/** + * This is a key => value array of plural irregular words. + * If key matches then the value is returned. + * + * $irregularPlural = array('atlas' => 'atlases', 'beef' => 'beefs', 'brother' => 'brothers') + */ + $irregularPlural = array(); +/** + * This is a key => value array of regex used to match words. + * If key matches then the value is returned. + * + * $singularRules = array('/(s)tatuses$/i' => '\1\2tatus', '/(matr)ices$/i' =>'\1ix','/(vert|ind)ices$/i') + */ + $singularRules = array(); +/** + * This is a key only array of singular words that should not be inflected. + * You should not have to change this value below if you do change it use same format + * as the $uninflectedPlural above. + */ + $uninflectedSingular = $uninflectedPlural; +/** + * This is a key => value array of singular irregular words. + * Most of the time this will be a reverse of the above $irregularPlural array + * You should not have to change this value below if you do change it use same format + * + * $irregularSingular = array('atlases' => 'atlas', 'beefs' => 'beef', 'brothers' => 'brother') + */ + $irregularSingular = array_flip($irregularPlural); +?> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/config/routes.php b/cake/console/libs/templates/skel/config/routes.php new file mode 100755 index 0000000..b14e435 --- /dev/null +++ b/cake/console/libs/templates/skel/config/routes.php @@ -0,0 +1,39 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Short description for file. + * + * In this file, you set up routes to your controllers and their actions. + * Routes are very important mechanism that allows you to freely connect + * different urls to chosen controllers and their actions (functions). + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.app.config + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Here, we are connecting '/' (base path) to controller called 'Pages', + * its action called 'display', and we pass a param to select the view file + * to use (in this case, /app/views/pages/home.ctp)... + */ + Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home')); +/** + * ...and connect the rest of 'Pages' controller's urls. + */ + Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display')); +?> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/config/sql/db_acl.php b/cake/console/libs/templates/skel/config/sql/db_acl.php new file mode 100755 index 0000000..19434d0 --- /dev/null +++ b/cake/console/libs/templates/skel/config/sql/db_acl.php @@ -0,0 +1,79 @@ +<?php +/* SVN FILE: $Id$ */ +/*DbAcl schema generated on: 2007-11-24 15:11:13 : 1195945453*/ +/** + * This is Acl Schema file + * + * Use it to configure database for ACL + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.app.config.sql + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/* + * + * Using the Schema command line utility + * cake schema run create DbAcl + * + */ +class DbAclSchema extends CakeSchema { + + var $name = 'DbAcl'; + + function before($event = array()) { + return true; + } + + function after($event = array()) { + } + + var $acos = array( + 'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'), + 'parent_id' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 10), + 'model' => array('type'=>'string', 'null' => true), + 'foreign_key' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 10), + 'alias' => array('type'=>'string', 'null' => true), + 'lft' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 10), + 'rght' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 10), + 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)) + ); + + var $aros = array( + 'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'), + 'parent_id' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 10), + 'model' => array('type'=>'string', 'null' => true), + 'foreign_key' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 10), + 'alias' => array('type'=>'string', 'null' => true), + 'lft' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 10), + 'rght' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 10), + 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)) + ); + + var $aros_acos = array( + 'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'), + 'aro_id' => array('type'=>'integer', 'null' => false, 'length' => 10, 'key' => 'index'), + 'aco_id' => array('type'=>'integer', 'null' => false, 'length' => 10), + '_create' => array('type'=>'string', 'null' => false, 'default' => '0', 'length' => 2), + '_read' => array('type'=>'string', 'null' => false, 'default' => '0', 'length' => 2), + '_update' => array('type'=>'string', 'null' => false, 'default' => '0', 'length' => 2), + '_delete' => array('type'=>'string', 'null' => false, 'default' => '0', 'length' => 2), + 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'ARO_ACO_KEY' => array('column' => array('aro_id', 'aco_id'), 'unique' => 1)) + ); + +} +?> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/config/sql/db_acl.sql b/cake/console/libs/templates/skel/config/sql/db_acl.sql new file mode 100755 index 0000000..3b8089b --- /dev/null +++ b/cake/console/libs/templates/skel/config/sql/db_acl.sql @@ -0,0 +1,40 @@ +# $Id$ +# +# Copyright 2005-2008, Cake Software Foundation, Inc. +# +# Licensed under The MIT License +# Redistributions of files must retain the above copyright notice. +# http://www.opensource.org/licenses/mit-license.php The MIT License + +CREATE TABLE acos ( + id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, + parent_id INTEGER(10) DEFAULT NULL, + model VARCHAR(255) DEFAULT '', + foreign_key INTEGER(10) UNSIGNED DEFAULT NULL, + alias VARCHAR(255) DEFAULT '', + lft INTEGER(10) DEFAULT NULL, + rght INTEGER(10) DEFAULT NULL, + PRIMARY KEY (id) +); + +CREATE TABLE aros_acos ( + id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, + aro_id INTEGER(10) UNSIGNED NOT NULL, + aco_id INTEGER(10) UNSIGNED NOT NULL, + _create CHAR(2) NOT NULL DEFAULT 0, + _read CHAR(2) NOT NULL DEFAULT 0, + _update CHAR(2) NOT NULL DEFAULT 0, + _delete CHAR(2) NOT NULL DEFAULT 0, + PRIMARY KEY(id) +); + +CREATE TABLE aros ( + id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, + parent_id INTEGER(10) DEFAULT NULL, + model VARCHAR(255) DEFAULT '', + foreign_key INTEGER(10) UNSIGNED DEFAULT NULL, + alias VARCHAR(255) DEFAULT '', + lft INTEGER(10) DEFAULT NULL, + rght INTEGER(10) DEFAULT NULL, + PRIMARY KEY (id) +); \ No newline at end of file diff --git a/cake/console/libs/templates/skel/config/sql/i18n.php b/cake/console/libs/templates/skel/config/sql/i18n.php new file mode 100755 index 0000000..b8158dd --- /dev/null +++ b/cake/console/libs/templates/skel/config/sql/i18n.php @@ -0,0 +1,56 @@ +<?php +/* SVN FILE: $Id$ */ +/*i18n schema generated on: 2007-11-25 07:11:25 : 1196004805*/ +/** + * This is i18n Schema file + * + * Use it to configure database for i18n + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.app.config.sql + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/* + * + * Using the Schema command line utility + * cake schema run create i18n + * + */ +class i18nSchema extends CakeSchema { + + var $name = 'i18n'; + + function before($event = array()) { + return true; + } + + function after($event = array()) { + } + + var $i18n = array( + 'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'), + 'locale' => array('type'=>'string', 'null' => false, 'length' => 6, 'key' => 'index'), + 'model' => array('type'=>'string', 'null' => false, 'key' => 'index'), + 'foreign_key' => array('type'=>'integer', 'null' => false, 'length' => 10, 'key' => 'index'), + 'field' => array('type'=>'string', 'null' => false, 'key' => 'index'), + 'content' => array('type'=>'text', 'null' => true, 'default' => NULL), + 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'locale' => array('column' => 'locale', 'unique' => 0), 'model' => array('column' => 'model', 'unique' => 0), 'row_id' => array('column' => 'foreign_key', 'unique' => 0), 'field' => array('column' => 'field', 'unique' => 0)) + ); + +} +?> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/config/sql/i18n.sql b/cake/console/libs/templates/skel/config/sql/i18n.sql new file mode 100755 index 0000000..7629b60 --- /dev/null +++ b/cake/console/libs/templates/skel/config/sql/i18n.sql @@ -0,0 +1,26 @@ +# $Id$ +# +# Copyright 2005-2008, Cake Software Foundation, Inc. +# +# Licensed under The MIT License +# Redistributions of files must retain the above copyright notice. +# http://www.opensource.org/licenses/mit-license.php The MIT License + +CREATE TABLE i18n ( + id int(10) NOT NULL auto_increment, + locale varchar(6) NOT NULL, + model varchar(255) NOT NULL, + foreign_key int(10) NOT NULL, + field varchar(255) NOT NULL, + content mediumtext, + PRIMARY KEY (id), +# UNIQUE INDEX I18N_LOCALE_FIELD(locale, model, foreign_key, field), +# INDEX I18N_LOCALE_ROW(locale, model, foreign_key), +# INDEX I18N_LOCALE_MODEL(locale, model), +# INDEX I18N_FIELD(model, foreign_key, field), +# INDEX I18N_ROW(model, foreign_key), + INDEX locale (locale), + INDEX model (model), + INDEX row_id (foreign_key), + INDEX field (field) +); \ No newline at end of file diff --git a/cake/console/libs/templates/skel/config/sql/sessions.php b/cake/console/libs/templates/skel/config/sql/sessions.php new file mode 100755 index 0000000..1973661 --- /dev/null +++ b/cake/console/libs/templates/skel/config/sql/sessions.php @@ -0,0 +1,53 @@ +<?php +/* SVN FILE: $Id$ */ +/*Sessions schema generated on: 2007-11-25 07:11:54 : 1196004714*/ +/** + * This is Sessions Schema file + * + * Use it to configure database for Sessions + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.app.config.sql + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/* + * + * Using the Schema command line utility + * cake schema run create Sessions + * + */ +class SessionsSchema extends CakeSchema { + + var $name = 'Sessions'; + + function before($event = array()) { + return true; + } + + function after($event = array()) { + } + + var $cake_sessions = array( + 'id' => array('type'=>'string', 'null' => false, 'key' => 'primary'), + 'data' => array('type'=>'text', 'null' => true, 'default' => NULL), + 'expires' => array('type'=>'integer', 'null' => true, 'default' => NULL), + 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)) + ); + +} +?> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/config/sql/sessions.sql b/cake/console/libs/templates/skel/config/sql/sessions.sql new file mode 100755 index 0000000..75be419 --- /dev/null +++ b/cake/console/libs/templates/skel/config/sql/sessions.sql @@ -0,0 +1,16 @@ +# $Id$ +# +# Copyright 2005-2008, Cake Software Foundation, Inc. +# 1785 E. Sahara Avenue, Suite 490-204 +# Las Vegas, Nevada 89104 +# +# Licensed under The MIT License +# Redistributions of files must retain the above copyright notice. +# http://www.opensource.org/licenses/mit-license.php The MIT License + +CREATE TABLE cake_sessions ( + id varchar(255) NOT NULL default '', + data text, + expires int(11) default NULL, + PRIMARY KEY (id) +); \ No newline at end of file diff --git a/cake/console/libs/templates/skel/controllers/components/empty b/cake/console/libs/templates/skel/controllers/components/empty new file mode 100755 index 0000000..e69de29 diff --git a/cake/console/libs/templates/skel/controllers/pages_controller.php b/cake/console/libs/templates/skel/controllers/pages_controller.php new file mode 100755 index 0000000..8196616 --- /dev/null +++ b/cake/console/libs/templates/skel/controllers/pages_controller.php @@ -0,0 +1,86 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Static content controller. + * + * This file will render views from views/pages/ + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.controller + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Static content controller + * + * Override this controller by placing a copy in controllers directory of an application + * + * @package cake + * @subpackage cake.cake.libs.controller + */ +class PagesController extends AppController { +/** + * Controller name + * + * @var string + * @access public + */ + var $name = 'Pages'; +/** + * Default helper + * + * @var array + * @access public + */ + var $helpers = array('Html'); +/** + * 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 = null; + + if (!empty($path[0])) { + $page = $path[0]; + } + if (!empty($path[1])) { + $subpage = $path[1]; + } + if (!empty($path[$count - 1])) { + $title = Inflector::humanize($path[$count - 1]); + } + $this->set(compact('page', 'subpage', 'title')); + $this->render(join('/', $path)); + } +} + +?> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/index.php b/cake/console/libs/templates/skel/index.php new file mode 100755 index 0000000..5724a1c --- /dev/null +++ b/cake/console/libs/templates/skel/index.php @@ -0,0 +1,24 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.app + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +require 'webroot' . DIRECTORY_SEPARATOR . 'index.php'; +?> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/locale/eng/LC_MESSAGES/empty b/cake/console/libs/templates/skel/locale/eng/LC_MESSAGES/empty new file mode 100755 index 0000000..e69de29 diff --git a/cake/console/libs/templates/skel/models/behaviors/empty b/cake/console/libs/templates/skel/models/behaviors/empty new file mode 100755 index 0000000..e69de29 diff --git a/cake/console/libs/templates/skel/models/datasources/empty b/cake/console/libs/templates/skel/models/datasources/empty new file mode 100755 index 0000000..e69de29 diff --git a/cake/console/libs/templates/skel/plugins/empty b/cake/console/libs/templates/skel/plugins/empty new file mode 100755 index 0000000..e69de29 diff --git a/cake/console/libs/templates/skel/tests/cases/behaviors/empty b/cake/console/libs/templates/skel/tests/cases/behaviors/empty new file mode 100755 index 0000000..e69de29 diff --git a/cake/console/libs/templates/skel/tests/cases/components/empty b/cake/console/libs/templates/skel/tests/cases/components/empty new file mode 100755 index 0000000..e69de29 diff --git a/cake/console/libs/templates/skel/tests/cases/controllers/empty b/cake/console/libs/templates/skel/tests/cases/controllers/empty new file mode 100755 index 0000000..e69de29 diff --git a/cake/console/libs/templates/skel/tests/cases/helpers/empty b/cake/console/libs/templates/skel/tests/cases/helpers/empty new file mode 100755 index 0000000..e69de29 diff --git a/cake/console/libs/templates/skel/tests/cases/models/empty b/cake/console/libs/templates/skel/tests/cases/models/empty new file mode 100755 index 0000000..e69de29 diff --git a/cake/console/libs/templates/skel/tests/fixtures/empty b/cake/console/libs/templates/skel/tests/fixtures/empty new file mode 100755 index 0000000..e69de29 diff --git a/cake/console/libs/templates/skel/tests/groups/empty b/cake/console/libs/templates/skel/tests/groups/empty new file mode 100755 index 0000000..e69de29 diff --git a/cake/console/libs/templates/skel/tmp/cache/models/empty b/cake/console/libs/templates/skel/tmp/cache/models/empty new file mode 100755 index 0000000..e69de29 diff --git a/cake/console/libs/templates/skel/tmp/cache/persistent/empty b/cake/console/libs/templates/skel/tmp/cache/persistent/empty new file mode 100755 index 0000000..e69de29 diff --git a/cake/console/libs/templates/skel/tmp/cache/views/empty b/cake/console/libs/templates/skel/tmp/cache/views/empty new file mode 100755 index 0000000..e69de29 diff --git a/cake/console/libs/templates/skel/tmp/logs/empty b/cake/console/libs/templates/skel/tmp/logs/empty new file mode 100755 index 0000000..e69de29 diff --git a/cake/console/libs/templates/skel/tmp/sessions/empty b/cake/console/libs/templates/skel/tmp/sessions/empty new file mode 100755 index 0000000..e69de29 diff --git a/cake/console/libs/templates/skel/tmp/tests/empty b/cake/console/libs/templates/skel/tmp/tests/empty new file mode 100755 index 0000000..e69de29 diff --git a/cake/console/libs/templates/skel/vendors/shells/tasks/empty b/cake/console/libs/templates/skel/vendors/shells/tasks/empty new file mode 100755 index 0000000..e69de29 diff --git a/cake/console/libs/templates/skel/vendors/shells/templates/empty b/cake/console/libs/templates/skel/vendors/shells/templates/empty new file mode 100755 index 0000000..e69de29 diff --git a/cake/console/libs/templates/skel/views/elements/email/html/default.ctp b/cake/console/libs/templates/skel/views/elements/email/html/default.ctp new file mode 100755 index 0000000..6e51c0f --- /dev/null +++ b/cake/console/libs/templates/skel/views/elements/email/html/default.ctp @@ -0,0 +1,31 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.elements.email.html + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<?php +$content = explode("\n", $content); + +foreach ($content as $line): + echo '<p> ' . $line . '</p>'; +endforeach; +?> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/views/elements/email/text/default.ctp b/cake/console/libs/templates/skel/views/elements/email/text/default.ctp new file mode 100755 index 0000000..cbb261c --- /dev/null +++ b/cake/console/libs/templates/skel/views/elements/email/text/default.ctp @@ -0,0 +1,25 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.elements.email.text + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<?php echo $content; ?> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/views/elements/empty b/cake/console/libs/templates/skel/views/elements/empty new file mode 100755 index 0000000..e69de29 diff --git a/cake/console/libs/templates/skel/views/errors/empty b/cake/console/libs/templates/skel/views/errors/empty new file mode 100755 index 0000000..e69de29 diff --git a/cake/console/libs/templates/skel/views/helpers/empty b/cake/console/libs/templates/skel/views/helpers/empty new file mode 100755 index 0000000..e69de29 diff --git a/cake/console/libs/templates/skel/views/layouts/ajax.ctp b/cake/console/libs/templates/skel/views/layouts/ajax.ctp new file mode 100755 index 0000000..ca3459a --- /dev/null +++ b/cake/console/libs/templates/skel/views/layouts/ajax.ctp @@ -0,0 +1,25 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.layouts + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<?php echo $content_for_layout; ?> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/views/layouts/default.ctp b/cake/console/libs/templates/skel/views/layouts/default.ctp new file mode 100755 index 0000000..8cc3467 --- /dev/null +++ b/cake/console/libs/templates/skel/views/layouts/default.ctp @@ -0,0 +1,64 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.console.libs.templates.skel.views.layouts + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <?php echo $html->charset(); ?> + <title> + <?php __('CakePHP: the rapid development php framework:'); ?> + <?php echo $title_for_layout; ?> + </title> + <?php + echo $html->meta('icon'); + + echo $html->css('cake.generic'); + + echo $scripts_for_layout; + ?> +</head> +<body> + <div id="container"> + <div id="header"> + <h1><?php echo $html->link(__('CakePHP: the rapid development php framework', true), 'http://cakephp.org'); ?></h1> + </div> + <div id="content"> + + <?php $session->flash(); ?> + + <?php echo $content_for_layout; ?> + + </div> + <div id="footer"> + <?php echo $html->link( + $html->image('cake.power.gif', array('alt'=> __("CakePHP: the rapid development php framework", true), 'border'=>"0")), + 'http://www.cakephp.org/', + array('target'=>'_blank'), null, false + ); + ?> + </div> + </div> + <?php echo $cakeDebug; ?> +</body> +</html> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/views/layouts/email/html/default.ctp b/cake/console/libs/templates/skel/views/layouts/email/html/default.ctp new file mode 100755 index 0000000..1853a7b --- /dev/null +++ b/cake/console/libs/templates/skel/views/layouts/email/html/default.ctp @@ -0,0 +1,37 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.layouts.email.html + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"> + +<html> +<head> + <title><?php echo $title_for_layout;?></title> +</head> + +<body> + <?php echo $content_for_layout;?> + + <p>This email was sent using the <a href="http://cakephp.org">CakePHP Framework</a></p> +</body> +</html> diff --git a/cake/console/libs/templates/skel/views/layouts/email/text/default.ctp b/cake/console/libs/templates/skel/views/layouts/email/text/default.ctp new file mode 100755 index 0000000..77fda3b --- /dev/null +++ b/cake/console/libs/templates/skel/views/layouts/email/text/default.ctp @@ -0,0 +1,29 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.layouts.email.text + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> + +<?php echo $content_for_layout;?> + +This email was sent using the CakePHP Framework, http://cakephp.org. + diff --git a/cake/console/libs/templates/skel/views/layouts/flash.ctp b/cake/console/libs/templates/skel/views/layouts/flash.ctp new file mode 100755 index 0000000..ddd6308 --- /dev/null +++ b/cake/console/libs/templates/skel/views/layouts/flash.ctp @@ -0,0 +1,44 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.layouts + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<?php echo $html->charset(); ?> +<title><?php echo $page_title; ?></title> + + +<?php if (Configure::read() == 0) { ?> +<meta http-equiv="Refresh" content="<?php echo $pause?>;url=<?php echo $url?>"/> +<?php } ?> +<style><!-- +P { text-align:center; font:bold 1.1em sans-serif } +A { color:#444; text-decoration:none } +A:HOVER { text-decoration: underline; color:#44E } +--></style> +</head> +<body> +<p><a href="<?php echo $url?>"><?php echo $message?></a></p> +</body> +</html> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/views/layouts/js/default.ctp b/cake/console/libs/templates/skel/views/layouts/js/default.ctp new file mode 100755 index 0000000..d94dc90 --- /dev/null +++ b/cake/console/libs/templates/skel/views/layouts/js/default.ctp @@ -0,0 +1,2 @@ +<?php echo $scripts_for_layout; ?> +<script type="text/javascript"><?php echo $content_for_layout; ?></script> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/views/layouts/rss/default.ctp b/cake/console/libs/templates/skel/views/layouts/rss/default.ctp new file mode 100755 index 0000000..94067f2 --- /dev/null +++ b/cake/console/libs/templates/skel/views/layouts/rss/default.ctp @@ -0,0 +1,17 @@ +<?php +echo $rss->header(); + +if (!isset($channel)) { + $channel = array(); +} +if (!isset($channel['title'])) { + $channel['title'] = $title_for_layout; +} + +echo $rss->document( + $rss->channel( + array(), $channel, $content_for_layout + ) +); + +?> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/views/layouts/xml/default.ctp b/cake/console/libs/templates/skel/views/layouts/xml/default.ctp new file mode 100755 index 0000000..c688702 --- /dev/null +++ b/cake/console/libs/templates/skel/views/layouts/xml/default.ctp @@ -0,0 +1,2 @@ +<?php e($xml->header()); ?> +<?php echo $content_for_layout; ?> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/views/pages/empty b/cake/console/libs/templates/skel/views/pages/empty new file mode 100755 index 0000000..e69de29 diff --git a/cake/console/libs/templates/skel/views/scaffolds/empty b/cake/console/libs/templates/skel/views/scaffolds/empty new file mode 100755 index 0000000..e69de29 diff --git a/cake/console/libs/templates/skel/webroot/.htaccess b/cake/console/libs/templates/skel/webroot/.htaccess new file mode 100755 index 0000000..f9d8b93 --- /dev/null +++ b/cake/console/libs/templates/skel/webroot/.htaccess @@ -0,0 +1,6 @@ +<IfModule mod_rewrite.c> + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^(.*)$ index.php?url=$1 [QSA,L] +</IfModule> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/webroot/css.php b/cake/console/libs/templates/skel/webroot/css.php new file mode 100755 index 0000000..52b57fe --- /dev/null +++ b/cake/console/libs/templates/skel/webroot/css.php @@ -0,0 +1,102 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Short description for file. + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.app.webroot + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +if (!defined('CAKE_CORE_INCLUDE_PATH')) { + header('HTTP/1.1 404 Not Found'); + exit('File Not Found'); +} +/** + * Enter description here... + */ +if (!class_exists('File')) { + uses('file'); +} +/** + * Enter description here... + * + * @param unknown_type $path + * @param unknown_type $name + * @return unknown + */ + function make_clean_css($path, $name) { + App::import('Vendor', 'csspp' . DS . 'csspp'); + $data = file_get_contents($path); + $csspp = new csspp(); + $output = $csspp->compress($data); + $ratio = 100 - (round(strlen($output) / strlen($data), 3) * 100); + $output = " /* file: $name, ratio: $ratio% */ " . $output; + return $output; + } +/** + * Enter description here... + * + * @param unknown_type $path + * @param unknown_type $content + * @return unknown + */ + function write_css_cache($path, $content) { + if (!is_dir(dirname($path))) { + mkdir(dirname($path)); + } + $cache = new File($path); + return $cache->write($content); + } + + if (preg_match('|\.\.|', $url) || !preg_match('|^ccss/(.+)$|i', $url, $regs)) { + die('Wrong file name.'); + } + + $filename = 'css/' . $regs[1]; + $filepath = CSS . $regs[1]; + $cachepath = CACHE . 'css' . DS . str_replace(array('/','\\'), '-', $regs[1]); + + if (!file_exists($filepath)) { + die('Wrong file name.'); + } + + if (file_exists($cachepath)) { + $templateModified = filemtime($filepath); + $cacheModified = filemtime($cachepath); + + if ($templateModified > $cacheModified) { + $output = make_clean_css($filepath, $filename); + write_css_cache($cachepath, $output); + } else { + $output = file_get_contents($cachepath); + } + } else { + $output = make_clean_css($filepath, $filename); + write_css_cache($cachepath, $output); + $templateModified = time(); + } + + header("Date: " . date("D, j M Y G:i:s ", $templateModified) . 'GMT'); + header("Content-Type: text/css"); + header("Expires: " . gmdate("D, j M Y H:i:s", time() + DAY) . " GMT"); + header("Cache-Control: max-age=86400, must-revalidate"); // HTTP/1.1 + header("Pragma: cache"); // HTTP/1.0 + print $output; +?> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/webroot/css/cake.generic.css b/cake/console/libs/templates/skel/webroot/css/cake.generic.css new file mode 100755 index 0000000..5042adf --- /dev/null +++ b/cake/console/libs/templates/skel/webroot/css/cake.generic.css @@ -0,0 +1,480 @@ +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.app.webroot.css + * @since CakePHP(tm) + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ + + +* { + margin:0; + padding:0; +} + +/* General Style Info */ +body { + background: #003d4c; + color: #fff; + font-family:'lucida grande',verdana,helvetica,arial,sans-serif; + font-size:90%; + margin: 0; +} +a { + background:#fff; + color: #003d4c; + text-decoration: underline; + font-weight: bold; +} +a:hover { + background:#fff; + color: #003d4c; + text-decoration:none; +} +a img { + border:none; +} +h1, h2, h3, h4 { + font-weight: normal; +} +h1 { + background:#fff; + color: #003d4c; + font-size: 100%; + margin: 0.1em 0; +} +h2 { + background:#fff; + color: #e32; + font-family:'Gill Sans','lucida grande',helvetica, arial, sans-serif; + font-size: 190%; + margin: 0.3em 0; + padding-top: 0.8em; +} +h3 { + color: #993; + font-family:'Gill Sans','lucida grande',helvetica, arial, sans-serif; + font-size: 165%; + padding-top: 1.5em; +} +h4 { + color: #993; + font-weight: normal; + padding-top: 0.5em; +} +ul, li { + margin: 0 12px; +} + +/* Layout */ +#container { + text-align: left; +} + +#header{ + padding: 10px 20px; +} +#header h1 { + background: #003d4c url('../img/cake.icon.gif') no-repeat left; + color: #fff; + padding: 0px 30px; +} +#header h1 a { + color: #fff; + background: #003d4c; + font-weight: normal; + text-decoration: none; +} +#header h1 a:hover { + color: #fff; + background: #003d4c; + text-decoration: underline; +} +#content{ + background: #fff; + clear: both; + color: #333; + padding: 10px 20px 40px 20px; + overflow: auto; +} +#footer { + clear: both; + padding: 6px 10px; + text-align: right; +} + +/* Tables */ +table { + background: #fff; + border:1px solid #ccc; + border-right:0; + clear: both; + color: #333; + margin-bottom: 10px; + width: 100%; +} +th { + background: #f2f2f2; + border:1px solid #bbb; + border-top: 1px solid #fff; + border-left: 1px solid #fff; + text-align: center; +} +th a { + background:#f2f2f2; + display: block; + padding: 2px 4px; + text-decoration: none; +} +th a:hover { + background: #ccc; + color: #333; + text-decoration: none; +} +table tr td { + background: #fff; + border-right: 1px solid #ccc; + padding: 4px; + text-align: center; + vertical-align: top; +} +table tr.altrow td { + background: #f4f4f4; +} +td.actions { + text-align: center; + white-space: nowrap; +} +td.actions a { + margin: 0px 6px; +} +.cake-sql-log table { + background: #f4f4f4; +} +.cake-sql-log td { + padding: 4px 8px; + text-align: left; +} + +/* Paging */ +div.paging { + background:#fff; + color: #ccc; + margin-bottom: 2em; +} +div.paging div.disabled { + color: #ddd; + display: inline; +} +div.paging span { +} +div.paging span.current { + color: #000; +} +div.paging span a { +} + +/* Scaffold View */ +dl { + line-height: 2em; + margin: 0em 0em; + width: 60%; +} +dl.altrow { + background: #f4f4f4; +} +dt { + font-weight: bold; + padding-left: 4px; + vertical-align: top; +} +dd { + margin-left: 10em; + margin-top: -2em; + vertical-align: top; +} + +/* Forms */ +form { + clear: both; + margin-right: 20px; + padding: 0; + width: 80%; +} +fieldset { + border: 1px solid #ccc; + margin-top: 30px; + padding: 16px 20px; +} +fieldset legend { + background:#fff; + color: #e32; + font-size: 160%; + font-weight: bold; +} +fieldset fieldset { + margin-top: 0px; + margin-bottom: 20px; + padding: 16px 10px; +} +fieldset fieldset legend { + font-size: 120%; + font-weight: normal; +} +fieldset fieldset div { + clear: left; + margin: 0 20px; +} +form div { + clear: both; + margin-bottom: 1em; + padding: .5em; + vertical-align: text-top; +} +form div.input { + color: #444; +} +form div.required { + color: #333; + font-weight: bold; +} +form div.submit { + border: 0; + clear: both; + margin-top: 10px; + margin-left: 140px; +} +label { + display: block; + font-size: 110%; + padding-right: 20px; +} +input, textarea { + clear: both; + font-size: 140%; + font-family: "frutiger linotype", "lucida grande", "verdana", sans-serif; + padding: 2px; + width: 100%; +} +select { + clear: both; + font-size: 120%; + vertical-align: text-bottom; +} +select[multiple=multiple] { + width: 100%; +} +option { + font-size: 120%; + padding: 0 3px; +} +input[type=checkbox] { + clear: left; + float: left; + margin: 0px 6px 7px 2px; + width: auto; +} +input[type=radio] { + float:left; + width:auto; + margin: 0 3px 7px 0; +} +div.radio label { + margin: 0 0 6px 20px; +} +input[type=submit] { + display: inline; + font-size: 110%; + padding: 2px 5px; + width: auto; + vertical-align: bottom; +} + +/* Notices and Errors */ +div.message { + clear: both; + color: #900; + font-size: 140%; + font-weight: bold; + margin: 1em 0; +} +div.error-message { + clear: both; + color: #900; + font-weight: bold; +} +p.error { + background-color: #e32; + color: #fff; + font-family: Courier, monospace; + font-size: 120%; + line-height: 140%; + padding: 0.8em; + margin: 1em 0; +} +p.error em { + color: #000; + font-weight: normal; + line-height: 140%; +} +.notice { + background: #ffcc00; + color: #000; + display: block; + font-family: Courier, monospace; + font-size: 120%; + line-height: 140%; + padding: 0.8em; + margin: 1em 0; +} +.success { + background: green; + color: #fff; +} + +/* Actions */ +div.actions ul { + margin: 0px 0; + padding: 0; +} +div.actions li { + display: inline; + list-style-type: none; + line-height: 2em; + margin: 0 2em 0 0; + white-space: nowrap; +} +div.actions ul li a { + background:#fff; + color: #003d4c; + text-decoration: none; +} +div.actions ul li a:hover { + color: #333; + text-decoration: underline; +} + +/* Related */ +div.related { + clear: both; + display: block; +} + +/* Debugging */ +pre { + color: #000; + background: #f0f0f0; + padding: 1em; +} +pre.cake-debug { + background: #ffcc00; + font-size: 120%; + line-height: 140%; + margin-top: 1em; + overflow: auto; + position: relative; +} +div.cake-stack-trace { + background: #fff; + border: 4px dotted #ffcc00; + color: #333; + margin: 0px; + padding: 6px; + font-size: 120%; + line-height: 140%; + overflow: auto; + position: relative; +} +div.cake-code-dump pre { + position: relative; + overflow: auto; +} +div.cake-stack-trace pre, div.cake-code-dump pre { + color: #000; + background-color: #F0F0F0; + margin: 0px; + padding: 1em; + overflow: auto; +} +div.cake-code-dump pre, div.cake-code-dump pre code { + clear: both; + font-size: 12px; + line-height: 15px; + margin: 4px 2px; + padding: 4px; + overflow: auto; +} +div.cake-code-dump span.code-highlight { + background-color: #ff0; + padding: 4px; +} +div.code-coverage-results div.code-line { + padding-left:5px; + display:block; + margin-left:10px; +} +div.code-coverage-results div.uncovered span.content { + background:#ecc; +} +div.code-coverage-results div.covered span.content { + background:#cec; +} +div.code-coverage-results div.ignored span.content { + color:#aaa; +} +div.code-coverage-results span.line-num { + color:#666; + display:block; + float:left; + width:20px; + text-align:right; + margin-right:5px; +} +div.code-coverage-results span.line-num strong { + color:#666; +} +div.code-coverage-results div.start { + border:1px solid #aaa; + border-width:1px 1px 0px 1px; + margin-top:30px; + padding-top:5px; +} +div.code-coverage-results div.end { + border:1px solid #aaa; + border-width:0px 1px 1px 1px; + margin-bottom:30px; + padding-bottom:5px; +} +div.code-coverage-results div.realstart { + margin-top:0px; +} +div.code-coverage-results p.note { + color:#bbb; + padding:5px; + margin:5px 0 10px; + font-size:10px; +} +div.code-coverage-results span.result-bad { + color: #a00; +} +div.code-coverage-results span.result-ok { + color: #fa0; +} +div.code-coverage-results span.result-good { + color: #0a0; +} \ No newline at end of file diff --git a/cake/console/libs/templates/skel/webroot/favicon.ico b/cake/console/libs/templates/skel/webroot/favicon.ico new file mode 100755 index 0000000..b36e81f Binary files /dev/null and b/cake/console/libs/templates/skel/webroot/favicon.ico differ diff --git a/cake/console/libs/templates/skel/webroot/img/cake.icon.gif b/cake/console/libs/templates/skel/webroot/img/cake.icon.gif new file mode 100755 index 0000000..f29f72e Binary files /dev/null and b/cake/console/libs/templates/skel/webroot/img/cake.icon.gif differ diff --git a/cake/console/libs/templates/skel/webroot/img/cake.power.gif b/cake/console/libs/templates/skel/webroot/img/cake.power.gif new file mode 100755 index 0000000..8f8d570 Binary files /dev/null and b/cake/console/libs/templates/skel/webroot/img/cake.power.gif differ diff --git a/cake/console/libs/templates/skel/webroot/index.php b/cake/console/libs/templates/skel/webroot/index.php new file mode 100755 index 0000000..01be9dd --- /dev/null +++ b/cake/console/libs/templates/skel/webroot/index.php @@ -0,0 +1,93 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Short description for file. + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.app.webroot + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Use the DS to separate the directories in other defines + */ + if (!defined('DS')) { + define('DS', DIRECTORY_SEPARATOR); + } +/** + * These defines should only be edited if you have cake installed in + * a directory layout other than the way it is distributed. + * When using custom settings be sure to use the DS and do not add a trailing DS. + */ + +/** + * The full path to the directory which holds "app", WITHOUT a trailing DS. + * + */ + if (!defined('ROOT')) { + define('ROOT', dirname(dirname(dirname(__FILE__)))); + } +/** + * The actual directory name for the "app". + * + */ + if (!defined('APP_DIR')) { + define('APP_DIR', basename(dirname(dirname(__FILE__)))); + } +/** + * The absolute path to the "cake" directory, WITHOUT a trailing DS. + * + */ + if (!defined('CAKE_CORE_INCLUDE_PATH')) { + define('CAKE_CORE_INCLUDE_PATH', ROOT); + } + +/** + * Editing below this line should not be necessary. + * Change at your own risk. + * + */ + if (!defined('WEBROOT_DIR')) { + define('WEBROOT_DIR', basename(dirname(__FILE__))); + } + if (!defined('WWW_ROOT')) { + define('WWW_ROOT', dirname(__FILE__) . DS); + } + if (!defined('CORE_PATH')) { + if (function_exists('ini_set') && ini_set('include_path', CAKE_CORE_INCLUDE_PATH . PATH_SEPARATOR . ROOT . DS . APP_DIR . DS . PATH_SEPARATOR . ini_get('include_path'))) { + define('APP_PATH', null); + define('CORE_PATH', null); + } else { + define('APP_PATH', ROOT . DS . APP_DIR . DS); + define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS); + } + } + if (!include(CORE_PATH . 'cake' . DS . 'bootstrap.php')) { + trigger_error("CakePHP core could not be found. Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php. It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory.", E_USER_ERROR); + } + if (isset($_GET['url']) && $_GET['url'] === 'favicon.ico') { + return; + } else { + $Dispatcher = new Dispatcher(); + $Dispatcher->dispatch($url); + } + if (Configure::read() > 0) { + echo "<!-- " . round(getMicrotime() - $TIME_START, 4) . "s -->"; + } +?> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/webroot/js/vendors.php b/cake/console/libs/templates/skel/webroot/js/vendors.php new file mode 100755 index 0000000..5fda0b7 --- /dev/null +++ b/cake/console/libs/templates/skel/webroot/js/vendors.php @@ -0,0 +1,42 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Short description for file. + * + * This file includes js vendor-files from /vendor/ directory if they need to + * be accessible to the public. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.app.webroot.js + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Enter description here... + */ +if (isset($_GET['file'])) { + $file = $_GET['file']; + $pos = strpos($file, '..'); + if ($pos === false) { + if (is_file('../../vendors/javascript/'.$file) && (preg_match('/(\/.+)\\.js/', $file))) { + readfile('../../vendors/javascript/'.$file); + return; + } + } +} +header('HTTP/1.1 404 Not Found'); +?> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/webroot/test.php b/cake/console/libs/templates/skel/webroot/test.php new file mode 100755 index 0000000..a57d157 --- /dev/null +++ b/cake/console/libs/templates/skel/webroot/test.php @@ -0,0 +1,180 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Short description for file. + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.cake.tests.libs + * @since CakePHP(tm) v 1.2.0.4433 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +set_time_limit(0); +ini_set('memory_limit','128M'); +ini_set('display_errors', 1); +/** + * Use the DS to separate the directories in other defines + */ + if (!defined('DS')) { + define('DS', DIRECTORY_SEPARATOR); + } +/** + * These defines should only be edited if you have cake installed in + * a directory layout other than the way it is distributed. + * When using custom settings be sure to use the DS and do not add a trailing DS. + */ + +/** + * The full path to the directory which holds "app", WITHOUT a trailing DS. + * + */ + if (!defined('ROOT')) { + define('ROOT', dirname(dirname(dirname(__FILE__)))); + } +/** + * The actual directory name for the "app". + * + */ + if (!defined('APP_DIR')) { + define('APP_DIR', basename(dirname(dirname(__FILE__)))); + } +/** + * The absolute path to the "cake" directory, WITHOUT a trailing DS. + * + */ + if (!defined('CAKE_CORE_INCLUDE_PATH')) { + define('CAKE_CORE_INCLUDE_PATH', ROOT); + } + +/** + * Editing below this line should not be necessary. + * Change at your own risk. + * + */ +if (!defined('WEBROOT_DIR')) { + define('WEBROOT_DIR', basename(dirname(__FILE__))); +} +if (!defined('WWW_ROOT')) { + define('WWW_ROOT', dirname(__FILE__) . DS); +} +if (!defined('CORE_PATH')) { + if (function_exists('ini_set') && ini_set('include_path', CAKE_CORE_INCLUDE_PATH . PATH_SEPARATOR . ROOT . DS . APP_DIR . DS . PATH_SEPARATOR . ini_get('include_path'))) { + define('APP_PATH', null); + define('CORE_PATH', null); + } else { + define('APP_PATH', ROOT . DS . APP_DIR . DS); + define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS); + } +} +if (!include(CORE_PATH . 'cake' . DS . 'bootstrap.php')) { + trigger_error("CakePHP core could not be found. Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php. It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory.", E_USER_ERROR); +} + +$corePath = Configure::corePaths('cake'); +if (isset($corePath[0])) { + define('TEST_CAKE_CORE_INCLUDE_PATH', rtrim($corePath[0], DS) . DS); +} else { + define('TEST_CAKE_CORE_INCLUDE_PATH', CAKE_CORE_INCLUDE_PATH); +} + +require_once CAKE_TESTS_LIB . 'test_manager.php'; + +if (Configure::read('debug') < 1) { + die(__('Debug setting does not allow access to this url.', true)); +} + +if (!isset($_SERVER['SERVER_NAME'])) { + $_SERVER['SERVER_NAME'] = ''; +} +if (empty( $_GET['output'])) { + $_GET['output'] = 'html'; +} +/** + * + * Used to determine output to display + */ +define('CAKE_TEST_OUTPUT_HTML', 1); +define('CAKE_TEST_OUTPUT_TEXT', 2); + +if (isset($_GET['output']) && $_GET['output'] == 'html') { + define('CAKE_TEST_OUTPUT', CAKE_TEST_OUTPUT_HTML); +} else { + Debugger::output('txt'); + define('CAKE_TEST_OUTPUT', CAKE_TEST_OUTPUT_TEXT); +} + +if (!App::import('Vendor', 'simpletest' . DS . 'reporter')) { + CakePHPTestHeader(); + include CAKE_TESTS_LIB . 'simpletest.php'; + CakePHPTestSuiteFooter(); + exit(); +} + +$analyzeCodeCoverage = false; +if (isset($_GET['code_coverage'])) { + $analyzeCodeCoverage = true; + require_once CAKE_TESTS_LIB . 'code_coverage_manager.php'; + if (!extension_loaded('xdebug')) { + CakePHPTestHeader(); + include CAKE_TESTS_LIB . 'xdebug.php'; + CakePHPTestSuiteFooter(); + exit(); + } +} + +CakePHPTestHeader(); +CakePHPTestSuiteHeader(); +define('RUN_TEST_LINK', $_SERVER['PHP_SELF']); + +if (isset($_GET['group'])) { + if ('all' == $_GET['group']) { + TestManager::runAllTests(CakeTestsGetReporter()); + } else { + if ($analyzeCodeCoverage) { + CodeCoverageManager::start($_GET['group'], CakeTestsGetReporter()); + } + TestManager::runGroupTest(ucfirst($_GET['group']), CakeTestsGetReporter()); + if ($analyzeCodeCoverage) { + CodeCoverageManager::report(); + } + } + + CakePHPTestRunMore(); + CakePHPTestAnalyzeCodeCoverage(); +} elseif (isset($_GET['case'])) { + if ($analyzeCodeCoverage) { + CodeCoverageManager::start($_GET['case'], CakeTestsGetReporter()); + } + + TestManager::runTestCase($_GET['case'], CakeTestsGetReporter()); + + if ($analyzeCodeCoverage) { + CodeCoverageManager::report(); + } + + CakePHPTestRunMore(); + CakePHPTestAnalyzeCodeCoverage(); +} elseif (isset($_GET['show']) && $_GET['show'] == 'cases') { + CakePHPTestCaseList(); +} else { + CakePHPTestGroupTestList(); +} +CakePHPTestSuiteFooter(); +$output = ob_get_clean(); +echo $output; +?> \ No newline at end of file diff --git a/cake/console/libs/templates/views/form.ctp b/cake/console/libs/templates/views/form.ctp new file mode 100755 index 0000000..fda3edd --- /dev/null +++ b/cake/console/libs/templates/views/form.ctp @@ -0,0 +1,69 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.console.libs.templates.views + * @since CakePHP(tm) v 1.2.0.5234 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<div class="<?php echo $pluralVar;?> form"> +<?php echo "<?php echo \$form->create('{$modelClass}');?>\n";?> + <fieldset> + <legend><?php echo "<?php __('" . Inflector::humanize($action) . " {$singularHumanName}');?>";?></legend> +<?php + echo "\t<?php\n"; + foreach ($fields as $field) { + if ($action == 'add' && $field == $primaryKey) { + continue; + } elseif (!in_array($field, array('created', 'modified', 'updated'))) { + echo "\t\techo \$form->input('{$field}');\n"; + } + } + if (!empty($associations['hasAndBelongsToMany'])) { + foreach ($associations['hasAndBelongsToMany'] as $assocName => $assocData) { + echo "\t\techo \$form->input('{$assocName}');\n"; + } + } + echo "\t?>\n"; +?> + </fieldset> +<?php + echo "<?php echo \$form->end('Submit');?>\n"; +?> +</div> +<div class="actions"> + <ul> +<?php if ($action != 'add'):?> + <li><?php echo "<?php echo \$html->link(__('Delete', true), array('action' => 'delete', \$form->value('{$modelClass}.{$primaryKey}')), null, sprintf(__('Are you sure you want to delete # %s?', true), \$form->value('{$modelClass}.{$primaryKey}'))); ?>";?></li> +<?php endif;?> + <li><?php echo "<?php echo \$html->link(__('List {$pluralHumanName}', true), array('action' => 'index'));?>";?></li> +<?php + $done = array(); + foreach ($associations as $type => $data) { + foreach ($data as $alias => $details) { + if ($details['controller'] != $this->name && !in_array($details['controller'], $done)) { + echo "\t\t<li><?php echo \$html->link(__('List " . Inflector::humanize($details['controller']) . "', true), array('controller' => '{$details['controller']}', 'action' => 'index')); ?> </li>\n"; + echo "\t\t<li><?php echo \$html->link(__('New " . Inflector::humanize(Inflector::underscore($alias)) . "', true), array('controller' => '{$details['controller']}', 'action' => 'add')); ?> </li>\n"; + $done[] = $details['controller']; + } + } + } +?> + </ul> +</div> diff --git a/cake/console/libs/templates/views/home.ctp b/cake/console/libs/templates/views/home.ctp new file mode 100755 index 0000000..5b3dc31 --- /dev/null +++ b/cake/console/libs/templates/views/home.ctp @@ -0,0 +1,82 @@ +<?php +$output = "<h2>Sweet, \"" . Inflector::humanize($app) . "\" got Baked by CakePHP!</h2>\n"; +$output .=" +<?php +if (Configure::read() > 0): + Debugger::checkSessionKey(); +endif; +?> +<p> +<?php + if (is_writable(TMP)): + echo '<span class=\"notice success\">'; + __('Your tmp directory is writable.'); + echo '</span>'; + else: + echo '<span class=\"notice\">'; + __('Your tmp directory is NOT writable.'); + echo '</span>'; + endif; +?> +</p> +<p> +<?php + \$settings = Cache::settings(); + if (!empty(\$settings)): + echo '<span class=\"notice success\">'; + echo sprintf(__('The %s is being used for caching. To change the config edit APP/config/core.php ', true), '<em>'. \$settings['engine'] . 'Engine</em>'); + echo '</span>'; + else: + echo '<span class=\"notice\">'; + __('Your cache is NOT working. Please check the settings in APP/config/core.php'); + echo '</span>'; + endif; +?> +</p> +<p> +<?php + \$filePresent = null; + if (file_exists(CONFIGS . 'database.php')): + echo '<span class=\"notice success\">'; + __('Your database configuration file is present.'); + \$filePresent = true; + echo '</span>'; + else: + echo '<span class=\"notice\">'; + __('Your database configuration file is NOT present.'); + echo '<br/>'; + __('Rename config/database.php.default to config/database.php'); + echo '</span>'; + endif; +?> +</p> +<?php +if (!empty(\$filePresent)): + uses('model' . DS . 'connection_manager'); + \$db = ConnectionManager::getInstance(); + \$connected = \$db->getDataSource('default'); +?> +<p> +<?php + if (\$connected->isConnected()): + echo '<span class=\"notice success\">'; + __('Cake is able to connect to the database.'); + echo '</span>'; + else: + echo '<span class=\"notice\">'; + __('Cake is NOT able to connect to the database.'); + echo '</span>'; + endif; +?> +</p>\n"; +$output .= "<?php endif;?>\n"; +$output .= "<h3><?php __('Editing this Page') ?></h3>\n"; +$output .= "<p>\n"; +$output .= "<?php\n"; +$output .= "\techo sprintf(__('To change the content of this page, edit: %s\n"; +$output .= "\t\tTo change its layout, edit: %s\n"; +$output .= "\t\tYou can also add some CSS styles for your pages at: %s', true),\n"; +$output .= "\t\tAPP . 'views' . DS . 'pages' . DS . 'home.ctp.<br />', APP . 'views' . DS . 'layouts' . DS . 'default.ctp.<br />', APP . 'webroot' . DS . 'css');\n"; +$output .= "?>\n"; +$output .= "</p>\n"; +?> diff --git a/cake/console/libs/templates/views/index.ctp b/cake/console/libs/templates/views/index.ctp new file mode 100755 index 0000000..49c69db --- /dev/null +++ b/cake/console/libs/templates/views/index.ctp @@ -0,0 +1,99 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.console.libs.templates.views + * @since CakePHP(tm) v 1.2.0.5234 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<div class="<?php echo $pluralVar;?> index"> +<h2><?php echo "<?php __('{$pluralHumanName}');?>";?></h2> +<p> +<?php echo "<?php +echo \$paginator->counter(array( +'format' => __('Page %page% of %pages%, showing %current% records out of %count% total, starting on record %start%, ending on %end%', true) +)); +?>";?> +</p> +<table cellpadding="0" cellspacing="0"> +<tr> +<?php foreach ($fields as $field):?> + <th><?php echo "<?php echo \$paginator->sort('{$field}');?>";?></th> +<?php endforeach;?> + <th class="actions"><?php echo "<?php __('Actions');?>";?></th> +</tr> +<?php +echo "<?php +\$i = 0; +foreach (\${$pluralVar} as \${$singularVar}): + \$class = null; + if (\$i++ % 2 == 0) { + \$class = ' class=\"altrow\"'; + } +?>\n"; + echo "\t<tr<?php echo \$class;?>>\n"; + foreach ($fields as $field) { + $isKey = false; + if (!empty($associations['belongsTo'])) { + foreach ($associations['belongsTo'] as $alias => $details) { + if ($field === $details['foreignKey']) { + $isKey = true; + echo "\t\t<td>\n\t\t\t<?php echo \$html->link(\${$singularVar}['{$alias}']['{$details['displayField']}'], array('controller' => '{$details['controller']}', 'action' => 'view', \${$singularVar}['{$alias}']['{$details['primaryKey']}'])); ?>\n\t\t</td>\n"; + break; + } + } + } + if ($isKey !== true) { + echo "\t\t<td>\n\t\t\t<?php echo \${$singularVar}['{$modelClass}']['{$field}']; ?>\n\t\t</td>\n"; + } + } + + echo "\t\t<td class=\"actions\">\n"; + echo "\t\t\t<?php echo \$html->link(__('View', true), array('action' => 'view', \${$singularVar}['{$modelClass}']['{$primaryKey}'])); ?>\n"; + echo "\t\t\t<?php echo \$html->link(__('Edit', true), array('action' => 'edit', \${$singularVar}['{$modelClass}']['{$primaryKey}'])); ?>\n"; + echo "\t\t\t<?php echo \$html->link(__('Delete', true), array('action' => 'delete', \${$singularVar}['{$modelClass}']['{$primaryKey}']), null, sprintf(__('Are you sure you want to delete # %s?', true), \${$singularVar}['{$modelClass}']['{$primaryKey}'])); ?>\n"; + echo "\t\t</td>\n"; + echo "\t</tr>\n"; + +echo "<?php endforeach; ?>\n"; +?> +</table> +</div> +<div class="paging"> +<?php echo "\t<?php echo \$paginator->prev('<< '.__('previous', true), array(), null, array('class'=>'disabled'));?>\n";?> + | <?php echo "\t<?php echo \$paginator->numbers();?>\n"?> +<?php echo "\t<?php echo \$paginator->next(__('next', true).' >>', array(), null, array('class' => 'disabled'));?>\n";?> +</div> +<div class="actions"> + <ul> + <li><?php echo "<?php echo \$html->link(__('New {$singularHumanName}', true), array('action' => 'add')); ?>";?></li> +<?php + $done = array(); + foreach ($associations as $type => $data) { + foreach ($data as $alias => $details) { + if ($details['controller'] != $this->name && !in_array($details['controller'], $done)) { + echo "\t\t<li><?php echo \$html->link(__('List " . Inflector::humanize($details['controller']) . "', true), array('controller' => '{$details['controller']}', 'action' => 'index')); ?> </li>\n"; + echo "\t\t<li><?php echo \$html->link(__('New " . Inflector::humanize(Inflector::underscore($alias)) . "', true), array('controller' => '{$details['controller']}', 'action' => 'add')); ?> </li>\n"; + $done[] = $details['controller']; + } + } + } +?> + </ul> +</div> diff --git a/cake/console/libs/templates/views/view.ctp b/cake/console/libs/templates/views/view.ctp new file mode 100755 index 0000000..e390bb0 --- /dev/null +++ b/cake/console/libs/templates/views/view.ctp @@ -0,0 +1,150 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.console.libs.templates.views + * @since CakePHP(tm) v 1.2.0.5234 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<div class="<?php echo $pluralVar;?> view"> +<h2><?php echo "<?php __('{$singularHumanName}');?>";?></h2> + <dl><?php echo "<?php \$i = 0; \$class = ' class=\"altrow\"';?>\n";?> +<?php +foreach ($fields as $field) { + $isKey = false; + if (!empty($associations['belongsTo'])) { + foreach ($associations['belongsTo'] as $alias => $details) { + if ($field === $details['foreignKey']) { + $isKey = true; + echo "\t\t<dt<?php if (\$i % 2 == 0) echo \$class;?>><?php __('" . Inflector::humanize(Inflector::underscore($alias)) . "'); ?></dt>\n"; + echo "\t\t<dd<?php if (\$i++ % 2 == 0) echo \$class;?>>\n\t\t\t<?php echo \$html->link(\${$singularVar}['{$alias}']['{$details['displayField']}'], array('controller' => '{$details['controller']}', 'action' => 'view', \${$singularVar}['{$alias}']['{$details['primaryKey']}'])); ?>\n\t\t\t&nbsp;\n\t\t</dd>\n"; + break; + } + } + } + if ($isKey !== true) { + echo "\t\t<dt<?php if (\$i % 2 == 0) echo \$class;?>><?php __('" . Inflector::humanize($field) . "'); ?></dt>\n"; + echo "\t\t<dd<?php if (\$i++ % 2 == 0) echo \$class;?>>\n\t\t\t<?php echo \${$singularVar}['{$modelClass}']['{$field}']; ?>\n\t\t\t&nbsp;\n\t\t</dd>\n"; + } +} +?> + </dl> +</div> +<div class="actions"> + <ul> +<?php + echo "\t\t<li><?php echo \$html->link(__('Edit {$singularHumanName}', true), array('action' => 'edit', \${$singularVar}['{$modelClass}']['{$primaryKey}'])); ?> </li>\n"; + echo "\t\t<li><?php echo \$html->link(__('Delete {$singularHumanName}', true), array('action' => 'delete', \${$singularVar}['{$modelClass}']['{$primaryKey}']), null, sprintf(__('Are you sure you want to delete # %s?', true), \${$singularVar}['{$modelClass}']['{$primaryKey}'])); ?> </li>\n"; + echo "\t\t<li><?php echo \$html->link(__('List {$pluralHumanName}', true), array('action' => 'index')); ?> </li>\n"; + echo "\t\t<li><?php echo \$html->link(__('New {$singularHumanName}', true), array('action' => 'add')); ?> </li>\n"; + + $done = array(); + foreach ($associations as $type => $data) { + foreach ($data as $alias => $details) { + if ($details['controller'] != $this->name && !in_array($details['controller'], $done)) { + echo "\t\t<li><?php echo \$html->link(__('List " . Inflector::humanize($details['controller']) . "', true), array('controller' => '{$details['controller']}', 'action' => 'index')); ?> </li>\n"; + echo "\t\t<li><?php echo \$html->link(__('New " . Inflector::humanize(Inflector::underscore($alias)) . "', true), array('controller' => '{$details['controller']}', 'action' => 'add')); ?> </li>\n"; + $done[] = $details['controller']; + } + } + } +?> + </ul> +</div> +<?php +if (!empty($associations['hasOne'])) : + foreach ($associations['hasOne'] as $alias => $details): ?> + <div class="related"> + <h3><?php echo "<?php __('Related " . Inflector::humanize($details['controller']) . "');?>";?></h3> + <?php echo "<?php if (!empty(\${$singularVar}['{$alias}'])):?>\n";?> + <dl><?php echo "\t<?php \$i = 0; \$class = ' class=\"altrow\"';?>\n";?> + <?php + foreach ($details['fields'] as $field) { + echo "\t\t<dt<?php if (\$i % 2 == 0) echo \$class;?>><?php __('" . Inflector::humanize($field) . "');?></dt>\n"; + echo "\t\t<dd<?php if (\$i++ % 2 == 0) echo \$class;?>>\n\t<?php echo \${$singularVar}['{$alias}']['{$field}'];?>\n&nbsp;</dd>\n"; + } + ?> + </dl> + <?php echo "<?php endif; ?>\n";?> + <div class="actions"> + <ul> + <li><?php echo "<?php echo \$html->link(__('Edit " . Inflector::humanize(Inflector::underscore($alias)) . "', true), array('controller' => '{$details['controller']}', 'action' => 'edit', \${$singularVar}['{$alias}']['{$details['primaryKey']}'])); ?></li>\n";?> + </ul> + </div> + </div> + <?php + endforeach; +endif; +if (empty($associations['hasMany'])) { + $associations['hasMany'] = array(); +} +if (empty($associations['hasAndBelongsToMany'])) { + $associations['hasAndBelongsToMany'] = array(); +} +$relations = array_merge($associations['hasMany'], $associations['hasAndBelongsToMany']); +$i = 0; +foreach ($relations as $alias => $details): + $otherSingularVar = Inflector::variable($alias); + $otherPluralHumanName = Inflector::humanize($details['controller']); + ?> +<div class="related"> + <h3><?php echo "<?php __('Related {$otherPluralHumanName}');?>";?></h3> + <?php echo "<?php if (!empty(\${$singularVar}['{$alias}'])):?>\n";?> + <table cellpadding = "0" cellspacing = "0"> + <tr> +<?php + foreach ($details['fields'] as $field) { + echo "\t\t<th><?php __('" . Inflector::humanize($field) . "'); ?></th>\n"; + } +?> + <th class="actions"><?php echo "<?php __('Actions');?>";?></th> + </tr> +<?php +echo "\t<?php + \$i = 0; + foreach (\${$singularVar}['{$alias}'] as \${$otherSingularVar}): + \$class = null; + if (\$i++ % 2 == 0) { + \$class = ' class=\"altrow\"'; + } + ?>\n"; + echo "\t\t<tr<?php echo \$class;?>>\n"; + + foreach ($details['fields'] as $field) { + echo "\t\t\t<td><?php echo \${$otherSingularVar}['{$field}'];?></td>\n"; + } + + echo "\t\t\t<td class=\"actions\">\n"; + echo "\t\t\t\t<?php echo \$html->link(__('View', true), array('controller' => '{$details['controller']}', 'action' => 'view', \${$otherSingularVar}['{$details['primaryKey']}'])); ?>\n"; + echo "\t\t\t\t<?php echo \$html->link(__('Edit', true), array('controller' => '{$details['controller']}', 'action' => 'edit', \${$otherSingularVar}['{$details['primaryKey']}'])); ?>\n"; + echo "\t\t\t\t<?php echo \$html->link(__('Delete', true), array('controller' => '{$details['controller']}', 'action' => 'delete', \${$otherSingularVar}['{$details['primaryKey']}']), null, sprintf(__('Are you sure you want to delete # %s?', true), \${$otherSingularVar}['{$details['primaryKey']}'])); ?>\n"; + echo "\t\t\t</td>\n"; + echo "\t\t</tr>\n"; + +echo "\t<?php endforeach; ?>\n"; +?> + </table> +<?php echo "<?php endif; ?>\n\n";?> + <div class="actions"> + <ul> + <li><?php echo "<?php echo \$html->link(__('New " . Inflector::humanize(Inflector::underscore($alias)) . "', true), array('controller' => '{$details['controller']}', 'action' => 'add'));?>";?> </li> + </ul> + </div> +</div> +<?php endforeach;?> \ No newline at end of file diff --git a/cake/console/libs/testsuite.php b/cake/console/libs/testsuite.php new file mode 100755 index 0000000..b11a7ea --- /dev/null +++ b/cake/console/libs/testsuite.php @@ -0,0 +1,360 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Test Suite Shell + * + * This Shell allows the running of test suites via the cake command line + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.cake.console.libs + * @since CakePHP(tm) v 1.2.0.4433 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +class TestSuiteShell extends Shell { +/** + * The test category, "app", "core" or the name of a plugin + * + * @var string + * @access public + */ + var $category = ''; +/** + * "group", "case" or "all" + * + * @var string + * @access public + */ + var $type = ''; +/** + * Path to the test case/group file + * + * @var string + * @access public + */ + var $file = ''; +/** + * Storage for plugins that have tests + * + * @var string + * @access public + */ + var $plugins = array(); +/** + * Convenience variable to avoid duplicated code + * + * @var string + * @access public + */ + var $isPluginTest = false; +/** + * Stores if the user wishes to get a code coverage analysis report + * + * @var string + * @access public + */ + var $doCoverage = false; +/** + * The headline for the test output + * + * @var string + * @access public + */ + var $headline = 'CakePHP Test Shell'; +/** + * Initialization method installs Simpletest and loads all plugins + * + * @return void + * @access public + */ + function initialize() { + $corePath = Configure::corePaths('cake'); + if (isset($corePath[0])) { + define('TEST_CAKE_CORE_INCLUDE_PATH', rtrim($corePath[0], DS) . DS); + } else { + define('TEST_CAKE_CORE_INCLUDE_PATH', CAKE_CORE_INCLUDE_PATH); + } + + $this->__installSimpleTest(); + + require_once CAKE . 'tests' . DS . 'lib' . DS . 'test_manager.php'; + require_once CAKE . 'tests' . DS . 'lib' . DS . 'cli_reporter.php'; + + $plugins = Configure::listObjects('plugin'); + foreach ($plugins as $p) { + $this->plugins[] = Inflector::underscore($p); + } + } +/** + * Main entry point to this shell + * + * @return void + * @access public + */ + function main() { + $this->out($this->headline); + $this->hr(); + + if (count($this->args) > 0) { + $this->category = $this->args[0]; + + if (!in_array($this->category, array('app', 'core'))) { + $this->isPluginTest = true; + } + + if (isset($this->args[1])) { + $this->type = $this->args[1]; + } + + if (isset($this->args[2])) { + if ($this->args[2] == 'cov') { + $this->doCoverage = true; + } else { + $this->file = Inflector::underscore($this->args[2]); + } + } + + if (isset($this->args[3]) && $this->args[3] == 'cov') { + $this->doCoverage = true; + } + } else { + $this->err('Sorry, you did not pass any arguments!'); + } + + if ($this->__canRun()) { + $this->out('Running '.$this->category.' '.$this->type.' '.$this->file); + + $exitCode = 0; + if (!$this->__run()) { + $exitCode = 1; + } + exit($exitCode); + } else { + $this->err('Sorry, the tests could not be found.'); + exit(1); + } + } +/** + * Help screen + * + * @return void + * @access public + */ + function help() { + $this->out('Usage: '); + $this->out("\tcake testsuite category test_type file"); + $this->out("\t\t- category - \"app\", \"core\" or name of a plugin"); + $this->out("\t\t- test_type - \"case\", \"group\" or \"all\""); + $this->out("\t\t- test_file - file name with folder prefix and without the (test|group).php suffix"); + $this->out(''); + $this->out('Examples: '); + $this->out("\t\tcake testsuite app all"); + $this->out("\t\tcake testsuite core all"); + $this->out(''); + $this->out("\t\tcake testsuite app case behaviors/debuggable"); + $this->out("\t\tcake testsuite app case models/my_model"); + $this->out("\t\tcake testsuite app case controllers/my_controller"); + $this->out(''); + $this->out("\t\tcake testsuite core case file"); + $this->out("\t\tcake testsuite core case router"); + $this->out("\t\tcake testsuite core case set"); + $this->out(''); + $this->out("\t\tcake testsuite app group mygroup"); + $this->out("\t\tcake testsuite core group acl"); + $this->out("\t\tcake testsuite core group socket"); + $this->out(''); + $this->out("\t\tcake testsuite bugs case models/bug"); + $this->out("\t\t // for the plugin 'bugs' and its test case 'models/bug'"); + $this->out("\t\tcake testsuite bugs group bug"); + $this->out("\t\t // for the plugin bugs and its test group 'bug'"); + $this->out(''); + $this->out('Code Coverage Analysis: '); + $this->out("\n\nAppend 'cov' to any of the above in order to enable code coverage analysis"); + } +/** + * Checks if the arguments supplied point to a valid test file and thus the shell can be run. + * + * @return bool true if it's a valid test file, false otherwise + * @access private + */ + function __canRun() { + $isNeitherAppNorCore = !in_array($this->category, array('app', 'core')); + $isPlugin = in_array(Inflector::underscore($this->category), $this->plugins); + + if ($isNeitherAppNorCore && !$isPlugin) { + $this->err($this->category.' is an invalid test category (either "app", "core" or name of a plugin)'); + return false; + } + + $folder = $this->__findFolderByCategory($this->category); + if (!file_exists($folder)) { + $this->err($folder . ' not found'); + return false; + } + + if (!in_array($this->type, array('all', 'group', 'case'))) { + $this->err($this->type.' is invalid. Should be case, group or all'); + return false; + } + + switch ($this->type) { + case 'all': + return true; + break; + case 'group': + if (file_exists($folder.DS.'groups'.DS.$this->file.'.group.php')) { + return true; + } + break; + case 'case': + if ($this->category == 'app' && file_exists($folder.DS.'cases'.DS.$this->file.'.test.php')) { + return true; + } + + if ($this->category == 'core' && file_exists($folder.DS.'cases'.DS.'libs'.DS.$this->file.'.test.php')) { + return true; + } + + if ($isPlugin && file_exists($folder.DS.'cases'.DS.$this->file.'.test.php')) { + return true; + } + break; + } + + $this->err($this->category.' '.$this->type.' '.$this->file.' is an invalid test identifier'); + return false; + } +/** + * Executes the tests depending on our settings + * + * @return void + * @access private + */ + function __run() { + $reporter = new CLIReporter(); + $this->__setGetVars(); + + if ($this->type == 'all') { + return TestManager::runAllTests($reporter); + } + + if ($this->doCoverage) { + if (!extension_loaded('xdebug')) { + $this->out('You must install Xdebug to use the CakePHP(tm) Code Coverage Analyzation. Download it from http://www.xdebug.org/docs/install'); + exit(0); + } + } + + if ($this->type == 'group') { + $ucFirstGroup = ucfirst($this->file); + + $path = CORE_TEST_GROUPS; + if ($this->category == 'app') { + $path = APP_TEST_GROUPS; + } elseif ($this->isPluginTest) { + $path = APP.'plugins'.DS.$this->category.DS.'tests'.DS.'groups'; + } + + if ($this->doCoverage) { + require_once CAKE . 'tests' . DS . 'lib' . DS . 'code_coverage_manager.php'; + CodeCoverageManager::start($ucFirstGroup, $reporter); + } + $result = TestManager::runGroupTest($ucFirstGroup, $reporter); + if ($this->doCoverage) { + CodeCoverageManager::report(); + } + return $result; + } + + $case = 'libs'.DS.$this->file.'.test.php'; + if ($this->category == 'app') { + $case = $this->file.'.test.php'; + } elseif ($this->isPluginTest) { + $case = $this->file.'.test.php'; + } + + if ($this->doCoverage) { + require_once CAKE . 'tests' . DS . 'lib' . DS . 'code_coverage_manager.php'; + CodeCoverageManager::start($case, $reporter); + } + + $result = TestManager::runTestCase($case, $reporter); + if ($this->doCoverage) { + CodeCoverageManager::report(); + } + + return $result; + } +/** + * Finds the correct folder to look for tests for based on the input category + * + * @return string the folder path + * @access private + */ + function __findFolderByCategory($category) { + $folder = ''; + $paths = array( + 'core' => CAKE, + 'app' => APP + ); + + if (array_key_exists($category, $paths)) { + $folder = $paths[$category] . 'tests'; + } else { + $scoredCategory = Inflector::underscore($category); + $folder = APP . 'plugins' . DS . $scoredCategory . DS; + $pluginPaths = Configure::read('pluginPaths'); + foreach ($pluginPaths as $path) { + if (file_exists($path . $scoredCategory . DS . 'tests')) { + $folder = $path . $scoredCategory . DS . 'tests'; + break; + } + } + } + return $folder; + } +/** + * Sets some get vars needed for TestManager + * + * @return void + * @access private + */ + function __setGetVars() { + if (in_array($this->category, $this->plugins)) { + $_GET['plugin'] = $this->category; + } elseif (in_array(Inflector::Humanize($this->category), $this->plugins)) { + $_GET['plugin'] = Inflector::Humanize($this->category); + } elseif ($this->category == 'app') { + $_GET['app'] = true; + } + if ($this->type == 'group') { + $_GET['group'] = true; + } + } +/** + * tries to install simpletest and exits gracefully if it is not there + * + * @return void + * @access private + */ + function __installSimpleTest() { + if (!App::import('Vendor', 'simpletest' . DS . 'reporter')) { + $this->err('Sorry, Simpletest could not be found. Download it from http://simpletest.org and install it to your vendors directory.'); + exit; + } + } +} +?> \ No newline at end of file diff --git a/cake/dispatcher.php b/cake/dispatcher.php new file mode 100755 index 0000000..ca147f3 --- /dev/null +++ b/cake/dispatcher.php @@ -0,0 +1,689 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Dispatcher takes the URL information, parses it for paramters and + * tells the involved controllers what to do. + * + * This is the heart of Cake's operation. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * List of helpers to include + */ +App::import('Core', array('Router', 'Controller')); +/** + * Dispatcher translates URLs to controller-action-paramter triads. + * + * Dispatches the request, creating appropriate models and controllers. + * + * @package cake + * @subpackage cake.cake + */ +class Dispatcher extends Object { +/** + * Base URL + * + * @var string + * @access public + */ + var $base = false; +/** + * webroot path + * + * @var string + * @access public + */ + var $webroot = '/'; +/** + * Current URL + * + * @var string + * @access public + */ + var $here = false; +/** + * Admin route (if on it) + * + * @var string + * @access public + */ + var $admin = false; +/** + * Plugin being served (if any) + * + * @var string + * @access public + */ + var $plugin = null; +/** + * the params for this request + * + * @var string + * @access public + */ + var $params = null; +/** + * Constructor. + */ + function __construct($url = null, $base = false) { + if ($base !== false) { + Configure::write('App.base', $base); + } + if ($url !== null) { + return $this->dispatch($url); + } + } +/** + * Dispatches and invokes given URL, handing over control to the involved controllers, and then renders the results (if autoRender is set). + * + * If no controller of given name can be found, invoke() shows error messages in + * the form of Missing Controllers information. It does the same with Actions (methods of Controllers are called + * Actions). + * + * @param string $url URL information to work on + * @param array $additionalParams Settings array ("bare", "return") which is melded with the GET and POST params + * @return boolean Success + * @access public + */ + function dispatch($url = null, $additionalParams = array()) { + if ($this->base === false) { + $this->base = $this->baseUrl(); + } + + if (is_array($url)) { + $url = $this->__extractParams($url, $additionalParams); + } else { + if ($url) { + $_GET['url'] = $url; + } + $url = $this->getUrl(); + $this->params = array_merge($this->parseParams($url), $additionalParams); + } + + $this->here = $this->base . '/' . $url; + + if ($this->cached($url)) { + $this->_stop(); + } + + $controller =& $this->__getController(); + + if (!is_object($controller)) { + Router::setRequestInfo(array($this->params, array('base' => $this->base, 'webroot' => $this->webroot))); + return $this->cakeError('missingController', array(array( + 'className' => Inflector::camelize($this->params['controller']) . 'Controller', + 'webroot' => $this->webroot, + 'url' => $url, + 'base' => $this->base + ))); + } + + $privateAction = (bool)(strpos($this->params['action'], '_', 0) === 0); + $prefixes = Router::prefixes(); + + if (!empty($prefixes)) { + if (isset($this->params['prefix'])) { + $this->params['action'] = $this->params['prefix'] . '_' . $this->params['action']; + } elseif (strpos($this->params['action'], '_') !== false && !$privateAction) { + list($prefix, $action) = explode('_', $this->params['action']); + $privateAction = in_array($prefix, $prefixes); + } + } + + Router::setRequestInfo(array( + $this->params, array('base' => $this->base, 'here' => $this->here, 'webroot' => $this->webroot) + )); + + if ($privateAction) { + return $this->cakeError('privateAction', array(array( + 'className' => Inflector::camelize($this->params['controller'] . "Controller"), + 'action' => $this->params['action'], + 'webroot' => $this->webroot, + 'url' => $url, + 'base' => $this->base + ))); + } + + $controller->base = $this->base; + $controller->here = $this->here; + $controller->webroot = $this->webroot; + $controller->plugin = $this->plugin; + $controller->params =& $this->params; + $controller->action =& $this->params['action']; + $controller->passedArgs = array_merge($this->params['pass'], $this->params['named']); + + if (!empty($this->params['data'])) { + $controller->data =& $this->params['data']; + } else { + $controller->data = null; + } + if (array_key_exists('return', $this->params) && $this->params['return'] == 1) { + $controller->autoRender = false; + } + if (!empty($this->params['bare'])) { + $controller->autoLayout = false; + } + if (array_key_exists('layout', $this->params)) { + if (empty($this->params['layout'])) { + $controller->autoLayout = false; + } else { + $controller->layout = $this->params['layout']; + } + } + if (isset($this->params['viewPath'])) { + $controller->viewPath = $this->params['viewPath']; + } + return $this->_invoke($controller, $this->params); + } +/** + * Invokes given controller's render action if autoRender option is set. Otherwise the + * contents of the operation are returned as a string. + * + * @param object $controller Controller to invoke + * @param array $params Parameters with at least the 'action' to invoke + * @param boolean $missingAction Set to true if missing action should be rendered, false otherwise + * @return string Output as sent by controller + * @access protected + */ + function _invoke(&$controller, $params) { + $controller->constructClasses(); + $controller->Component->initialize($controller); + $controller->beforeFilter(); + $controller->Component->startup($controller); + + $methods = array_flip($controller->methods); + + if (!isset($methods[strtolower($params['action'])])) { + if ($controller->scaffold !== false) { + App::import('Core', 'Scaffold'); + return new Scaffold($controller, $params); + } + return $this->cakeError('missingAction', array(array( + 'className' => Inflector::camelize($params['controller']."Controller"), + 'action' => $params['action'], + 'webroot' => $this->webroot, + 'url' => $this->here, + 'base' => $this->base + ))); + } + $output = $controller->dispatchMethod($params['action'], $params['pass']); + + if ($controller->autoRender) { + $controller->output = $controller->render(); + } elseif (empty($controller->output)) { + $controller->output = $output; + } + $controller->Component->shutdown($controller); + $controller->afterFilter(); + + if (isset($params['return'])) { + return $controller->output; + } + echo($controller->output); + } +/** + * Sets the params when $url is passed as an array to Object::requestAction(); + * + * @param array $url + * @param array $additionalParams + * @return string $url + * @access private + */ + function __extractParams($url, $additionalParams = array()) { + $defaults = array('pass' => array(), 'named' => array(), 'form' => array()); + $this->params = array_merge($defaults, $url, $additionalParams); + return Router::url($url); + } +/** + * Returns array of GET and POST parameters. GET parameters are taken from given URL. + * + * @param string $fromUrl URL to mine for parameter information. + * @return array Parameters found in POST and GET. + * @access public + */ + function parseParams($fromUrl) { + $params = array(); + + if (isset($_POST)) { + $params['form'] = $_POST; + if (ini_get('magic_quotes_gpc') === '1') { + $params['form'] = stripslashes_deep($params['form']); + } + if (env('HTTP_X_HTTP_METHOD_OVERRIDE')) { + $params['form']['_method'] = env('HTTP_X_HTTP_METHOD_OVERRIDE'); + } + if (isset($params['form']['_method'])) { + if (isset($_SERVER) && !empty($_SERVER)) { + $_SERVER['REQUEST_METHOD'] = $params['form']['_method']; + } else { + $_ENV['REQUEST_METHOD'] = $params['form']['_method']; + } + unset($params['form']['_method']); + } + } + $namedExpressions = Router::getNamedExpressions(); + extract($namedExpressions); + include CONFIGS . 'routes.php'; + $params = array_merge(Router::parse($fromUrl), $params); + + if (strlen($params['action']) === 0) { + $params['action'] = 'index'; + } + if (isset($params['form']['data'])) { + $params['data'] = Router::stripEscape($params['form']['data']); + unset($params['form']['data']); + } + if (isset($_GET)) { + if (ini_get('magic_quotes_gpc') === '1') { + $url = stripslashes_deep($_GET); + } else { + $url = $_GET; + } + if (isset($params['url'])) { + $params['url'] = array_merge($params['url'], $url); + } else { + $params['url'] = $url; + } + } + + foreach ($_FILES as $name => $data) { + if ($name != 'data') { + $params['form'][$name] = $data; + } + } + + if (isset($_FILES['data'])) { + foreach ($_FILES['data'] as $key => $data) { + foreach ($data as $model => $fields) { + if (is_array($fields)) { + foreach ($fields as $field => $value) { + if (is_array($value)) { + foreach ($value as $k => $v) { + $params['data'][$model][$field][$k][$key] = $v; + } + } else { + $params['data'][$model][$field][$key] = $value; + } + } + } else { + $params['data'][$model][$key] = $fields; + } + } + } + } + return $params; + } +/** + * Returns a base URL and sets the proper webroot + * + * @return string Base URL + * @access public + */ + function baseUrl() { + $dir = $webroot = null; + $config = Configure::read('App'); + extract($config); + + if (!$base) { + $base = $this->base; + } + if ($base !== false) { + $this->webroot = $base . '/'; + return $this->base = $base; + } + if (!$baseUrl) { + $replace = array('<', '>', '*', '\'', '"'); + $base = str_replace($replace, '', dirname(env('PHP_SELF'))); + + if ($webroot === 'webroot' && $webroot === basename($base)) { + $base = dirname($base); + } + if ($dir === 'app' && $dir === basename($base)) { + $base = dirname($base); + } + + if ($base === DS || $base === '.') { + $base = ''; + } + + $this->webroot = $base .'/'; + return $base; + } + $file = null; + + if ($baseUrl) { + $file = '/' . basename($baseUrl); + $base = dirname($baseUrl); + + if ($base === DS || $base === '.') { + $base = ''; + } + $this->webroot = $base .'/'; + + if (strpos($this->webroot, $dir) === false) { + $this->webroot .= $dir . '/' ; + } + if (strpos($this->webroot, $webroot) === false) { + $this->webroot .= $webroot . '/'; + } + return $base . $file; + } + return false; + } +/** + * Restructure params in case we're serving a plugin. + * + * @param array $params Array on where to re-set 'controller', 'action', and 'pass' indexes + * @param boolean $reverse + * @return array Restructured array + * @access protected + */ + function _restructureParams($params, $reverse = false) { + if ($reverse === true) { + extract(Router::getArgs($params['action'])); + $params = array_merge($params, array( + 'controller'=> $params['plugin'], + 'action'=> $params['controller'], + 'pass' => array_merge($pass, $params['pass']), + 'named' => array_merge($named, $params['named']) + )); + $this->plugin = $params['plugin']; + } else { + $params['plugin'] = $params['controller']; + $params['controller'] = $params['action']; + if (isset($params['pass'][0])) { + $params['action'] = $params['pass'][0]; + array_shift($params['pass']); + } else { + $params['action'] = null; + } + } + return $params; + } +/** + * Get controller to use, either plugin controller or application controller + * + * @param array $params Array of parameters + * @return mixed name of controller if not loaded, or object if loaded + * @access private + */ + function &__getController($params = null) { + if (!is_array($params)) { + $original = $params = $this->params; + } + $controller = false; + $ctrlClass = $this->__loadController($params); + if (!$ctrlClass) { + if (!isset($params['plugin'])) { + $params = $this->_restructureParams($params); + } else { + if (empty($original['pass']) && $original['action'] == 'index') { + $params['action'] = null; + } + $params = $this->_restructureParams($params, true); + } + $ctrlClass = $this->__loadController($params); + if (!$ctrlClass) { + $this->params = $original; + return $controller; + } + } else { + $params = $this->params; + } + $name = $ctrlClass; + $ctrlClass = $ctrlClass . 'Controller'; + if (class_exists($ctrlClass)) { + if (strtolower(get_parent_class($ctrlClass)) === strtolower($name . 'AppController') && empty($params['plugin'])) { + $params = $this->_restructureParams($params); + $params = $this->_restructureParams($params, true); + } + $this->params = $params; + $controller =& new $ctrlClass(); + } + return $controller; + } +/** + * Load controller and return controller class + * + * @param array $params Array of parameters + * @return string|bool Name of controller class name + * @access private + */ + function __loadController($params) { + $pluginName = $pluginPath = $controller = null; + if (!empty($params['plugin'])) { + $this->plugin = $params['plugin']; + $pluginName = Inflector::camelize($params['plugin']); + $pluginPath = $pluginName . '.'; + $this->params['controller'] = $this->plugin; + $controller = $pluginName; + } + if (!empty($params['controller'])) { + $this->params['controller'] = $params['controller']; + $controller = Inflector::camelize($params['controller']); + } + if ($pluginPath . $controller) { + if (App::import('Controller', $pluginPath . $controller)) { + return $controller; + } + } + return false; + } +/** + * Returns the REQUEST_URI from the server environment, or, failing that, + * constructs a new one, using the PHP_SELF constant and other variables. + * + * @return string URI + * @access public + */ + function uri() { + foreach (array('HTTP_X_REWRITE_URL', 'REQUEST_URI', 'argv') as $var) { + if ($uri = env($var)) { + if ($var == 'argv') { + $uri = $uri[0]; + } + break; + } + } + $base = preg_replace('/^\//', '', '' . Configure::read('App.baseUrl')); + + if ($base) { + $uri = preg_replace('/^(?:\/)?(?:' . preg_quote($base, '/') . ')?(?:url=)?/', '', $uri); + } + if (PHP_SAPI == 'isapi') { + $uri = preg_replace('/^(?:\/)?(?:\/)?(?:\?)?(?:url=)?/', '', $uri); + } + if (!empty($uri)) { + if (key($_GET) && strpos(key($_GET), '?') !== false) { + unset($_GET[key($_GET)]); + } + $uri = preg_split('/\?/', $uri, 2); + + if (isset($uri[1])) { + parse_str($uri[1], $_GET); + } + $uri = $uri[0]; + } else { + $uri = env('QUERY_STRING'); + } + if (is_string($uri) && strpos($uri, 'index.php') !== false) { + list(, $uri) = explode('index.php', $uri, 2); + } + if (empty($uri) || $uri == '/' || $uri == '//') { + return ''; + } + return str_replace('//', '/', '/' . $uri); + } +/** + * Returns and sets the $_GET[url] derived from the REQUEST_URI + * + * @param string $uri Request URI + * @param string $base Base path + * @return string URL + * @access public + */ + function getUrl($uri = null, $base = null) { + if (empty($_GET['url'])) { + if ($uri == null) { + $uri = $this->uri(); + } + if ($base == null) { + $base = $this->base; + } + $url = null; + $tmpUri = preg_replace('/^(?:\?)?(?:\/)?/', '', $uri); + $baseDir = preg_replace('/^\//', '', dirname($base)) . '/'; + + if ($tmpUri === '/' || $tmpUri == $baseDir || $tmpUri == $base) { + $url = $_GET['url'] = '/'; + } else { + if ($base && strpos($uri, $base) !== false) { + $elements = explode($base, $uri); + } elseif (preg_match('/^[\/\?\/|\/\?|\?\/]/', $uri)) { + $elements = array(1 => preg_replace('/^[\/\?\/|\/\?|\?\/]/', '', $uri)); + } else { + $elements = array(); + } + + if (!empty($elements[1])) { + $_GET['url'] = $elements[1]; + $url = $elements[1]; + } else { + $url = $_GET['url'] = '/'; + } + + if (strpos($url, '/') === 0 && $url != '/') { + $url = $_GET['url'] = substr($url, 1); + } + } + } else { + $url = $_GET['url']; + } + if ($url{0} == '/') { + $url = substr($url, 1); + } + return $url; + } +/** + * Outputs cached dispatch for js, css, img, view cache + * + * @param string $url Requested URL + * @access public + */ + function cached($url) { + if (strpos($url, 'css/') !== false || strpos($url, 'js/') !== false || strpos($url, 'img/') !== false) { + if (strpos($url, 'ccss/') === 0) { + include WWW_ROOT . DS . Configure::read('Asset.filter.css'); + $this->_stop(); + } elseif (strpos($url, 'cjs/') === 0) { + include WWW_ROOT . DS . Configure::read('Asset.filter.js'); + $this->_stop(); + } + $isAsset = false; + $assets = array('js' => 'text/javascript', 'css' => 'text/css', 'gif' => 'image/gif', 'jpg' => 'image/jpeg', 'png' => 'image/png'); + $ext = array_pop(explode('.', $url)); + + foreach ($assets as $type => $contentType) { + if ($type === $ext) { + if ($type === 'css' || $type === 'js') { + $pos = strpos($url, $type . '/'); + } else { + $pos = strpos($url, 'img/'); + } + $isAsset = true; + break; + } + } + + if ($isAsset === true) { + $ob = @ini_get("zlib.output_compression") !== '1' && extension_loaded("zlib") && (strpos(env('HTTP_ACCEPT_ENCODING'), 'gzip') !== false); + + if ($ob && Configure::read('Asset.compress')) { + ob_start(); + ob_start('ob_gzhandler'); + } + $assetFile = null; + $paths = array(); + + if ($pos > 0) { + $plugin = substr($url, 0, $pos - 1); + $url = preg_replace('/^' . preg_quote($plugin, '/') . '\//i', '', $url); + $pluginPaths = Configure::read('pluginPaths'); + $count = count($pluginPaths); + for ($i = 0; $i < $count; $i++) { + $paths[] = $pluginPaths[$i] . $plugin . DS . 'vendors' . DS; + } + } + $paths = array_merge($paths, Configure::read('vendorPaths')); + foreach ($paths as $path) { + if (is_file($path . $url) && file_exists($path . $url)) { + $assetFile = $path . $url; + break; + } + } + + if ($assetFile !== null) { + $fileModified = filemtime($assetFile); + header("Date: " . date("D, j M Y G:i:s ", $fileModified) . 'GMT'); + header('Content-type: ' . $assets[$type]); + header("Expires: " . gmdate("D, j M Y H:i:s", time() + DAY) . " GMT"); + header("Cache-Control: cache"); + header("Pragma: cache"); + if ($type === 'css' || $type === 'js') { + include($assetFile); + } else { + readfile($assetFile); + } + + if (Configure::read('Asset.compress')) { + ob_end_flush(); + } + return true; + } + } + } + + if (Configure::read('Cache.check') === true) { + $path = $this->here; + if ($this->here == '/') { + $path = 'home'; + } + $path = strtolower(Inflector::slug($path)); + + $filename = CACHE . 'views' . DS . $path . '.php'; + + if (!file_exists($filename)) { + $filename = CACHE . 'views' . DS . $path . '_index.php'; + } + + if (file_exists($filename)) { + if (!class_exists('View')) { + App::import('Core', 'View'); + } + $controller = null; + $view =& new View($controller, false); + return $view->renderCache($filename, getMicrotime()); + } + } + return false; + } +} +?> \ No newline at end of file diff --git a/cake/libs/cache.php b/cake/libs/cache.php new file mode 100755 index 0000000..aabd279 --- /dev/null +++ b/cake/libs/cache.php @@ -0,0 +1,506 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Caching for CakePHP. + * + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v 1.2.0.4933 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Caching for CakePHP. + * + * @package cake + * @subpackage cake.cake.libs + */ +class Cache extends Object { +/** + * Cache engine to use + * + * @var CacheEngine + * @access protected + */ + var $_Engine = null; +/** + * Cache configuration stack + * + * @var array + * @access private + */ + var $__config = array(); +/** + * Holds name of the current configuration being used + * + * @var array + * @access private + */ + var $__name = 'default'; +/** + * whether to reset the settings with the next call to self::set(); + * + * @var array + * @access private + */ + var $__reset = false; +/** + * Returns a singleton instance + * + * @return object + * @access public + * @static + */ + function &getInstance() { + static $instance = array(); + if (!$instance) { + $instance[0] =& new Cache(); + } + return $instance[0]; + } +/** + * 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) { + if (!class_exists($name . 'Engine')) { + require LIBS . DS . 'cache' . DS . strtolower($name) . '.php'; + } + return true; + } +/** + * Set the cache configuration to use + * + * @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()) { + $_this =& Cache::getInstance(); + if (is_array($name)) { + $settings = $name; + } + + if ($name === null || !is_string($name)) { + $name = $_this->__name; + } + + $current = array(); + if (isset($_this->__config[$name])) { + $current = $_this->__config[$name]; + } + + if (!empty($settings)) { + $_this->__name = null; + $_this->__config[$name] = array_merge($current, $settings); + } + + if (empty($_this->__config[$name]['engine'])) { + return false; + } + + $_this->__name = $name; + $engine = $_this->__config[$name]['engine']; + + if (!$_this->isInitialized($engine)) { + if ($_this->engine($engine, $_this->__config[$name]) === false) { + return false; + } + $settings = $_this->__config[$name] = $_this->settings($engine); + } else { + $settings = $_this->__config[$name] = $_this->set($_this->__config[$name]); + } + return compact('engine', 'settings'); + } +/** + * Set the cache engine to use or modify settings for one instance + * + * @param string $name Name of the engine (without 'Engine') + * @param array $settings Optional associative array of settings passed to the engine + * @return boolean True on success, false on failure + * @access public + * @static + */ + function engine($name = 'File', $settings = array()) { + $cacheClass = $name . 'Engine'; + $_this =& Cache::getInstance(); + if (!isset($_this->_Engine[$name])) { + if ($_this->__loadEngine($name) === false) { + return false; + } + $_this->_Engine[$name] =& new $cacheClass(); + } + + if ($_this->_Engine[$name]->init($settings)) { + if (time() % $_this->_Engine[$name]->settings['probability'] === 0) { + $_this->_Engine[$name]->gc(); + } + return true; + } + $_this->_Engine[$name] = null; + return 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 of settings + * @access public + * @static + */ + function set($settings = array(), $value = null) { + $_this =& Cache::getInstance(); + if (!isset($_this->__config[$_this->__name])) { + return false; + } + + $engine = $_this->__config[$_this->__name]['engine']; + + if (!empty($settings)) { + $_this->__reset = true; + } + + if ($_this->__reset === true) { + if (empty($settings)) { + $_this->__reset = false; + $settings = $_this->__config[$_this->__name]; + } else { + if (is_string($settings) && $value !== null) { + $settings = array($settings => $value); + } + $settings = array_merge($_this->__config[$_this->__name], $settings); + } + $_this->_Engine[$engine]->init($settings); + } + + return $_this->settings($engine); + } +/** + * Garbage collection + * + * Permanently remove all expired and deleted data + * + * @return void + * @access public + * @static + */ + function gc() { + $_this =& Cache::getInstance(); + $config = $_this->config(); + $_this->_Engine[$config['engine']]->gc(); + } +/** + * Write data for key into cache + * + * @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 + * @return boolean True if the data was successfully cached, false on failure + * @access public + * @static + */ + function write($key, $value, $config = null) { + $_this =& Cache::getInstance(); + + if (is_array($config)) { + extract($config); + } else if ($config && (is_numeric($config) || is_numeric($config[0]) || (isset($config[1]) && is_numeric($config[1])))) { + $config = null; + } + + if ($config && isset($_this->__config[$config])) { + $settings = $_this->set($_this->__config[$config]); + } else { + $settings = $_this->settings(); + } + + if (empty($settings)) { + return null; + } + extract($settings); + + if (!$_this->isInitialized($engine)) { + return false; + } + + if (!$key = $_this->_Engine[$engine]->key($key)) { + return false; + } + + if (is_resource($value)) { + return false; + } + + if ($duration < 1) { + return false; + } + + $success = $_this->_Engine[$engine]->write($settings['prefix'] . $key, $value, $duration); + $settings = $_this->set(); + return $success; + } +/** + * Read a key from the cache + * + * @param string $key Identifier for the data + * @param string $config 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) { + $_this =& Cache::getInstance(); + + if (isset($_this->__config[$config])) { + $settings = $_this->set($_this->__config[$config]); + } else { + $settings = $_this->settings(); + } + + if (empty($settings)) { + return null; + } + extract($settings); + + if (!$_this->isInitialized($engine)) { + return false; + } + if (!$key = $_this->_Engine[$engine]->key($key)) { + return false; + } + $success = $_this->_Engine[$engine]->read($settings['prefix'] . $key); + + if ($config !== null && $config !== $_this->__name) { + $settings = $_this->set(); + } + return $success; + } +/** + * Delete a key from the cache + * + * @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) { + $_this =& Cache::getInstance(); + if (isset($_this->__config[$config])) { + $settings = $_this->set($_this->__config[$config]); + } else { + $settings = $_this->settings(); + } + + if (empty($settings)) { + return null; + } + extract($settings); + + if (!$_this->isInitialized($engine)) { + return false; + } + + if (!$key = $_this->_Engine[$engine]->key($key)) { + return false; + } + + $success = $_this->_Engine[$engine]->delete($settings['prefix'] . $key); + $settings = $_this->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) { + $_this =& Cache::getInstance(); + if (isset($_this->__config[$config])) { + $settings = $_this->set($_this->__config[$config]); + } else { + $settings = $_this->settings(); + } + + if (empty($settings)) { + return null; + } + extract($settings); + + if (isset($engine) && !$_this->isInitialized($engine)) { + return false; + } + $success = $_this->_Engine[$engine]->clear($check); + $settings = $_this->set(); + return $success; + } +/** + * Check if Cache has initialized a working storage engine + * + * @param string $engine Name of the engine + * @param string $config Name of the configuration setting + * @return bool + * @access public + * @static + */ + function isInitialized($engine = null) { + if (Configure::read('Cache.disable')) { + return false; + } + $_this =& Cache::getInstance(); + if (!$engine && isset($_this->__config[$_this->__name]['engine'])) { + $engine = $_this->__config[$_this->__name]['engine']; + } + return isset($_this->_Engine[$engine]); + } + +/** + * Return the settings for current cache engine + * + * @param string $engine Name of the engine + * @return array list of settings for this engine + * @access public + * @static + */ + function settings($engine = null) { + $_this =& Cache::getInstance(); + if (!$engine && isset($_this->__config[$_this->__name]['engine'])) { + $engine = $_this->__config[$_this->__name]['engine']; + } + + if (isset($_this->_Engine[$engine]) && !is_null($_this->_Engine[$engine])) { + return $_this->_Engine[$engine]->settings(); + } + return array(); + } +} +/** + * Storage engine for CakePHP caching + * + * @package cake + * @subpackage cake.cake.libs + */ +class CacheEngine extends Object { +/** + * settings of current engine instance + * + * @var int + * @access public + */ + var $settings = array(); +/** + * Iitialize 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); + } +/** + * 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 + * + * @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; + } +} +?> \ No newline at end of file diff --git a/cake/libs/cache/apc.php b/cake/libs/cache/apc.php new file mode 100755 index 0000000..8e2a781 --- /dev/null +++ b/cake/libs/cache/apc.php @@ -0,0 +1,97 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * APC storage engine for cache. + * + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.cache + * @since CakePHP(tm) v 1.2.0.4933 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * 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); + } +/** + * 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'); + } +} +?> \ No newline at end of file diff --git a/cake/libs/cache/file.php b/cake/libs/cache/file.php new file mode 100755 index 0000000..abbd5a0 --- /dev/null +++ b/cake/libs/cache/file.php @@ -0,0 +1,269 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * File Storage engine for cache + * + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.cache + * @since CakePHP(tm) v 1.2.0.4933 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * 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 private + */ + 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(); +/** + * Set to true if FileEngine::init(); and FileEngine::__active(); do not fail. + * + * @var boolean + * @access private + */ + var $__active = false; +/** + * True unless FileEngine::__active(); fails + * + * @var boolean + * @access private + */ + 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)) { + if (!class_exists('File')) { + require LIBS . 'file.php'; + } + $this->__File =& new File($this->settings['path'] . DS . 'cake'); + } + + if (DIRECTORY_SEPARATOR === '\\') { + $this->settings['isWindows'] = true; + } + + $this->settings['path'] = $this->__File->Folder->cd($this->settings['path']); + if (empty($this->settings['path'])) { + return false; + } + 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']; + } + while (($entry = $dir->read()) !== false) { + 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->__active && $this->__init && !is_writable($this->settings['path'])) { + $this->__init = false; + trigger_error(sprintf(__('%s is not writable', true), $this->settings['path']), E_USER_WARNING); + } else { + $this->__active = true; + } + return true; + } +} +?> \ No newline at end of file diff --git a/cake/libs/cache/memcache.php b/cake/libs/cache/memcache.php new file mode 100755 index 0000000..2d70a6e --- /dev/null +++ b/cake/libs/cache/memcache.php @@ -0,0 +1,158 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Memcache storage engine for cache + * + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.cache + * @since CakePHP(tm) v 1.2.0.4933 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Memcache storage engine for cache + * + * @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 + * 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) { + $parts = explode(':', $server); + $host = $parts[0]; + $port = 11211; + if (isset($parts[1])) { + $port = $parts[1]; + } + if ($this->__Memcache->addServer($host, $port)) { + $return = true; + } + } + return $return; + } + return true; + } +/** + * 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; + $this->__Memcache->set($key.'_expires', $expires, $this->settings['compress'], $expires); + return $this->__Memcache->set($key, $value, $this->settings['compress'], $expires); + } +/** + * 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($this->__Memcache->get($key.'_expires')); + if ($cachetime < $time || ($time + $this->settings['duration']) < $cachetime) { + return false; + } + return $this->__Memcache->get($key); + } +/** + * 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; + } +} +?> diff --git a/cake/libs/cache/xcache.php b/cake/libs/cache/xcache.php new file mode 100755 index 0000000..1ed9b0b --- /dev/null +++ b/cake/libs/cache/xcache.php @@ -0,0 +1,154 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Xcache storage engine for cache. + * + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.cache + * @since CakePHP(tm) v 1.2.0.4947 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * 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; + } +/** + * 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; + } + } + } + } +} +?> \ No newline at end of file diff --git a/cake/libs/cake_log.php b/cake/libs/cake_log.php new file mode 100755 index 0000000..da779b2 --- /dev/null +++ b/cake/libs/cake_log.php @@ -0,0 +1,101 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Logging. + * + * Log messages to text files. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Included libraries. + * + */ + if (!class_exists('File')) { + require LIBS . 'file.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 text files + * + * @package cake + * @subpackage cake.cake.libs + */ +class CakeLog { +/** + * Writes given message to a log file in the logs directory. + * + * @param string $type Type of log, becomes part of the log's filename + * @param string $msg Message to log + * @return boolean Success + * @access public + * @static + */ + function write($type, $msg) { + 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]; + } + + if ($type == 'error' || $type == 'warning') { + $filename = LOGS . 'error.log'; + } elseif (in_array($type, $levels)) { + $filename = LOGS . 'debug.log'; + } else { + $filename = LOGS . $type . '.log'; + } + $output = date('Y-m-d H:i:s') . ' ' . ucfirst($type) . ': ' . $msg . "\n"; + $log = new File($filename, true); + if ($log->writable()) { + return $log->append($output); + } + } +} +?> \ No newline at end of file diff --git a/cake/libs/class_registry.php b/cake/libs/class_registry.php new file mode 100755 index 0000000..5ff5ec3 --- /dev/null +++ b/cake/libs/class_registry.php @@ -0,0 +1,353 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Class collections. + * + * A repository for class objects, each registered with a key. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v 0.9.2 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * 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)) { + $plugin = $pluginPath = null; + $settings = array_merge($defaults, $settings); + $class = $settings['class']; + + if (strpos($class, '.') !== false) { + list($plugin, $class) = explode('.', $class); + $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 + * @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(); + } +} +?> \ No newline at end of file diff --git a/cake/libs/configure.php b/cake/libs/configure.php new file mode 100755 index 0000000..30aeaf5 --- /dev/null +++ b/cake/libs/configure.php @@ -0,0 +1,1194 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Short description for file. + * + * Long description for filec + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v 1.0.0.2363 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Configuration class (singleton). Used for managing runtime configuration information. + * + * @package cake + * @subpackage cake.cake.libs + * @link http://book.cakephp.org/view/42/The-Configuration-Class + */ +class Configure extends Object { +/** + * List of additional path(s) where model files reside. + * + * @var array + * @access public + */ + var $modelPaths = array(); +/** + * List of additional path(s) where behavior files reside. + * + * @var array + * @access public + */ + var $behaviorPaths = array(); +/** + * List of additional path(s) where controller files reside. + * + * @var array + * @access public + */ + var $controllerPaths = array(); +/** + * List of additional path(s) where component files reside. + * + * @var array + * @access public + */ + var $componentPaths = array(); +/** + * List of additional path(s) where view files reside. + * + * @var array + * @access public + */ + var $viewPaths = array(); +/** + * List of additional path(s) where helper files reside. + * + * @var array + * @access public + */ + var $helperPaths = array(); +/** + * List of additional path(s) where plugins reside. + * + * @var array + * @access public + */ + var $pluginPaths = array(); +/** + * List of additional path(s) where vendor packages reside. + * + * @var array + * @access public + */ + var $vendorPaths = array(); +/** + * List of additional path(s) where locale files reside. + * + * @var array + * @access public + */ + var $localePaths = array(); +/** + * List of additional path(s) where console shell files reside. + * + * @var array + * @access public + */ + var $shellPaths = array(); +/** + * Current debug level. + * + * @link http://book.cakephp.org/view/44/CakePHP-Core-Configuration-Variables + * @var integer + * @access public + */ + var $debug = null; +/** + * Determines if $__objects cache should be written. + * + * @var boolean + * @access private + */ + var $__cache = false; +/** + * Holds and key => value array of objects' types. + * + * @var array + * @access private + */ + var $__objects = array(); +/** + * Returns a singleton instance of the Configure class. + * + * @return Configure instance + * @access public + */ + function &getInstance($boot = true) { + static $instance = array(); + if (!$instance) { + $instance[0] =& new Configure(); + $instance[0]->__loadBootstrap($boot); + } + return $instance[0]; + } +/** + * Returns an index of objects of the given type, with the physical path to each object. + * + * @param string $type Type of object, i.e. 'model', 'controller', 'helper', or 'plugin' + * @param mixed $path Optional + * @return Configure instance + * @access public + */ + function listObjects($type, $path = null, $cache = true) { + $objects = array(); + $extension = false; + $name = $type; + + if ($type === 'file' && !$path) { + return false; + } elseif ($type === 'file') { + $extension = true; + $name = $type . str_replace(DS, '', $path); + } + $_this =& Configure::getInstance(); + + if (empty($_this->__objects) && $cache === true) { + $_this->__objects = Cache::read('object_map', '_cake_core_'); + } + + if (empty($_this->__objects) || !isset($_this->__objects[$type]) || $cache !== true) { + $types = array( + 'model' => array('suffix' => '.php', 'base' => 'AppModel', 'core' => false), + 'behavior' => array('suffix' => '.php', 'base' => 'ModelBehavior'), + 'controller' => array('suffix' => '_controller.php', 'base' => 'AppController'), + 'component' => array('suffix' => '.php', 'base' => null), + 'view' => array('suffix' => '.php', 'base' => null), + 'helper' => array('suffix' => '.php', 'base' => 'AppHelper'), + 'plugin' => array('suffix' => '', 'base' => null), + 'vendor' => array('suffix' => '', 'base' => null), + 'class' => array('suffix' => '.php', 'base' => null), + 'file' => array('suffix' => '.php', 'base' => null) + ); + + if (!isset($types[$type])) { + return false; + } + $objects = array(); + + if (empty($path)) { + $path = $_this->{$type . 'Paths'}; + if (isset($types[$type]['core']) && $types[$type]['core'] === false) { + array_pop($path); + } + } + $items = array(); + + foreach ((array)$path as $dir) { + if ($type === 'file' || $type === 'class' || strpos($dir, $type) !== false) { + $items = $_this->__list($dir, $types[$type]['suffix'], $extension); + $objects = array_merge($items, array_diff($objects, $items)); + } + } + + if ($type !== 'file') { + foreach ($objects as $key => $value) { + $objects[$key] = Inflector::camelize($value); + } + } + if ($cache === true && !empty($objects)) { + $_this->__objects[$name] = $objects; + $_this->__cache = true; + } else { + return $objects; + } + } + return $_this->__objects[$name]; + } +/** + * Returns an array of filenames of PHP files in the given directory. + * + * @param string $path Path to scan for files + * @param string $suffix if false, return only directories. if string, match and return files + * @return array List of directories or files in directory + */ + function __list($path, $suffix = false, $extension = false) { + if (!class_exists('Folder')) { + require LIBS . 'folder.php'; + } + $items = array(); + $Folder =& new Folder($path); + $contents = $Folder->read(false, true); + + if (is_array($contents)) { + if (!$suffix) { + return $contents[0]; + } else { + foreach ($contents[1] as $item) { + if (substr($item, - strlen($suffix)) === $suffix) { + if ($extension) { + $items[] = $item; + } else { + $items[] = substr($item, 0, strlen($item) - strlen($suffix)); + } + } + } + } + } + return $items; + } +/** + * Used to store a dynamic variable in the Configure instance. + * + * Usage: + * {{{ + * Configure::write('One.key1', 'value of the Configure::One[key1]'); + * Configure::write(array('One.key1' => 'value of the Configure::One[key1]')); + * Configure::write('One', array( + * 'key1' => 'value of the Configure::One[key1]', + * 'key2' => 'value of the Configure::One[key2]' + * ); + * + * Configure::write(array( + * 'One.key1' => 'value of the Configure::One[key1]', + * 'One.key2' => 'value of the Configure::One[key2]' + * )); + * }}} + * + * @link http://book.cakephp.org/view/412/write + * @param array $config Name of var to write + * @param mixed $value Value to set for var + * @return void + * @access public + */ + function write($config, $value = null) { + $_this =& Configure::getInstance(); + + if (!is_array($config)) { + $config = array($config => $value); + } + + foreach ($config as $names => $value) { + $name = $_this->__configVarNames($names); + + switch (count($name)) { + case 3: + $_this->{$name[0]}[$name[1]][$name[2]] = $value; + break; + case 2: + $_this->{$name[0]}[$name[1]] = $value; + break; + case 1: + $_this->{$name[0]} = $value; + break; + } + } + + if (isset($config['debug'])) { + if ($_this->debug) { + error_reporting(E_ALL & ~E_DEPRECATED); + + if (function_exists('ini_set')) { + ini_set('display_errors', 1); + } + + if (!class_exists('Debugger')) { + require LIBS . 'debugger.php'; + } + if (!class_exists('CakeLog')) { + require LIBS . 'cake_log.php'; + } + Configure::write('log', LOG_NOTICE); + } else { + error_reporting(0); + Configure::write('log', LOG_NOTICE); + } + } + } +/** + * Used to read information stored in the Configure instance. + * + * Usage + * Configure::read('Name'); will return all values for Name + * Configure::read('Name.key'); will return only the value of Configure::Name[key] + * + * @link http://book.cakephp.org/view/413/read + * @param string $var Variable to obtain + * @return string value of Configure::$var + * @access public + */ + function read($var = 'debug') { + $_this =& Configure::getInstance(); + + if ($var === 'debug') { + if (!isset($_this->debug)) { + if (defined('DEBUG')) { + $_this->debug = DEBUG; + } else { + $_this->debug = 0; + } + } + return $_this->debug; + } + $name = $_this->__configVarNames($var); + + switch (count($name)) { + case 3: + if (isset($_this->{$name[0]}[$name[1]][$name[2]])) { + return $_this->{$name[0]}[$name[1]][$name[2]]; + } + break; + case 2: + if (isset($_this->{$name[0]}[$name[1]])) { + return $_this->{$name[0]}[$name[1]]; + } + break; + case 1: + if (isset($_this->{$name[0]})) { + return $_this->{$name[0]}; + } + break; + } + return null; + } +/** + * Used to delete a variable from the Configure instance. + * + * Usage: + * Configure::delete('Name'); will delete the entire Configure::Name + * Configure::delete('Name.key'); will delete only the Configure::Name[key] + * + * @link http://book.cakephp.org/view/414/delete + * @param string $var the var to be deleted + * @return void + * @access public + */ + function delete($var = null) { + $_this =& Configure::getInstance(); + $name = $_this->__configVarNames($var); + + if (count($name) > 1) { + unset($_this->{$name[0]}[$name[1]]); + } else { + unset($_this->{$name[0]}); + } + } +/** + * Loads a file from app/config/configure_file.php. + * Config file variables should be formated like: + * $config['name'] = 'value'; + * These will be used to create dynamic Configure vars. + * + * Usage Configure::load('configure_file'); + * + * @link http://book.cakephp.org/view/415/load + * @param string $fileName name of file to load, extension must be .php and only the name + * should be used, not the extenstion + * @return mixed false if file not found, void if load successful + * @access public + */ + function load($fileName) { + $found = false; + + if (file_exists(CONFIGS . $fileName . '.php')) { + include(CONFIGS . $fileName . '.php'); + $found = true; + } elseif (file_exists(CACHE . 'persistent' . DS . $fileName . '.php')) { + include(CACHE . 'persistent' . DS . $fileName . '.php'); + $found = true; + } else { + foreach (Configure::corePaths('cake') as $key => $path) { + if (file_exists($path . DS . 'config' . DS . $fileName . '.php')) { + include($path . DS . 'config' . DS . $fileName . '.php'); + $found = true; + break; + } + } + } + + if (!$found) { + return false; + } + + if (!isset($config)) { + $error = __("Configure::load() - no variable \$config found in %s.php", true); + trigger_error(sprintf($error, $fileName), E_USER_WARNING); + return false; + } + return Configure::write($config); + } +/** + * Used to determine the current version of CakePHP. + * + * Usage Configure::version(); + * + * @link http://book.cakephp.org/view/416/version + * @return string Current version of CakePHP + * @access public + */ + function version() { + $_this =& Configure::getInstance(); + + if (!isset($_this->Cake['version'])) { + require(CORE_PATH . 'cake' . DS . 'config' . DS . 'config.php'); + $_this->write($config); + } + return $_this->Cake['version']; + } +/** + * Used to write a config file to disk. + * + * Configure::store('Model', 'class.paths', array('Users' => array( + * 'path' => 'users', 'plugin' => true + * ))); + * + * @param string $type Type of config file to write, ex: Models, Controllers, Helpers, Components + * @param string $name file name. + * @param array $data array of values to store. + * @return void + * @access public + */ + function store($type, $name, $data = array()) { + $write = true; + $content = ''; + + foreach ($data as $key => $value) { + $content .= "\$config['$type']['$key']"; + + if (is_array($value)) { + $content .= " = array("; + + foreach ($value as $key1 => $value2) { + $value2 = addslashes($value2); + $content .= "'$key1' => '$value2', "; + } + $content .= ");\n"; + } else { + $value = addslashes($value); + $content .= " = '$value';\n"; + } + } + if (is_null($type)) { + $write = false; + } + Configure::__writeConfig($content, $name, $write); + } +/** + * Returns a key/value list of all paths where core libs are found. + * Passing $type only returns the values for a given value of $key. + * + * @param string $type valid values are: 'model', 'behavior', 'controller', 'component', + * 'view', 'helper', 'datasource', 'libs', and 'cake' + * @return array numeric keyed array of core lib paths + * @access public + */ + function corePaths($type = null) { + $paths = Cache::read('core_paths', '_cake_core_'); + if (!$paths) { + $paths = array(); + $openBasedir = ini_get('open_basedir'); + if ($openBasedir) { + $all = explode(PATH_SEPARATOR, $openBasedir); + $all = array_flip(array_flip((array_merge(array(CAKE_CORE_INCLUDE_PATH), $all)))); + } else { + $all = explode(PATH_SEPARATOR, ini_get('include_path')); + $all = array_flip(array_flip((array_merge(array(CAKE_CORE_INCLUDE_PATH), $all)))); + } + foreach ($all as $path) { + if ($path !== DS) { + $path = rtrim($path, DS); + } + if (empty($path) || $path === '.') { + continue; + } + $cake = $path . DS . 'cake' . DS; + $libs = $cake . 'libs' . DS; + if (is_dir($libs)) { + $paths['libs'][] = $libs; + $paths['model'][] = $libs . 'model' . DS; + $paths['behavior'][] = $libs . 'model' . DS . 'behaviors' . DS; + $paths['controller'][] = $libs . 'controller' . DS; + $paths['component'][] = $libs . 'controller' . DS . 'components' . DS; + $paths['view'][] = $libs . 'view' . DS; + $paths['helper'][] = $libs . 'view' . DS . 'helpers' . DS; + $paths['cake'][] = $cake; + $paths['vendor'][] = $path . DS . 'vendors' . DS; + $paths['shell'][] = $cake . 'console' . DS . 'libs' . DS; + break; + } + } + Cache::write('core_paths', array_filter($paths), '_cake_core_'); + } + if ($type && isset($paths[$type])) { + return $paths[$type]; + } + return $paths; + } +/** + * Creates a cached version of a configuration file. + * Appends values passed from Configure::store() to the cached file + * + * @param string $content Content to write on file + * @param string $name Name to use for cache file + * @param boolean $write true if content should be written, false otherwise + * @return void + * @access private + */ + function __writeConfig($content, $name, $write = true) { + $file = CACHE . 'persistent' . DS . $name . '.php'; + + if (Configure::read() > 0) { + $expires = "+10 seconds"; + } else { + $expires = "+999 days"; + } + $cache = cache('persistent' . DS . $name . '.php', null, $expires); + + if ($cache === null) { + cache('persistent' . DS . $name . '.php', "<?php\n\$config = array();\n", $expires); + } + + if ($write === true) { + if (!class_exists('File')) { + require LIBS . 'file.php'; + } + $fileClass = new File($file); + + if ($fileClass->writable()) { + $fileClass->append($content); + } + } + } +/** + * Checks $name for dot notation to create dynamic Configure::$var as an array when needed. + * + * @param mixed $name Name to split + * @return array Name separated in items through dot notation + * @access private + */ + function __configVarNames($name) { + if (is_string($name)) { + if (strpos($name, ".")) { + return explode(".", $name); + } + return array($name); + } + return $name; + } +/** + * Build path references. Merges the supplied $paths + * with the base paths and the default core paths. + * + * @param array $paths paths defines in config/bootstrap.php + * @return void + * @access public + */ + function buildPaths($paths) { + $_this =& Configure::getInstance(); + $core = $_this->corePaths(); + $basePaths = array( + 'model' => array(MODELS), + 'behavior' => array(BEHAVIORS), + 'controller' => array(CONTROLLERS), + 'component' => array(COMPONENTS), + 'view' => array(VIEWS), + 'helper' => array(HELPERS), + 'plugin' => array(APP . 'plugins' . DS), + 'vendor' => array(APP . 'vendors' . DS, VENDORS), + 'locale' => array(APP . 'locale' . DS), + 'shell' => array(), + 'datasource' => array(MODELS . 'datasources') + ); + + foreach ($basePaths as $type => $default) { + $pathsVar = $type . 'Paths'; + $merge = array(); + + if (isset($core[$type])) { + $merge = $core[$type]; + } + if ($type === 'model' || $type === 'controller' || $type === 'helper') { + $merge = array_merge(array(APP), $merge); + } + + if (!is_array($default)) { + $default = array($default); + } + $_this->{$pathsVar} = $default; + + if (isset($paths[$pathsVar]) && !empty($paths[$pathsVar])) { + $path = array_flip(array_flip((array_merge( + $_this->{$pathsVar}, (array)$paths[$pathsVar], $merge + )))); + $_this->{$pathsVar} = array_values($path); + } else { + $path = array_flip(array_flip((array_merge($_this->{$pathsVar}, $merge)))); + $_this->{$pathsVar} = array_values($path); + } + } + } +/** + * Loads app/config/bootstrap.php. + * If the alternative paths are set in this file + * they will be added to the paths vars. + * + * @param boolean $boot Load application bootstrap (if true) + * @return void + * @access private + */ + function __loadBootstrap($boot) { + $modelPaths = $behaviorPaths = $controllerPaths = $componentPaths = $viewPaths = $helperPaths = $pluginPaths = $vendorPaths = $localePaths = $shellPaths = null; + + if ($boot) { + Configure::write('App', array('base' => false, 'baseUrl' => false, 'dir' => APP_DIR, 'webroot' => WEBROOT_DIR)); + + if (!include(CONFIGS . 'core.php')) { + trigger_error(sprintf(__("Can't find application core file. Please create %score.php, and make sure it is readable by PHP.", true), CONFIGS), E_USER_ERROR); + } + + if (!include(CONFIGS . 'bootstrap.php')) { + trigger_error(sprintf(__("Can't find application bootstrap file. Please create %sbootstrap.php, and make sure it is readable by PHP.", true), CONFIGS), E_USER_ERROR); + } + + if (Configure::read('Cache.disable') !== true) { + $cache = Cache::config('default'); + + if (empty($cache['settings'])) { + trigger_error('Cache not configured properly. Please check Cache::config(); in APP/config/core.php', E_USER_WARNING); + $cache = Cache::config('default', array('engine' => 'File')); + } + $path = $prefix = $duration = null; + + if (!empty($cache['settings']['path'])) { + $path = realpath($cache['settings']['path']); + } else { + $prefix = $cache['settings']['prefix']; + } + + if (Configure::read() >= 1) { + $duration = '+10 seconds'; + } else { + $duration = '+999 days'; + } + + if (Cache::config('_cake_core_') === false) { + Cache::config('_cake_core_', array_merge($cache['settings'], array( + 'prefix' => $prefix . 'cake_core_', 'path' => $path . DS . 'persistent' . DS, + 'serialize' => true, 'duration' => $duration + ))); + } + + if (Cache::config('_cake_model_') === false) { + Cache::config('_cake_model_', array_merge($cache['settings'], array( + 'prefix' => $prefix . 'cake_model_', 'path' => $path . DS . 'models' . DS, + 'serialize' => true, 'duration' => $duration + ))); + } + Cache::config('default'); + } + Configure::buildPaths(compact( + 'modelPaths', 'viewPaths', 'controllerPaths', 'helperPaths', 'componentPaths', + 'behaviorPaths', 'pluginPaths', 'vendorPaths', 'localePaths', 'shellPaths' + )); + } + } +/** + * Caches the object map when the instance of the Configure class is destroyed + * + * @access public + */ + function __destruct() { + if ($this->__cache) { + Cache::write('object_map', array_filter($this->__objects), '_cake_core_'); + } + } +} +/** + * Class and file loader. + * + * @link http://book.cakephp.org/view/499/The-App-Class + * @since CakePHP(tm) v 1.2.0.6001 + * @package cake + * @subpackage cake.cake.libs + */ +class App extends Object { +/** + * Paths to search for files. + * + * @var array + * @access public + */ + var $search = array(); +/** + * Whether or not to return the file that is loaded. + * + * @var boolean + * @access public + */ + var $return = false; +/** + * Determines if $__maps and $__paths cache should be written. + * + * @var boolean + * @access private + */ + var $__cache = false; +/** + * Holds key/value pairs of $type => file path. + * + * @var array + * @access private + */ + var $__map = array(); +/** + * Holds paths for deep searching of files. + * + * @var array + * @access private + */ + var $__paths = array(); +/** + * Holds loaded files. + * + * @var array + * @access private + */ + var $__loaded = array(); +/** + * Finds classes based on $name or specific file(s) to search. + * + * @link http://book.cakephp.org/view/529/Using-App-import + * @param mixed $type The type of Class if passed as a string, or all params can be passed as + * an single array to $type, + * @param string $name Name of the Class or a unique name for the file + * @param mixed $parent boolean true if Class Parent should be searched, accepts key => value + * array('parent' => $parent ,'file' => $file, 'search' => $search, 'ext' => '$ext'); + * $ext allows setting the extension of the file name + * based on Inflector::underscore($name) . ".$ext"; + * @param array $search paths to search for files, array('path 1', 'path 2', 'path 3'); + * @param string $file full name of the file to search for including extension + * @param boolean $return, return the loaded file, the file must have a return + * statement in it to work: return $variable; + * @return boolean true if Class is already in memory or if file is found and loaded, false if not + * @access public + */ + function import($type = null, $name = null, $parent = true, $search = array(), $file = null, $return = false) { + $plugin = $directory = null; + + if (is_array($type)) { + extract($type, EXTR_OVERWRITE); + } + + if (is_array($parent)) { + extract($parent, EXTR_OVERWRITE); + } + + if ($name === null && $file === null) { + $name = $type; + $type = 'Core'; + } elseif ($name === null) { + $type = 'File'; + } + + if (is_array($name)) { + foreach ($name as $class) { + $tempType = $type; + $plugin = null; + + if (strpos($class, '.') !== false) { + $value = explode('.', $class); + $count = count($value); + + if ($count > 2) { + $tempType = $value[0]; + $plugin = $value[1] . '.'; + $class = $value[2]; + } elseif ($count === 2 && ($type === 'Core' || $type === 'File')) { + $tempType = $value[0]; + $class = $value[1]; + } else { + $plugin = $value[0] . '.'; + $class = $value[1]; + } + } + + if (!App::import($tempType, $plugin . $class)) { + return false; + } + } + return true; + } + + if ($name != null && strpos($name, '.') !== false) { + list($plugin, $name) = explode('.', $name); + } + $_this =& App::getInstance(); + $_this->return = $return; + + if (isset($ext)) { + $file = Inflector::underscore($name) . ".$ext"; + } + $ext = $_this->__settings($type, $plugin, $parent); + + if ($name != null && !class_exists($name . $ext['class'])) { + if ($load = $_this->__mapped($name . $ext['class'], $type, $plugin)) { + if ($_this->__load($load)) { + $_this->__overload($type, $name . $ext['class']); + + if ($_this->return) { + $value = include $load; + return $value; + } + return true; + } else { + $_this->__remove($name . $ext['class'], $type, $plugin); + $_this->__cache = true; + } + } + if (!empty($search)) { + $_this->search = $search; + } elseif ($plugin) { + $_this->search = $_this->__paths('plugin'); + } else { + $_this->search = $_this->__paths($type); + } + $find = $file; + + if ($find === null) { + $find = Inflector::underscore($name . $ext['suffix']).'.php'; + + if ($plugin) { + $paths = $_this->search; + foreach ($paths as $key => $value) { + $_this->search[$key] = $value . $ext['path']; + } + $plugin = Inflector::camelize($plugin); + } + } + + if (strtolower($type) !== 'vendor' && empty($search) && $_this->__load($file)) { + $directory = false; + } else { + $file = $find; + $directory = $_this->__find($find, true); + } + + if ($directory !== null) { + $_this->__cache = true; + $_this->__map($directory . $file, $name . $ext['class'], $type, $plugin); + $_this->__overload($type, $name . $ext['class']); + + if ($_this->return) { + $value = include $directory . $file; + return $value; + } + return true; + } + return false; + } + return true; + } +/** + * Returns a single instance of App. + * + * @return object + * @access public + */ + function &getInstance() { + static $instance = array(); + if (!$instance) { + $instance[0] =& new App(); + $instance[0]->__map = Cache::read('file_map', '_cake_core_'); + } + return $instance[0]; + } +/** + * Locates the $file in $__paths, searches recursively. + * + * @param string $file full file name + * @param boolean $recursive search $__paths recursively + * @return mixed boolean on fail, $file directory path on success + * @access private + */ + function __find($file, $recursive = true) { + if (empty($this->search)) { + return null; + } elseif (is_string($this->search)) { + $this->search = array($this->search); + } + + if (empty($this->__paths)) { + $this->__paths = Cache::read('dir_map', '_cake_core_'); + } + + foreach ($this->search as $path) { + $path = rtrim($path, DS); + + if ($path === rtrim(APP, DS)) { + $recursive = false; + } + if ($recursive === false) { + if ($this->__load($path . DS . $file)) { + return $path . DS; + } + continue; + } + if (!isset($this->__paths[$path])) { + if (!class_exists('Folder')) { + require LIBS . 'folder.php'; + } + $Folder =& new Folder(); + $directories = $Folder->tree($path, false, 'dir'); + $this->__paths[$path] = $directories; + } + + foreach ($this->__paths[$path] as $directory) { + if ($this->__load($directory . DS . $file)) { + return $directory . DS; + } + } + } + return null; + } +/** + * Attempts to load $file. + * + * @param string $file full path to file including file name + * @return boolean + * @access private + */ + function __load($file) { + if (empty($file)) { + return false; + } + if (!$this->return && isset($this->__loaded[$file])) { + return true; + } + if (file_exists($file)) { + if (!$this->return) { + require($file); + $this->__loaded[$file] = true; + } + return true; + } + return false; + } +/** + * Maps the $name to the $file. + * + * @param string $file full path to file + * @param string $name unique name for this map + * @param string $type type object being mapped + * @param string $plugin if object is from a plugin, the name of the plugin + * @access private + */ + function __map($file, $name, $type, $plugin) { + if ($plugin) { + $plugin = Inflector::camelize($plugin); + $this->__map['Plugin'][$plugin][$type][$name] = $file; + } else { + $this->__map[$type][$name] = $file; + } + } +/** + * Returns a file's complete path. + * + * @param string $name unique name + * @param string $type type object + * @param string $plugin if object is from a plugin, the name of the plugin + * @return mixed, file path if found, false otherwise + * @access private + */ + function __mapped($name, $type, $plugin) { + if ($plugin) { + $plugin = Inflector::camelize($plugin); + + if (isset($this->__map['Plugin'][$plugin][$type]) && isset($this->__map['Plugin'][$plugin][$type][$name])) { + return $this->__map['Plugin'][$plugin][$type][$name]; + } + return false; + } + + if (isset($this->__map[$type]) && isset($this->__map[$type][$name])) { + return $this->__map[$type][$name]; + } + return false; + } +/** + * Used to overload objects as needed. + * + * @param string $type Model or Helper + * @param string $name Class name to overload + * @access private + */ + function __overload($type, $name) { + if (($type === 'Model' || $type === 'Helper') && strtolower($name) != 'schema') { + Overloadable::overload($name); + } + } +/** + * Loads parent classes based on $type. + * Returns a prefix or suffix needed for loading files. + * + * @param string $type type of object + * @param string $plugin name of plugin + * @param boolean $parent false will not attempt to load parent + * @return array + * @access private + */ + function __settings($type, $plugin, $parent) { + if (!$parent) { + return null; + } + + if ($plugin) { + $plugin = Inflector::underscore($plugin); + $name = Inflector::camelize($plugin); + } + $path = null; + $load = strtolower($type); + + switch ($load) { + case 'model': + if (!class_exists('Model')) { + App::import('Core', 'Model', false, Configure::corePaths('model')); + } + if (!class_exists('AppModel')) { + App::import($type, 'AppModel', false, Configure::read('modelPaths')); + } + if ($plugin) { + if (!class_exists($name . 'AppModel')) { + App::import($type, $plugin . '.' . $name . 'AppModel', false, array(), $plugin . DS . $plugin . '_app_model.php'); + } + $path = $plugin . DS . 'models' . DS; + } + return array('class' => null, 'suffix' => null, 'path' => $path); + break; + case 'behavior': + if ($plugin) { + $path = $plugin . DS . 'models' . DS . 'behaviors' . DS; + } + return array('class' => $type, 'suffix' => null, 'path' => $path); + break; + case 'controller': + App::import($type, 'AppController', false); + if ($plugin) { + App::import($type, $plugin . '.' . $name . 'AppController', false, array(), $plugin . DS . $plugin . '_app_controller.php'); + $path = $plugin . DS . 'controllers' . DS; + } + return array('class' => $type, 'suffix' => $type, 'path' => $path); + break; + case 'component': + if ($plugin) { + $path = $plugin . DS . 'controllers' . DS . 'components' . DS; + } + return array('class' => $type, 'suffix' => null, 'path' => $path); + break; + case 'view': + if ($plugin) { + $path = $plugin . DS . 'views' . DS; + } + return array('class' => $type, 'suffix' => null, 'path' => $path); + break; + case 'helper': + if (!class_exists('AppHelper')) { + App::import($type, 'AppHelper', false); + } + if ($plugin) { + $path = $plugin . DS . 'views' . DS . 'helpers' . DS; + } + return array('class' => $type, 'suffix' => null, 'path' => $path); + break; + case 'vendor': + if ($plugin) { + $path = $plugin . DS . 'vendors' . DS; + } + return array('class' => null, 'suffix' => null, 'path' => $path); + break; + default: + $type = $suffix = $path = null; + break; + } + return array('class' => null, 'suffix' => null, 'path' => null); + } +/** + * Returns default search paths. + * + * @param string $type type of object to be searched + * @return array list of paths + * @access private + */ + function __paths($type) { + $type = strtolower($type); + + if ($type === 'core') { + $path = Configure::corePaths(); + $paths = array(); + + foreach ($path as $key => $value) { + $count = count($key); + for ($i = 0; $i < $count; $i++) { + $paths[] = $path[$key][$i]; + } + } + return $paths; + } + + if ($paths = Configure::read($type . 'Paths')) { + return $paths; + } + + switch ($type) { + case 'plugin': + return array(APP . 'plugins' . DS); + case 'vendor': + return array(APP . 'vendors' . DS, VENDORS, APP . 'plugins' . DS); + case 'controller': + return array(APP . 'controllers' . DS, APP); + case 'model': + return array(APP . 'models' . DS, APP); + case 'view': + return array(APP . 'views' . DS); + } + } +/** + * Removes file location from map if the file has been deleted. + * + * @param string $name name of object + * @param string $type type of object + * @param string $plugin name of plugin + * @return void + * @access private + */ + function __remove($name, $type, $plugin) { + if ($plugin) { + $plugin = Inflector::camelize($plugin); + unset($this->__map['Plugin'][$plugin][$type][$name]); + } else { + unset($this->__map[$type][$name]); + } + } +/** + * Object destructor. + * + * Writes cache file if changes have been made to the $__map or $__paths + * + * @return void + * @access private + */ + function __destruct() { + if ($this->__cache) { + $core = Configure::corePaths('cake'); + unset($this->__paths[rtrim($core[0], DS)]); + Cache::write('dir_map', array_filter($this->__paths), '_cake_core_'); + Cache::write('file_map', array_filter($this->__map), '_cake_core_'); + } + } +} +?> \ No newline at end of file diff --git a/cake/libs/controller/app_controller.php b/cake/libs/controller/app_controller.php new file mode 100755 index 0000000..e791fa9 --- /dev/null +++ b/cake/libs/controller/app_controller.php @@ -0,0 +1,40 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Short description for file. + * + * 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://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.controller + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * 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 + */ +class AppController extends Controller { +} +?> \ No newline at end of file diff --git a/cake/libs/controller/component.php b/cake/libs/controller/component.php new file mode 100755 index 0000000..c40571c --- /dev/null +++ b/cake/libs/controller/component.php @@ -0,0 +1,255 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.controller + * @since CakePHP(tm) v TBD + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Handler for Controller::$components + * + * @package cake + * @subpackage cake.cake.libs.controller + * @link http://book.cakephp.org/view/62/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/65/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/65/MVC-Class-Access-Within-Components + */ + function startup(&$controller) { + foreach ($this->_primary as $name) { + $component =& $this->_loaded[$name]; + if ($component->enabled === true && method_exists($component, 'startup')) { + $component->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 + */ + function beforeRender(&$controller) { + foreach ($this->_primary as $name) { + $component =& $this->_loaded[$name]; + if ($component->enabled === true && method_exists($component,'beforeRender')) { + $component->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 + */ + function shutdown(&$controller) { + foreach ($this->_primary as $name) { + $component =& $this->_loaded[$name]; + if (method_exists($component,'shutdown') && $component->enabled === true) { + $component->shutdown($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); + if ($parent == null) { + $normal = Set::merge(array('Session' => null), $normal); + } + foreach ((array)$normal as $component => $config) { + $plugin = null; + + if (isset($this->__controllerVars['plugin'])) { + $plugin = $this->__controllerVars['plugin'] . '.'; + } + + if (strpos($component, '.') !== false) { + list($plugin, $component) = explode('.', $component); + $plugin = $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); + } + } + } +} + +?> \ No newline at end of file diff --git a/cake/libs/controller/components/acl.php b/cake/libs/controller/components/acl.php new file mode 100755 index 0000000..d68c61f --- /dev/null +++ b/cake/libs/controller/components/acl.php @@ -0,0 +1,588 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Access Control List factory class. + * + * Permissions system. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.controller.components + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Access Control List factory class. + * + * Looks for ACL implementation class in core config, and returns an instance of that class. + * + * @package cake + * @subpackage cake.cake.libs.controller.components + */ +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. + * + */ + function __construct() { + $name = Inflector::camelize(strtolower(Configure::read('Acl.classname'))); + if (!class_exists($name)) { + if (App::import('Component', $name)) { + if (strpos($name, '.') !== false) { + list($plugin, $name) = explode('.', $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. + * + * @param string $aro ARO + * @param string $aco ACO + * @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. + * + * @param string $aro ARO + * @param string $aco ACO + * @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. + * + * @param string $aro ARO + * @param string $aco ACO + * @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. + * + * @param string $aro ARO + * @param string $aco ACO + * @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. + * + * @param string $aro ARO + * @param string $aco ACO + * @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. + * + * @param string $aro ARO + * @param string $aco ACO + * @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 + * @param string $aco ACO + * @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) { + } +} +/** + * In this file you can extend the AclBase. + * + * @package cake + * @subpackage cake.cake.libs.model + */ +class DbAcl extends AclBase { +/** + * Constructor + * + */ + function __construct() { + parent::__construct(); + if (!class_exists('AclNode')) { + uses('model' . DS . 'db_acl'); + } + $this->Aro =& ClassRegistry::init(array('class' => 'Aro', 'alias' => 'Aro')); + $this->Aco =& ClassRegistry::init(array('class' => 'Aco', 'alias' => 'Aco')); + } +/** + * Enter description here... + * + * @param object $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 + * @param string $aco ACO + * @param string $action Action (defaults to *) + * @return boolean Success (true if ARO has access to action in ACO, false otherwise) + * @access public + */ + 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: " . 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: " . 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 + * @param string $aco ACO + * @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 + */ + 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 && count($perms['link']) > 0) { + $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 + * @param string $aco ACO + * @param string $actions Action (defaults to *) + * @return boolean Success + * @access public + */ + 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 + * @param string $aco ACO + * @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 + * @param string $aco ACO + * @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 + * @param string $aco ACO + * @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 + * @param string $aco ACO + * @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; + } +} +/** + * In this file you can extend the AclBase. + * + * @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; + } +} +?> \ No newline at end of file diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php new file mode 100755 index 0000000..a68e5b6 --- /dev/null +++ b/cake/libs/controller/components/auth.php @@ -0,0 +1,881 @@ +<?php +/* SVN FILE: $Id$ */ + +/** + * Authentication component + * + * Manages user logins and permissions. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.controller.components + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ + +App::import(array('Router', 'Security')); + +/** + * Authentication control component class + * + * Binds access control with user authentication and session management. + * + * @package cake + * @subpackage cake.cake.libs.controller.components + */ +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 + */ + 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 + */ + 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 + */ + var $ajaxLogin = null; +/** + * The name of the model that represents users which will be authenticated. Defaults to 'User'. + * + * @var string + * @access public + */ + 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 + */ + 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 + */ + 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 + */ + 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 + */ + var $actionPath = null; +/** + * A URL (defined as a string or array) to the controller action that handles + * logins. + * + * @var mixed + * @access public + */ + 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 + */ + var $loginRedirect = null; +/** + * The 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() + */ + 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 + */ + 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 + */ + var $authError = null; +/** + * Determines whether AuthComponent will automatically redirect and exit if login is successful. + * + * @var boolean + * @access public + */ + var $autoRedirect = true; +/** + * Controller actions for which user validation is not required. + * + * @var array + * @access public + * @see AuthComponent::allow() + */ + 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) { + $this->params = $controller->params; + $crud = array('create', 'read', 'update', 'delete'); + $this->actionMap = array_merge($this->actionMap, array_combine($crud, $crud)); + $this->_methods = $controller->methods; + + $admin = Configure::read('Routing.admin'); + if (!empty($admin)) { + $this->actionMap = array_merge($this->actionMap, array( + $admin . '_index' => 'read', + $admin . '_add' => 'create', + $admin . '_edit' => 'update', + $admin . '_view' => 'read', + $admin . '_remove' => 'delete', + $admin . '_create' => 'create', + $admin . '_read' => 'read', + $admin . '_update' => 'update', + $admin . '_delete' => 'delete' + )); + } + if (Configure::read() > 0) { + App::import('Debugger'); + Debugger::checkSessionKey(); + } + } +/** + * 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) { + $methods = array_flip($controller->methods); + $action = strtolower($controller->params['action']); + $allowedActions = array_map('strtolower', $this->allowedActions); + + $isErrorOrTests = ( + strtolower($controller->name) == 'cakeerror' || + (strtolower($controller->name) == 'tests' && Configure::read() > 0) + ); + if ($isErrorOrTests) { + return true; + } + + $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); + + $isAllowed = ( + $this->allowedActions == array('*') || + in_array($action, $allowedActions) + ); + + if ($loginAction != $url && $isAllowed) { + return true; + } + + if ($loginAction == $url) { + if (empty($controller->data) || !isset($controller->data[$this->userModel])) { + if (!$this->Session->check('Auth.redirect') && env('HTTP_REFERER')) { + $this->Session->write('Auth.redirect', $controller->referer(null, true)); + } + return false; + } + + $isValid = !empty($controller->data[$this->userModel][$this->fields['username']]) && + !empty($controller->data[$this->userModel][$this->fields['password']]); + + if ($isValid) { + $username = $controller->data[$this->userModel][$this->fields['username']]; + $password = $controller->data[$this->userModel][$this->fields['password']]; + + $data = array( + $this->userModel . '.' . $this->fields['username'] => $username, + $this->userModel . '.' . $this->fields['password'] => $password + ); + + if ($this->login($data)) { + if ($this->autoRedirect) { + $controller->redirect($this->redirect(), null, true); + } + return true; + } + } + + $this->Session->setFlash($this->loginError, 'default', array(), 'auth'); + $controller->data[$this->userModel][$this->fields['password']] = null; + return false; + } else { + if (!$this->user()) { + if (!$this->RequestHandler->isAjax()) { + $this->Session->setFlash($this->authError, 'default', 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 { + $err = 'Could not find AclComponent. Please include Acl in '; + $err .= 'Controller::$components.'; + trigger_error(__($err, 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, 'default', 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; + } + $defaults = array( + 'loginAction' => Router::normalize(array( + 'controller'=> Inflector::underscore(Inflector::pluralize($this->userModel)), + 'action' => 'login' + )), + 'sessionKey' => 'Auth.' . $this->userModel, + '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': + $this->mapActions(); + if (!isset($this->actionMap[$this->params['action']])) { + $err = 'Auth::startup() - Attempted access of un-mapped action "%1$s" in'; + $err .= ' controller "%2$s"'; + trigger_error( + sprintf(__($err, 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': + $this->mapActions(); + $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 string $action Controller action name + * @param string $action Controller action name + * @param string ... etc. + * @return void + * @access public + */ + 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, $args); + } + } +/** + * Removes items from the list of allowed actions. + * + * @param string $action Controller action name + * @param string $action Controller action name + * @param string ... etc. + * @return void + * @see AuthComponent::allow() + * @access public + */ + function deny() { + $args = func_get_args(); + foreach ($args as $arg) { + $i = array_search($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 + */ + 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 + */ + 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 + */ + function logout() { + $this->__setDefaults(); + $this->Session->del($this->sessionKey); + $this->Session->del('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 + */ + function user($key = null) { + $this->__setDefaults(); + if (!$this->Session->check($this->sessionKey)) { + return null; + } + + if ($key == null) { + return array($this->userModel => $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 + */ + function action($action = ':controller/:action') { + return str_replace( + array(':controller', ':action'), + array(Inflector::camelize($this->params['controller']), $this->params['action']), + $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; + } + 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[$this->userModel]; + } elseif (is_array($user) && isset($user[$this->userModel])) { + $user = $user[$this->userModel]; + } + + if (is_array($user) && (isset($user[$this->fields['username']]) || isset($user[$this->userModel . '.' . $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( + $this->userModel.'.'.$this->fields['username'] => $user[$this->fields['username']], + $this->userModel.'.'.$this->fields['password'] => $user[$this->fields['password']] + ); + } elseif (isset($user[$this->userModel . '.' . $this->fields['username']]) && !empty($user[$this->userModel . '.' . $this->fields['username']])) { + if (trim($user[$this->userModel . '.' . $this->fields['username']]) == '=' || trim($user[$this->userModel . '.' . $this->fields['password']]) == '=') { + return false; + } + $find = array( + $this->userModel.'.'.$this->fields['username'] => $user[$this->userModel . '.' . $this->fields['username']], + $this->userModel.'.'.$this->fields['password'] => $user[$this->userModel . '.' . $this->fields['password']] + ); + } else { + return false; + } + $model =& $this->getModel(); + $data = $model->find(array_merge($find, $conditions), null, null, 0); + if (empty($data) || empty($data[$this->userModel])) { + return null; + } + } elseif (!empty($user) && is_string($user)) { + $model =& $this->getModel(); + $data = $model->find(array_merge(array($model->escapeField() => $user), $conditions)); + + if (empty($data) || empty($data[$this->userModel])) { + return null; + } + } + + if (!empty($data)) { + if (!empty($data[$this->userModel][$this->fields['password']])) { + unset($data[$this->userModel][$this->fields['password']]); + } + return $data[$this->userModel]; + } + 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 + */ + function hashPasswords($data) { + if (is_object($this->authenticate) && method_exists($this->authenticate, 'hashPasswords')) { + return $this->authenticate->hashPasswords($data); + } + + if (is_array($data) && isset($data[$this->userModel])) { + if (isset($data[$this->userModel][$this->fields['username']]) && isset($data[$this->userModel][$this->fields['password']])) { + $data[$this->userModel][$this->fields['password']] = $this->password($data[$this->userModel][$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 + */ + 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->del('Auth.redirect'); + } + } +} +?> \ No newline at end of file diff --git a/cake/libs/controller/components/cookie.php b/cake/libs/controller/components/cookie.php new file mode 100755 index 0000000..56713f5 --- /dev/null +++ b/cake/libs/controller/components/cookie.php @@ -0,0 +1,485 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Short description for file. + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.controller.components + * @since CakePHP(tm) v 1.2.0.4213 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Load Security class + */ +App::import('Core', 'Security'); +/** + * Cookie Component. + * + * Cookie handling for the controller. + * + * @package cake + * @subpackage cake.cake.libs.controller.components + * + */ +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) && $value !== null) { + $name = $this->__cookieVarNames($key); + + if (count($name) > 1) { + $this->__values[$name[0]][$name[1]] = $value; + $this->__write("[" . $name[0] . "][" . $name[1] . "]", $value); + } else { + $this->__values[$name[0]] = $value; + $this->__write("[" . $name[0] . "]", $value); + } + } else { + foreach ($key as $names => $value) { + $name = $this->__cookieVarNames($names); + + if (count($name) > 1) { + $this->__values[$name[0]][$name[1]] = $value; + $this->__write("[" . $name[0] . "][" . $name[1] . "]", $value); + } else { + $this->__values[$name[0]] = $value; + $this->__write("[" . $name[0] . "]", $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; + } + $name = $this->__cookieVarNames($key); + + if (count($name) > 1) { + if (isset($this->__values[$name[0]])) { + if (isset($this->__values[$name[0]][$name[1]])) { + return $this->__values[$name[0]][$name[1]]; + } + } + return null; + } else { + if (isset($this->__values[$name[0]])) { + $value = $this->__values[$name[0]]; + return $value; + } + return null; + } + } +/** + * 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 del($key) { + if (empty($this->__values)) { + $this->read(); + } + $name = $this->__cookieVarNames($key); + if (count($name) > 1) { + if (isset($this->__values[$name[0]])) { + $this->__delete("[" . $name[0] . "][" . $name[1] . "]"); + unset($this->__values[$name[0]][$name[1]]); + } + } else { + if (isset($this->__values[$name[0]])) { + if (is_array($this->__values[$name[0]])) { + foreach ($this->__values[$name[0]] as $key => $value) { + $this->__delete("[" . $name[0] . "][" . $key . "]"); + } + } + $this->__delete("[" . $name[0] . "]"); + unset($this->__values[$name[0]]); + } + } + } +/** + * 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 (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); + } + +/** + * Creates an array from the $name parameter which allows the dot notation + * similar to one used by Session and Configure classes + * + * @param string $name Name with or without dot notation + * @return array Extracted names + * @access private + */ + function __cookieVarNames($name) { + if (is_string($name)) { + if (strpos($name, ".")) { + $name = explode(".", $name); + } else { + $name = array($name); + } + } + return $name; + } +/** + * 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; + } +} +?> \ No newline at end of file diff --git a/cake/libs/controller/components/email.php b/cake/libs/controller/components/email.php new file mode 100755 index 0000000..b2e2ea4 --- /dev/null +++ b/cake/libs/controller/components/email.php @@ -0,0 +1,797 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Short description for file. + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.controller.components + * @since CakePHP(tm) v 1.2.0.3467 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * 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 + * + */ +App::import('Core', 'Multibyte'); +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 + * + * @var array + * @access public + */ + var $smtpOptions = array( + 'port'=> 25, 'host' => 'localhost', 'timeout' => 30 + ); +/** + * Placeholder for any errors that might happen with the + * smtp mail methods + * + * @var string + * @access public + */ + var $smtpError = null; +/** + * If set to true, the mail method will be auto-set to 'debug' + * + * @var string + * @access protected + */ + var $_debug = false; +/** + * 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 + * @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"; + } + + $message = $this->__wrap($content); + if ($this->template === null) { + $message = $this->__formatMessage($message); + } else { + $message = $this->__renderTemplate($message); + } + $message[] = ''; + $this->__message = $message; + + if (!empty($this->attachments)) { + $this->__attachFiles(); + } + + if (!is_null($this->__boundary)) { + $this->__message[] = ''; + $this->__message[] = '--' . $this->__boundary . '--'; + $this->__message[] = ''; + } + + if ($this->_debug) { + return $this->__debug(); + } + $__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 + */ + function reset() { + $this->template = null; + $this->to = null; + $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->__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 __renderTemplate($content) { + $viewClass = $this->Controller->view; + + if ($viewClass != 'View') { + if (strpos($viewClass, '.') !== false) { + list($plugin, $viewClass) = explode('.', $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", 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", 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", str_replace(array("\r\n", "\r"), "\n", $View->renderLayout($content))); + $msg = array_merge($msg, $content); + + return $msg; + } +/** + * Create unique boundary identifier + * + * @access private + */ + function __createBoundary() { + $this->__boundary = md5(uniqid(time())); + } +/** + * Create emails headers including (but not limited to) from email address, reply to, + * bcc and cc. + * + * @access private + */ + function __createHeader() { + if ($this->delivery == 'smtp') { + $this->__header[] = 'To: ' . $this->__formatAddress($this->to); + } + $this->__header[] = 'From: ' . $this->__formatAddress($this->from); + + if (!empty($this->replyTo)) { + $this->__header[] = 'Reply-To: ' . $this->__formatAddress($this->replyTo); + } + if (!empty($this->return)) { + $this->__header[] = 'Return-Path: ' . $this->__formatAddress($this->return); + } + if (!empty($this->readReceipt)) { + $this->__header[] = 'Disposition-Notification-To: ' . $this->__formatAddress($this->readReceipt); + } + + if (!empty($this->cc)) { + $this->__header[] = 'cc: ' .implode(', ', array_map(array($this, '__formatAddress'), $this->cc)); + } + + if (!empty($this->bcc) && $this->delivery != 'smtp') { + $this->__header[] = 'Bcc: ' .implode(', ', array_map(array($this, '__formatAddress'), $this->bcc)); + } + if ($this->delivery == 'smtp') { + $this->__header[] = 'Subject: ' . $this->__encode($this->subject); + } + $this->__header[] = 'X-Mailer: ' . $this->xMailer; + + if (!empty($this->headers)) { + foreach ($this->headers as $key => $val) { + $this->__header[] = 'X-' . $key . ': ' . $val; + } + } + + if (!empty($this->attachments)) { + $this->__createBoundary(); + $this->__header[] = 'MIME-Version: 1.0'; + $this->__header[] = 'Content-Type: multipart/mixed; boundary="' . $this->__boundary . '"'; + $this->__header[] = 'This part of the E-mail should never be seen. If'; + $this->__header[] = 'you are reading this, consider upgrading your e-mail'; + $this->__header[] = 'client to a MIME-compatible client.'; + } elseif ($this->sendAs === 'text') { + $this->__header[] = 'Content-Type: text/plain; charset=' . $this->charset; + } elseif ($this->sendAs === 'html') { + $this->__header[] = 'Content-Type: text/html; charset=' . $this->charset; + } elseif ($this->sendAs === 'both') { + $this->__header[] = 'Content-Type: multipart/alternative; boundary="alt-' . $this->__boundary . '"'; + } + + $this->__header[] = 'Content-Transfer-Encoding: 7bit'; + } +/** + * 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 $attachment) { + $file = $this->__findFiles($attachment); + if (!empty($file)) { + $files[] = $file; + } + } + + foreach ($files as $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($file) . '"'; + $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 + * @return array Wrapped message + * @access private + */ + function __wrap($message) { + $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', E_USER_WARNING); + $this->lineLength = $this->_lineLength; + } + + foreach ($lines as $line) { + if (substr($line, 0, 1) == '.') { + $line = '.' . $line; + } + $formatted = array_merge($formatted, explode("\n", wordwrap($line, $this->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 = ''; + } + return mb_encode_mimeheader($subject, $this->charset, 'B', $nl); + } +/** + * 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) { + if (strpos($string, '<') !== false) { + $value = explode('<', $string); + if ($smtp) { + $string = '<' . $value[1]; + } else { + $string = $this->__encode($value[0]) . ' <' . $value[1]; + } + } + return $this->__strip($string); + } +/** + * Remove certain elements (such as bcc:, to:, %0a) from given value + * + * @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("\n", $this->__header); + $message = implode("\n", $this->__message); + if (ini_get('safe_mode')) { + return @mail($this->to, $this->__encode($this->subject), $message, $header); + } + return @mail($this->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('Socket')); + + $this->__smtpConnection =& new CakeSocket(array_merge(array('protocol'=>'smtp'), $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("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 (!$this->__smtpSend('RCPT TO: ' . $this->__formatAddress($this->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; + } +/** + * Private 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 private + */ + function __smtpSend($data, $checkCode = '250') { + if (!is_null($data)) { + $this->__smtpConnection->write($data . "\r\n"); + } + if ($checkCode !== false) { + $response = $this->__smtpConnection->read(); + + if (preg_match('/^(' . $checkCode . ')/', $response, $code)) { + return $code[0]; + } + $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 ($this->delivery == 'smtp') { + $fm .= sprintf('%s %s%s', 'Host:', $this->smtpOptions['host'], $nl); + $fm .= sprintf('%s %s%s', 'Port:', $this->smtpOptions['port'], $nl); + $fm .= sprintf('%s %s%s', 'Timeout:', $this->smtpOptions['timeout'], $nl); + } + $fm .= sprintf('%s %s%s', 'To:', $this->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>'; + + $this->Controller->Session->setFlash($fm, 'default', null, 'email'); + return true; + } + +} +?> \ No newline at end of file diff --git a/cake/libs/controller/components/request_handler.php b/cake/libs/controller/components/request_handler.php new file mode 100755 index 0000000..efca6b2 --- /dev/null +++ b/cake/libs/controller/components/request_handler.php @@ -0,0 +1,741 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * 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://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.controller.components + * @since CakePHP(tm) v 0.10.4.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ + +if (!defined('REQUEST_MOBILE_UA')) { + define('REQUEST_MOBILE_UA', '(iPhone|MIDP|AvantGo|BlackBerry|J2ME|Opera Mini|DoCoMo|NetFront|Nokia|PalmOS|PalmSource|portalmmm|Plucker|ReqwirelessWeb|SonyEricsson|Symbian|UP\.Browser|Windows CE|Xiino)'); +} + +/** + * Request object for handling HTTP requests + * + * @package cake + * @subpackage cake.cake.libs.controller.components + * + */ +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' + ); +/** + * 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 + * @return void + * @see Router::parseExtensions() + * @access public + */ + function initialize(&$controller) { + if (isset($controller->params['url']['ext'])) { + $this->ext = $controller->params['url']['ext']; + } + } +/** + * 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'); + } + + if ($this->requestedWith('xml')) { + if (!class_exists('XmlNode')) { + App::import('Core', 'Xml'); + } + $xml = new Xml(trim(file_get_contents('php://input'))); + + if (is_object($xml->child('data')) && count($xml->children) == 1) { + $controller->data = $xml->child('data'); + } else { + $controller->data = $xml; + } + } + } +/** + * 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 + * @access public + */ + function beforeRedirect(&$controller, $url) { + if (!$this->isAjax()) { + return; + } + foreach ($_POST as $key => $val) { + unset($_POST[$key]); + } + echo $this->requestAction($url, array('return')); + $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 + */ + function isMobile() { + preg_match('/' . REQUEST_MOBILE_UA . '/i', env('HTTP_USER_AGENT'), $match); + if (!empty($match) || $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 getReferrer() { + 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 .= '/' . $type; + } else { + $remove = preg_replace("/(?:\/{$this->__renderType})$/", '/' . $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 ($this->__responseTypeSet != null) { + return false; + } + 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'])) { + header("Content-Disposition: attachment; filename=\"{$options['attachment']}\""); + } + if (Configure::read() < 2 && !defined('CAKEPHP_SHELL')) { + @header($header); + } + $this->__responseTypeSet = $cType; + return true; + } + return false; + } +/** + * 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; + } +} + +?> \ No newline at end of file diff --git a/cake/libs/controller/components/security.php b/cake/libs/controller/components/security.php new file mode 100755 index 0000000..96d1402 --- /dev/null +++ b/cake/libs/controller/components/security.php @@ -0,0 +1,692 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Short description for file. + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.controller.components + * @since CakePHP(tm) v 0.10.8.2156 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Short description for file. + * + * Long description for file + * + * @package cake + * @subpackage cake.cake.libs.controller.components + */ +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. + * If using digest authentication, passwords should be MD5-hashed. + * + * @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; +/** + * Component startup. All security checking happens here. + * + * @param object $controller Instantiating controller + * @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 + */ + 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 + */ + 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 + */ + 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 + */ + 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 + */ + 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 + */ + 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 . ' ' . join(',', $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 + */ + 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() + */ + 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 + */ + function blackHole(&$controller, $error = '') { + $this->Session->del('_Token'); + + 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()) { + $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'])) { + 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']); + + $lockedFields = array(); + $fields = Set::flatten($check); + $fieldList = array_keys($fields); + $locked = unserialize(str_rot13($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) { + 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 { + // Debug::warning('Callback method ' . $method . ' in controller ' . get_class($controller) + return null; + } + } +} + +?> \ No newline at end of file diff --git a/cake/libs/controller/components/session.php b/cake/libs/controller/components/session.php new file mode 100755 index 0000000..f5164d4 --- /dev/null +++ b/cake/libs/controller/components/session.php @@ -0,0 +1,315 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Short description for file. + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.controller.components + * @since CakePHP(tm) v 0.10.0.1232 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +if (!class_exists('cakesession')) { + require LIBS . 'session.php'; +} +/** + * Session Component. + * + * Session handling from the controller. + * + * @package cake + * @subpackage cake.cake.libs.controller.components + * + */ +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 Session has been started + * + * @var boolean + * @access private + */ + var $__started = false; +/** + * 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; + } + } +/** + * Initializes the component, gets a reference to Controller::$param['bare']. + * + * @param object $controller A reference to the controller + * @return void + * @access public + */ + function initialize(&$controller) { + if (isset($controller->params['bare'])) { + $this->__bare = $controller->params['bare']; + } + } +/** + * 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 + */ + 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 + */ + function read($name = null) { + if ($this->__active === true) { + $this->__start(); + return parent::read($name); + } + return false; + } +/** + * Used to delete a session variable. + * + * In your controller: $this->Session->del('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 + */ + function del($name) { + if ($this->__active === true) { + $this->__start(); + return parent::del($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 + */ + function delete($name) { + if ($this->__active === true) { + $this->__start(); + return $this->del($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 + */ + 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 + */ + 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 $layout Layout 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 + */ + function setFlash($message, $layout = 'default', $params = array(), $key = 'flash') { + if ($this->__active === true) { + $this->__start(); + $this->write('Message.' . $key, compact('message', 'layout', '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 + */ + 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()) { + $this->__started = true; + parent::_checkValid(); + } else { + $this->__started = parent::start(); + } + } + return $this->__started; + } +} + +?> \ No newline at end of file diff --git a/cake/libs/controller/controller.php b/cake/libs/controller/controller.php new file mode 100755 index 0000000..6a582bf --- /dev/null +++ b/cake/libs/controller/controller.php @@ -0,0 +1,1178 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Base controller class. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.controller + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Include files + */ +App::import('Core', array('Component', 'View')); +/** + * Controller + * + * Application controller class for organization of business logic. + * Provides basic functionality, such as rendering views inside layouts, + * automatic model availability, redirection, callbacks, and more. + * + * @package cake + * @subpackage cake.cake.libs.controller + * @link http://book.cakephp.org/view/49/Controllers + * + */ +class Controller extends Object { +/** + * The name of this controller. Controller names are plural, named after the model they manipulate. + * + * @var string + * @access public + * @link http://book.cakephp.org/view/52/name + */ + var $name = null; +/** + * Stores the current URL, relative to the webroot of the application. + * + * @var string + * @access public + */ + var $here = null; +/** + * The webroot of the application. Helpful if your application is placed in a folder under the current domain name. + * + * @var string + * @access public + */ + var $webroot = null; +/** + * The name of the currently requested controller action. + * + * @var string + * @access public + */ + var $action = null; +/** + * An array containing the class names of models this controller uses. + * + * Example: var $uses = array('Product', 'Post', 'Comment'); + * + * @var mixed A single name as a string or a list of names as an array. + * @access protected + * @link http://book.cakephp.org/view/53/components-helpers-and-uses + */ + var $uses = false; +/** + * An array containing the names of helpers this controller uses. The array elements should + * not contain the "Helper" part of the classname. + * + * Example: var $helpers = array('Html', 'Javascript', 'Time', 'Ajax'); + * + * @var mixed A single name as a string or a list of names as an array. + * @access protected + * @link http://book.cakephp.org/view/53/components-helpers-and-uses + */ + var $helpers = array('Html', 'Form'); +/** + * Parameters received in the current request: GET and POST data, information + * about the request, etc. + * + * @var array + * @access public + * @link http://book.cakephp.org/view/55/The-Parameters-Attribute-params + */ + var $params = array(); +/** + * Data POSTed to the controller using the HtmlHelper. Data here is accessible + * using the $this->data['ModelName']['fieldName'] pattern. + * + * @var array + * @access public + */ + var $data = array(); +/** + * Holds pagination defaults for controller actions. The keys that can be included + * in this array are: 'conditions', 'fields', 'order', 'limit', 'page', and 'recursive', + * similar to the keys in the second parameter of Model::find(). + * + * Pagination defaults can also be supplied in a model-by-model basis by using + * the name of the model as a key for a pagination array: + * + * var $paginate = array( + * 'Post' => array(...), + * 'Comment' => array(...) + * ); + * + * @var array + * @access public + * @link http://book.cakephp.org/view/164/Pagination + */ + var $paginate = array('limit' => 20, 'page' => 1); +/** + * The name of the views subfolder containing views for this controller. + * + * @var string + * @access public + */ + var $viewPath = null; +/** + * The name of the layouts subfolder containing layouts for this controller. + * + * @var string + * @access public + */ + var $layoutPath = null; +/** + * Contains variables to be handed to the view. + * + * @var array + * @access public + */ + var $viewVars = array(); +/** + * Text to be used for the $title_for_layout layout variable (usually + * placed inside <title> tags.) + * + * @var boolean + * @access public + * @link http://book.cakephp.org/view/54/Page-related-Attributes-layout-and-pageTitle + */ + var $pageTitle = false; +/** + * An array containing the class names of the models this controller uses. + * + * @var array Array of model objects. + * @access public + */ + var $modelNames = array(); +/** + * Base URL path. + * + * @var string + * @access public + */ + var $base = null; +/** + * The name of the layout file to render the view inside of. The name specified + * is the filename of the layout in /app/views/layouts without the .ctp + * extension. + * + * @var string + * @access public + * @link http://book.cakephp.org/view/54/Page-related-Attributes-layout-and-pageTitle + */ + var $layout = 'default'; +/** + * Set to true to automatically render the view + * after action logic. + * + * @var boolean + * @access public + */ + var $autoRender = true; +/** + * Set to true to automatically render the layout around views. + * + * @var boolean + * @access public + */ + var $autoLayout = true; +/** + * Instance of Component used to handle callbacks. + * + * @var string + * @access public + */ + var $Component = null; +/** + * Array containing the names of components this controller uses. Component names + * should not contain the "Component" portion of the classname. + * + * Example: var $components = array('Session', 'RequestHandler', 'Acl'); + * + * @var array + * @access public + * @link http://book.cakephp.org/view/53/components-helpers-and-uses + */ + var $components = array(); +/** + * The name of the View class this controller sends output to. + * + * @var string + * @access public + */ + var $view = 'View'; +/** + * File extension for view templates. Defaults to Cake's conventional ".ctp". + * + * @var string + * @access public + */ + var $ext = '.ctp'; +/** + * The output of the requested action. Contains either a variable + * returned from the action, or the data of the rendered view; + * You can use this var in child controllers' afterFilter() callbacks to alter output. + * + * @var string + * @access public + */ + var $output = null; +/** + * Automatically set to the name of a plugin. + * + * @var string + * @access public + */ + var $plugin = null; +/** + * Used to define methods a controller that will be cached. To cache a + * single action, the value is set to an array containing keys that match + * action names and values that denote cache expiration times (in seconds). + * + * Example: var $cacheAction = array( + * 'view/23/' => 21600, + * 'recalled/' => 86400 + * ); + * + * $cacheAction can also be set to a strtotime() compatible string. This + * marks all the actions in the controller for view caching. + * + * @var mixed + * @access public + * @link http://book.cakephp.org/view/346/Caching-in-the-Controller + */ + var $cacheAction = false; +/** + * Used to create cached instances of models a controller uses. + * When set to true, all models related to the controller will be cached. + * This can increase performance in many cases. + * + * @var boolean + * @access public + */ + var $persistModel = false; +/** + * Holds all params passed and named. + * + * @var mixed + * @access public + */ + var $passedArgs = array(); +/** + * Triggers Scaffolding + * + * @var mixed + * @access public + * @link http://book.cakephp.org/view/105/Scaffolding + */ + var $scaffold = false; +/** + * Holds current methods of the controller + * + * @var array + * @access public + * @link + */ + var $methods = array(); +/** + * This controller's primary model class name, the Inflector::classify()'ed version of + * the controller's $name property. + * + * Example: For a controller named 'Comments', the modelClass would be 'Comment' + * + * @var string + * @access public + */ + var $modelClass = null; +/** + * This controller's model key name, an underscored version of the controller's $modelClass property. + * + * Example: For a controller named 'ArticleComments', the modelKey would be 'article_comment' + * + * @var string + * @access public + */ + var $modelKey = null; +/** + * Holds any validation errors produced by the last call of the validateErrors() method/ + * + * @var array Validation errors, or false if none + * @access public + */ + var $validationErrors = null; +/** + * Constructor. + * + */ + function __construct() { + if ($this->name === null) { + $r = null; + if (!preg_match('/(.*)Controller/i', get_class($this), $r)) { + die (__("Controller::__construct() : Can not get or parse my own class name, exiting.")); + } + $this->name = $r[1]; + } + + if ($this->viewPath == null) { + $this->viewPath = Inflector::underscore($this->name); + } + $this->modelClass = Inflector::classify($this->name); + $this->modelKey = Inflector::underscore($this->modelClass); + $this->Component =& new Component(); + + $childMethods = get_class_methods($this); + $parentMethods = get_class_methods('Controller'); + + foreach ($childMethods as $key => $value) { + $childMethods[$key] = strtolower($value); + } + + foreach ($parentMethods as $key => $value) { + $parentMethods[$key] = strtolower($value); + } + $this->methods = array_diff($childMethods, $parentMethods); + parent::__construct(); + } +/** + * Merge components, helpers, and uses vars from AppController and PluginAppController. + * + * @return void + * @access protected + */ + function __mergeVars() { + $pluginName = Inflector::camelize($this->plugin); + $pluginController = $pluginName . 'AppController'; + + if (is_subclass_of($this, 'AppController') || is_subclass_of($this, $pluginController)) { + $appVars = get_class_vars('AppController'); + $uses = $appVars['uses']; + $merge = array('components', 'helpers'); + $plugin = null; + + if (!empty($this->plugin)) { + $plugin = $pluginName . '.'; + if (!is_subclass_of($this, $pluginController)) { + $pluginController = null; + } + } else { + $pluginController = null; + } + + if ($uses == $this->uses && !empty($this->uses)) { + if (!in_array($plugin . $this->modelClass, $this->uses)) { + array_unshift($this->uses, $plugin . $this->modelClass); + } elseif ($this->uses[0] !== $plugin . $this->modelClass) { + $this->uses = array_flip($this->uses); + unset($this->uses[$plugin . $this->modelClass]); + $this->uses = array_flip($this->uses); + array_unshift($this->uses, $plugin . $this->modelClass); + } + } elseif ($this->uses !== null || $this->uses !== false) { + $merge[] = 'uses'; + } + + foreach ($merge as $var) { + if (!empty($appVars[$var]) && is_array($this->{$var})) { + if ($var === 'components') { + $normal = Set::normalize($this->{$var}); + $app = Set::normalize($appVars[$var]); + if ($app !== $normal) { + $this->{$var} = Set::merge($app, $normal); + } + } else { + $this->{$var} = Set::merge($this->{$var}, array_diff($appVars[$var], $this->{$var})); + } + } + } + } + + if ($pluginController && $pluginName != null) { + $appVars = get_class_vars($pluginController); + $uses = $appVars['uses']; + $merge = array('components', 'helpers'); + + if ($this->uses !== null || $this->uses !== false) { + $merge[] = 'uses'; + } + + foreach ($merge as $var) { + if (isset($appVars[$var]) && !empty($appVars[$var]) && is_array($this->{$var})) { + if ($var === 'components') { + $normal = Set::normalize($this->{$var}); + $app = Set::normalize($appVars[$var]); + if ($app !== $normal) { + $this->{$var} = Set::merge($app, $normal); + } + } else { + $this->{$var} = Set::merge($this->{$var}, array_diff($appVars[$var], $this->{$var})); + } + } + } + } + } +/** + * Loads Model classes based on the the uses property + * see Controller::loadModel(); for more info. + * Loads Components and prepares them for initialization. + * + * @return mixed true if models found and instance created, or cakeError if models not found. + * @access public + * @see Controller::loadModel() + * @link http://book.cakephp.org/view/429/constructClasses + */ + function constructClasses() { + $this->__mergeVars(); + $this->Component->init($this); + + if ($this->uses !== null || ($this->uses !== array())) { + if (empty($this->passedArgs) || !isset($this->passedArgs['0'])) { + $id = false; + } else { + $id = $this->passedArgs['0']; + } + + if ($this->uses === false) { + $this->loadModel($this->modelClass, $id); + } elseif ($this->uses) { + $uses = is_array($this->uses) ? $this->uses : array($this->uses); + $modelClassName = $uses[0]; + if (strpos($uses[0], '.') !== false) { + list($plugin, $modelClassName) = explode('.', $uses[0]); + } + $this->modelClass = $modelClassName; + foreach ($uses as $modelClass) { + $this->loadModel($modelClass); + } + } + } + return true; + } +/** + * Loads and instantiates models required by this controller. + * If Controller::persistModel; is true, controller will create cached model instances on first request, + * additional request will used cached models. + * If the model is non existent, it will throw a missing database table error, as Cake generates + * dynamic models for the time being. + * + * @param string $modelClass Name of model class to load + * @param mixed $id Initial ID the instanced model class should have + * @return mixed true when single model found and instance created error returned if models not found. + * @access public + */ + function loadModel($modelClass = null, $id = null) { + if ($modelClass === null) { + $modelClass = $this->modelClass; + } + $cached = false; + $object = null; + $plugin = null; + if ($this->uses === false) { + if ($this->plugin) { + $plugin = $this->plugin . '.'; + } + } + + if (strpos($modelClass, '.') !== false) { + list($plugin, $modelClass) = explode('.', $modelClass); + $plugin = $plugin . '.'; + } + + if ($this->persistModel === true) { + $cached = $this->_persist($modelClass, null, $object); + } + + if (($cached === false)) { + $this->modelNames[] = $modelClass; + + if (!PHP5) { + $this->{$modelClass} =& ClassRegistry::init(array('class' => $plugin . $modelClass, 'alias' => $modelClass, 'id' => $id)); + } else { + $this->{$modelClass} = ClassRegistry::init(array('class' => $plugin . $modelClass, 'alias' => $modelClass, 'id' => $id)); + } + + if (!$this->{$modelClass}) { + return $this->cakeError('missingModel', array(array('className' => $modelClass, 'webroot' => '', 'base' => $this->base))); + } + + if ($this->persistModel === true) { + $this->_persist($modelClass, true, $this->{$modelClass}); + $registry = ClassRegistry::getInstance(); + $this->_persist($modelClass . 'registry', true, $registry->__objects, 'registry'); + } + } else { + $this->_persist($modelClass . 'registry', true, $object, 'registry'); + $this->_persist($modelClass, true, $object); + $this->modelNames[] = $modelClass; + } + } +/** + * Redirects to given $url, after turning off $this->autoRender. + * Script execution is halted after the redirect. + * + * @param mixed $url A string or array-based URL pointing to another location within the app, or an absolute URL + * @param integer $status Optional HTTP status code (eg: 404) + * @param boolean $exit If true, exit() will be called after the redirect + * @return mixed void if $exit = false. Terminates script if $exit = true + * @access public + * @link http://book.cakephp.org/view/425/redirect + */ + function redirect($url, $status = null, $exit = true) { + $this->autoRender = false; + + if (is_array($status)) { + extract($status, EXTR_OVERWRITE); + } + $response = $this->Component->beforeRedirect($this, $url, $status, $exit); + + if ($response === false) { + return; + } + if (is_array($response)) { + foreach ($response as $resp) { + if (is_array($resp) && isset($resp['url'])) { + extract($resp, EXTR_OVERWRITE); + } elseif ($resp !== null) { + $url = $resp; + } + } + } + + if (function_exists('session_write_close')) { + session_write_close(); + } + + if (!empty($status)) { + $codes = array( + 100 => 'Continue', + 101 => 'Switching Protocols', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Time-out', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Large', + 415 => 'Unsupported Media Type', + 416 => 'Requested range not satisfiable', + 417 => 'Expectation Failed', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Time-out' + ); + if (is_string($status)) { + $codes = array_combine(array_values($codes), array_keys($codes)); + } + + if (isset($codes[$status])) { + $code = $msg = $codes[$status]; + if (is_numeric($status)) { + $code = $status; + } + if (is_string($status)) { + $msg = $status; + } + $status = "HTTP/1.1 {$code} {$msg}"; + } else { + $status = null; + } + } + + if (!empty($status)) { + $this->header($status); + } + if ($url !== null) { + $this->header('Location: ' . Router::url($url, true)); + } + + if (!empty($status) && ($status >= 300 && $status < 400)) { + $this->header($status); + } + + if ($exit) { + $this->_stop(); + } + } +/** + * Convenience method for header() + * + * @param string $status + * @return void + * @access public + */ + function header($status) { + header($status); + } +/** + * Saves a variable for use inside a view template. + * + * @param mixed $one A string or an array of data. + * @param mixed $two Value in case $one is a string (which then works as the key). + * Unused if $one is an associative array, otherwise serves as the values to $one's keys. + * @return void + * @access public + * @link http://book.cakephp.org/view/427/set + */ + function set($one, $two = null) { + $data = array(); + + if (is_array($one)) { + if (is_array($two)) { + $data = array_combine($one, $two); + } else { + $data = $one; + } + } else { + $data = array($one => $two); + } + + foreach ($data as $name => $value) { + if ($name === 'title') { + $this->pageTitle = $value; + } else { + if ($two === null && is_array($one)) { + $this->viewVars[Inflector::variable($name)] = $value; + } else { + $this->viewVars[$name] = $value; + } + } + } + } +/** + * Internally redirects one action to another. Examples: + * + * setAction('another_action'); + * setAction('action_with_parameters', $parameter1); + * + * @param string $action The new action to be redirected to + * @param mixed Any other parameters passed to this method will be passed as + * parameters to the new action. + * @return mixed Returns the return value of the called action + * @access public + */ + function setAction($action) { + $this->action = $action; + $args = func_get_args(); + unset($args[0]); + return call_user_func_array(array(&$this, $action), $args); + } +/** + * Controller callback to tie into Auth component. Only called when AuthComponent::authorize is set to 'controller'. + * + * @return bool true if authorized, false otherwise + * @access public + * @link http://book.cakephp.org/view/396/authorize + */ + function isAuthorized() { + trigger_error(sprintf(__('%s::isAuthorized() is not defined.', true), $this->name), E_USER_WARNING); + return false; + } +/** + * Returns number of errors in a submitted FORM. + * + * @return integer Number of errors + * @access public + */ + function validate() { + $args = func_get_args(); + $errors = call_user_func_array(array(&$this, 'validateErrors'), $args); + + if ($errors === false) { + return 0; + } + return count($errors); + } +/** + * Validates models passed by parameters. Example: + * + * $errors = $this->validateErrors($this->Article, $this->User); + * + * @param mixed A list of models as a variable argument + * @return array Validation errors, or false if none + * @access public + */ + function validateErrors() { + $objects = func_get_args(); + + if (!count($objects)) { + return false; + } + + $errors = array(); + foreach ($objects as $object) { + $this->{$object->alias}->set($object->data); + $errors = array_merge($errors, $this->{$object->alias}->invalidFields()); + } + + return $this->validationErrors = (count($errors) ? $errors : false); + } +/** + * Instantiates the correct view class, hands it its data, and uses it to render the view output. + * + * @param string $action Action name to render + * @param string $layout Layout to use + * @param string $file File to use for rendering + * @return string Full output string of view contents + * @access public + * @link http://book.cakephp.org/view/428/render + */ + function render($action = null, $layout = null, $file = null) { + $this->beforeRender(); + + $viewClass = $this->view; + if ($this->view != 'View') { + if (strpos($viewClass, '.') !== false) { + list($plugin, $viewClass) = explode('.', $viewClass); + } + $viewClass = $viewClass . 'View'; + App::import('View', $this->view); + } + + $this->Component->beforeRender($this); + + $this->params['models'] = $this->modelNames; + + if (Configure::read() > 2) { + $this->set('cakeDebug', $this); + } + + $View =& new $viewClass($this); + + if (!empty($this->modelNames)) { + $models = array(); + foreach ($this->modelNames as $currentModel) { + if (isset($this->$currentModel) && is_a($this->$currentModel, 'Model')) { + $models[] = Inflector::underscore($currentModel); + } + if (isset($this->$currentModel) && is_a($this->$currentModel, 'Model') && !empty($this->$currentModel->validationErrors)) { + $View->validationErrors[Inflector::camelize($currentModel)] =& $this->$currentModel->validationErrors; + } + } + $models = array_diff(ClassRegistry::keys(), $models); + foreach ($models as $currentModel) { + if (ClassRegistry::isKeySet($currentModel)) { + $currentObject =& ClassRegistry::getObject($currentModel); + if (is_a($currentObject, 'Model') && !empty($currentObject->validationErrors)) { + $View->validationErrors[Inflector::camelize($currentModel)] =& $currentObject->validationErrors; + } + } + } + } + + $this->autoRender = false; + $this->output .= $View->render($action, $layout, $file); + + return $this->output; + } +/** + * Returns the referring URL for this request. + * + * @param string $default Default URL to use if HTTP_REFERER cannot be read from headers + * @param boolean $local If true, restrict referring URLs to local server + * @return string Referring URL + * @access public + * @link http://book.cakephp.org/view/430/referer + */ + function referer($default = null, $local = false) { + $ref = env('HTTP_REFERER'); + if (!empty($ref) && defined('FULL_BASE_URL')) { + $base = FULL_BASE_URL . $this->webroot; + if (strpos($ref, $base) === 0) { + $return = substr($ref, strlen($base)); + if ($return[0] != '/') { + $return = '/'.$return; + } + return $return; + } elseif (!$local) { + return $ref; + } + } + + if ($default != null) { + return $default; + } + return '/'; + } +/** + * Forces the user's browser not to cache the results of the current request. + * + * @return void + * @access public + * @link http://book.cakephp.org/view/431/disableCache + */ + function disableCache() { + header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); + header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); + header("Cache-Control: no-store, no-cache, must-revalidate"); + header("Cache-Control: post-check=0, pre-check=0", false); + header("Pragma: no-cache"); + } +/** + * Shows a message to the user for $pause seconds, then redirects to $url. + * Uses flash.ctp as the default layout for the message. + * Does not work if the current debug level is higher than 0. + * + * @param string $message Message to display to the user + * @param mixed $url Relative string or array-based URL to redirect to after the time expires + * @param integer $pause Time to show the message + * @return void Renders flash layout + * @access public + * @link http://book.cakephp.org/view/426/flash + */ + function flash($message, $url, $pause = 1) { + $this->autoRender = false; + $this->set('url', Router::url($url)); + $this->set('message', $message); + $this->set('pause', $pause); + $this->set('page_title', $message); + $this->render(false, 'flash'); + } +/** + * Converts POST'ed form data to a model conditions array, suitable for use in a Model::find() call. + * + * @param array $data POST'ed data organized by model and field + * @param mixed $op A string containing an SQL comparison operator, or an array matching operators to fields + * @param string $bool SQL boolean operator: AND, OR, XOR, etc. + * @param boolean $exclusive If true, and $op is an array, fields not included in $op will not be included in the returned conditions + * @return array An array of model conditions + * @access public + * @link http://book.cakephp.org/view/432/postConditions + */ + function postConditions($data = array(), $op = null, $bool = 'AND', $exclusive = false) { + if (!is_array($data) || empty($data)) { + if (!empty($this->data)) { + $data = $this->data; + } else { + return null; + } + } + $cond = array(); + + if ($op === null) { + $op = ''; + } + + foreach ($data as $model => $fields) { + foreach ($fields as $field => $value) { + $key = $model.'.'.$field; + $fieldOp = $op; + if (is_array($op) && array_key_exists($key, $op)) { + $fieldOp = $op[$key]; + } elseif (is_array($op) && array_key_exists($field, $op)) { + $fieldOp = $op[$field]; + } elseif (is_array($op)) { + $fieldOp = false; + } + if ($exclusive && $fieldOp === false) { + continue; + } + $fieldOp = strtoupper(trim($fieldOp)); + if ($fieldOp === 'LIKE') { + $key = $key.' LIKE'; + $value = '%'.$value.'%'; + } elseif ($fieldOp && $fieldOp != '=') { + $key = $key.' '.$fieldOp; + } + $cond[$key] = $value; + } + } + if ($bool != null && strtoupper($bool) != 'AND') { + $cond = array($bool => $cond); + } + return $cond; + } +/** + * Handles automatic pagination of model records. + * + * @param mixed $object Model to paginate (e.g: model instance, or 'Model', or 'Model.InnerModel') + * @param mixed $scope Conditions to use while paginating + * @param array $whitelist List of allowed options for paging + * @return array Model query results + * @access public + * @link http://book.cakephp.org/view/165/Controller-Setup + */ + function paginate($object = null, $scope = array(), $whitelist = array()) { + if (is_array($object)) { + $whitelist = $scope; + $scope = $object; + $object = null; + } + $assoc = null; + + if (is_string($object)) { + $assoc = null; + + if (strpos($object, '.') !== false) { + list($object, $assoc) = explode('.', $object); + } + + if ($assoc && isset($this->{$object}->{$assoc})) { + $object =& $this->{$object}->{$assoc}; + } elseif ($assoc && isset($this->{$this->modelClass}) && isset($this->{$this->modelClass}->{$assoc})) { + $object =& $this->{$this->modelClass}->{$assoc}; + } elseif (isset($this->{$object})) { + $object =& $this->{$object}; + } elseif (isset($this->{$this->modelClass}) && isset($this->{$this->modelClass}->{$object})) { + $object =& $this->{$this->modelClass}->{$object}; + } + } elseif (empty($object) || $object === null) { + if (isset($this->{$this->modelClass})) { + $object =& $this->{$this->modelClass}; + } else { + $className = null; + $name = $this->uses[0]; + if (strpos($this->uses[0], '.') !== false) { + list($name, $className) = explode('.', $this->uses[0]); + } + if ($className) { + $object =& $this->{$className}; + } else { + $object =& $this->{$name}; + } + } + } + + if (!is_object($object)) { + trigger_error(sprintf(__('Controller::paginate() - can\'t find model %1$s in controller %2$sController', true), $object, $this->name), E_USER_WARNING); + return array(); + } + $options = array_merge($this->params, $this->params['url'], $this->passedArgs); + + if (isset($this->paginate[$object->alias])) { + $defaults = $this->paginate[$object->alias]; + } else { + $defaults = $this->paginate; + } + + if (isset($options['show'])) { + $options['limit'] = $options['show']; + } + + if (isset($options['sort'])) { + $direction = null; + if (isset($options['direction'])) { + $direction = strtolower($options['direction']); + } + if ($direction != 'asc' && $direction != 'desc') { + $direction = 'asc'; + } + $options['order'] = array($options['sort'] => $direction); + } + + if (!empty($options['order']) && is_array($options['order'])) { + $alias = $object->alias ; + $key = $field = key($options['order']); + + if (strpos($key, '.') !== false) { + list($alias, $field) = explode('.', $key); + } + $value = $options['order'][$key]; + unset($options['order'][$key]); + + if (isset($object->{$alias}) && $object->{$alias}->hasField($field)) { + $options['order'][$alias . '.' . $field] = $value; + } elseif ($object->hasField($field)) { + $options['order'][$alias . '.' . $field] = $value; + } + } + $vars = array('fields', 'order', 'limit', 'page', 'recursive'); + $keys = array_keys($options); + $count = count($keys); + + for ($i = 0; $i < $count; $i++) { + if (!in_array($keys[$i], $vars, true)) { + unset($options[$keys[$i]]); + } + if (empty($whitelist) && ($keys[$i] === 'fields' || $keys[$i] === 'recursive')) { + unset($options[$keys[$i]]); + } elseif (!empty($whitelist) && !in_array($keys[$i], $whitelist)) { + unset($options[$keys[$i]]); + } + } + $conditions = $fields = $order = $limit = $page = $recursive = null; + + if (!isset($defaults['conditions'])) { + $defaults['conditions'] = array(); + } + + $type = 'all'; + + if (isset($defaults[0])) { + $type = $defaults[0]; + unset($defaults[0]); + } + + extract($options = array_merge(array('page' => 1, 'limit' => 20), $defaults, $options)); + + if (is_array($scope) && !empty($scope)) { + $conditions = array_merge($conditions, $scope); + } elseif (is_string($scope)) { + $conditions = array($conditions, $scope); + } + if ($recursive === null) { + $recursive = $object->recursive; + } + + $extra = array_diff_key($defaults, compact( + 'conditions', 'fields', 'order', 'limit', 'page', 'recursive' + )); + if ($type !== 'all') { + $extra['type'] = $type; + } + + if (method_exists($object, 'paginateCount')) { + $count = $object->paginateCount($conditions, $recursive, $extra); + } else { + $parameters = compact('conditions'); + if ($recursive != $object->recursive) { + $parameters['recursive'] = $recursive; + } + $count = $object->find('count', array_merge($parameters, $extra)); + } + $pageCount = intval(ceil($count / $limit)); + + if ($page === 'last' || $page >= $pageCount) { + $options['page'] = $page = $pageCount; + } elseif (intval($page) < 1) { + $options['page'] = $page = 1; + } + $page = $options['page'] = (integer)$page; + + if (method_exists($object, 'paginate')) { + $results = $object->paginate($conditions, $fields, $order, $limit, $page, $recursive, $extra); + } else { + $parameters = compact('conditions', 'fields', 'order', 'limit', 'page'); + if ($recursive != $object->recursive) { + $parameters['recursive'] = $recursive; + } + $results = $object->find($type, array_merge($parameters, $extra)); + } + $paging = array( + 'page' => $page, + 'current' => count($results), + 'count' => $count, + 'prevPage' => ($page > 1), + 'nextPage' => ($count > ($page * $limit)), + 'pageCount' => $pageCount, + 'defaults' => array_merge(array('limit' => 20, 'step' => 1), $defaults), + 'options' => $options + ); + $this->params['paging'][$object->alias] = $paging; + + if (!in_array('Paginator', $this->helpers) && !array_key_exists('Paginator', $this->helpers)) { + $this->helpers[] = 'Paginator'; + } + return $results; + } +/** + * Called before the controller action. + * + * @access public + * @link http://book.cakephp.org/view/60/Callbacks + */ + function beforeFilter() { + } +/** + * Called after the controller action is run, but before the view is rendered. + * + * @access public + * @link http://book.cakephp.org/view/60/Callbacks + */ + function beforeRender() { + } +/** + * Called after the controller action is run and rendered. + * + * @access public + * @link http://book.cakephp.org/view/60/Callbacks + */ + function afterFilter() { + } +/** + * This method should be overridden in child classes. + * + * @param string $method name of method called example index, edit, etc. + * @return boolean Success + * @access protected + * @link http://book.cakephp.org/view/60/Callbacks + */ + function _beforeScaffold($method) { + return true; + } +/** + * This method should be overridden in child classes. + * + * @param string $method name of method called either edit or update. + * @return boolean Success + * @access protected + * @link http://book.cakephp.org/view/60/Callbacks + */ + function _afterScaffoldSave($method) { + return true; + } +/** + * This method should be overridden in child classes. + * + * @param string $method name of method called either edit or update. + * @return boolean Success + * @access protected + * @link http://book.cakephp.org/view/60/Callbacks + */ + function _afterScaffoldSaveError($method) { + return true; + } +/** + * This method should be overridden in child classes. + * If not it will render a scaffold error. + * Method MUST return true in child classes + * + * @param string $method name of method called example index, edit, etc. + * @return boolean Success + * @access protected + * @link http://book.cakephp.org/view/60/Callbacks + */ + function _scaffoldError($method) { + return false; + } +} +?> diff --git a/cake/libs/controller/pages_controller.php b/cake/libs/controller/pages_controller.php new file mode 100755 index 0000000..8196616 --- /dev/null +++ b/cake/libs/controller/pages_controller.php @@ -0,0 +1,86 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Static content controller. + * + * This file will render views from views/pages/ + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.controller + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Static content controller + * + * Override this controller by placing a copy in controllers directory of an application + * + * @package cake + * @subpackage cake.cake.libs.controller + */ +class PagesController extends AppController { +/** + * Controller name + * + * @var string + * @access public + */ + var $name = 'Pages'; +/** + * Default helper + * + * @var array + * @access public + */ + var $helpers = array('Html'); +/** + * 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 = null; + + if (!empty($path[0])) { + $page = $path[0]; + } + if (!empty($path[1])) { + $subpage = $path[1]; + } + if (!empty($path[$count - 1])) { + $title = Inflector::humanize($path[$count - 1]); + } + $this->set(compact('page', 'subpage', 'title')); + $this->render(join('/', $path)); + } +} + +?> \ No newline at end of file diff --git a/cake/libs/controller/scaffold.php b/cake/libs/controller/scaffold.php new file mode 100755 index 0000000..165e2b5 --- /dev/null +++ b/cake/libs/controller/scaffold.php @@ -0,0 +1,528 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Scaffold. + * + * Automatic forms and actions generation for rapid web application development. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.controller + * @since Cake v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Scaffolding is a set of automatic views, forms and controllers 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; +/** + * 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; + $this->controller->pageTitle = __('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('modelClass', 'primaryKey', 'displayField', 'singularVar', 'pluralVar', + 'singularHumanName', 'pluralHumanName', 'scaffoldFields', 'associations')); + + if ($this->controller->view && $this->controller->view !== 'Theme') { + $this->controller->view = 'scaffold'; + } + $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')) { + + if (isset($params['pass'][0])) { + $this->ScaffoldModel->id = $params['pass'][0]; + } elseif (isset($this->controller->Session) && $this->controller->Session->valid() != false) { + $this->controller->Session->setFlash(sprintf(__("No id set for %s::view()", true), Inflector::humanize($this->modelKey))); + $this->controller->redirect($this->redirect); + } else { + return $this->controller->flash(sprintf(__("No id set for %s::view()", true), Inflector::humanize($this->modelKey)), + '/' . 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()", true), Inflector::humanize($this->modelKey)); + if (isset($this->controller->Session) && $this->controller->Session->valid() != false) { + $this->controller->Session->setFlash($message); + $this->controller->redirect($this->redirect); + } else { + return $this->controller->flash($message, $this->redirect); + } + } + } + + 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', true), + Inflector::humanize($this->modelKey), + $success + ); + if (isset($this->controller->Session) && $this->controller->Session->valid() != false) { + $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 (isset($this->controller->Session) && $this->controller->Session->valid() != false) { + $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')) { + if (isset($params['pass'][0])) { + $id = $params['pass'][0]; + } elseif (isset($this->controller->Session) && $this->controller->Session->valid() != false) { + $this->controller->Session->setFlash(sprintf(__("No id set for %s::delete()", true), Inflector::humanize($this->modelKey))); + $this->controller->redirect($this->redirect); + } else { + $this->controller->flash(sprintf(__("No id set for %s::delete()", true), Inflector::humanize($this->modelKey)), '/' . Inflector::underscore($this->controller->viewPath)); + return $this->_output(); + } + + if ($this->ScaffoldModel->del($id)) { + if (isset($this->controller->Session) && $this->controller->Session->valid() != false) { + $this->controller->Session->setFlash(sprintf(__('The %1$s with id: %2$d has been deleted.', true), Inflector::humanize($this->modelClass), $id)); + $this->controller->redirect($this->redirect); + } else { + $this->controller->flash(sprintf(__('The %1$s with id: %2$d has been deleted.', true), Inflector::humanize($this->modelClass), $id), '/' . $this->viewPath); + return $this->_output(); + } + } else { + if (isset($this->controller->Session) && $this->controller->Session->valid() != false) { + $this->controller->Session->setFlash(sprintf(__('There was an error deleting the %1$s with id: %2$d', true), Inflector::humanize($this->modelClass), $id)); + $this->controller->redirect($this->redirect); + } else { + $this->controller->flash(sprintf(__('There was an error deleting the %1$s with id: %2$d', true), Inflector::humanize($this->modelClass), $id), '/' . $this->viewPath); + 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: + * <code> + * var $scaffold; + * </code> + * 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); + $admin = Configure::read('Routing.admin'); + + if (isset($db)) { + if (empty($this->scaffoldActions)) { + $this->scaffoldActions = array('index', 'list', 'view', 'add', 'create', 'edit', 'update', 'delete'); + } elseif (!empty($admin) && $this->scaffoldActions === $admin) { + $this->scaffoldActions = array($admin .'_index', $admin .'_list', $admin .'_view', $admin .'_add', $admin .'_create', $admin .'_edit', $admin .'_update', $admin .'_delete'); + } + + if (in_array($params['action'], $this->scaffoldActions)) { + if (!empty($admin)) { + $params['action'] = str_replace($admin . '_', '', $params['action']); + } + switch ($params['action']) { + case 'index': + $this->__scaffoldIndex($params); + break; + case 'view': + $this->__scaffoldView($params); + break; + case 'list': + $this->__scaffoldIndex($params); + break; + case 'add': + $this->__scaffoldSave($params, 'add'); + break; + case 'edit': + $this->__scaffoldSave($params, 'edit'); + break; + case 'create': + $this->__scaffoldSave($params, 'add'); + break; + 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'])); + } + } + return $associations; + } +} + +/** + * Scaffold View. + * + * @package cake + * @subpackage cake.cake.libs.controller +*/ +if (!class_exists('ThemeView')) { + App::import('View', 'Theme'); +} + +class ScaffoldView extends ThemeView { +/** + * Override _getViewFileName + * + * @return string action + * @access protected + */ + function _getViewFileName($name = null) { + if ($name === null) { + $name = $this->action; + } + $name = Inflector::underscore($name); + $admin = Configure::read('Routing.admin'); + + if (!empty($admin) && strpos($name, $admin . '_') !== false) { + $name = substr($name, strlen($admin) + 1); + } + + 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, '.ctp', '.thtml'); + foreach ($paths as $path) { + foreach ($names as $name) { + foreach ($exts as $ext) { + 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'); + } +} +?> \ No newline at end of file diff --git a/cake/libs/debugger.php b/cake/libs/debugger.php new file mode 100755 index 0000000..1e942e5 --- /dev/null +++ b/cake/libs/debugger.php @@ -0,0 +1,577 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * 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://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v 1.2.4560 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Included libraries. + * + */ + if (!class_exists('Object')) { + uses('object'); + } + if (!class_exists('CakeLog')) { + uses('cake_log'); + } +/** + * 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/460/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'; +/** + * 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)) { + ini_set('docref_root', 'http://php.net/'); + } + if (!defined('E_RECOVERABLE_ERROR')) { + define('E_RECOVERABLE_ERROR', 4096); + } + } +/** + * 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 exportVar + * @access public + * @static + * @link http://book.cakephp.org/view/460/Using-the-Debugger-Class +*/ + function dump($var) { + $_this = Debugger::getInstance(); + pr($_this->exportVar($var)); + } +/** + * Creates a detailed stack trace log at the time of invocation, much like dump() + * but to 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/460/Using-the-Debugger-Class + */ + function log($var, $level = LOG_DEBUG) { + $_this = Debugger::getInstance(); + $trace = $_this->trace(array('start' => 1, 'depth' => 2, 'format' => 'array')); + $source = null; + + if (is_object($trace[0]['object']) && isset($trace[0]['object']->_reporter->_test_stack)) { + $stack = $trace[0]['object']->_reporter->_test_stack; + $source = sprintf('[%1$s, %3$s::%2$s()]' . "\n", + array_shift($stack), array_pop($stack), array_pop($stack)); + } + + CakeLog::write($level, $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 = '??'; + } + $file = $_this->trimPath($file); + + $info = compact('code', 'description', 'file', 'line'); + if (!in_array($info, $_this->errors)) { + $_this->errors[] = $info; + } else { + return; + } + + $level = LOG_DEBUG; + 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 false; + break; + } + + $helpCode = null; + if (!empty($_this->helpPath) && preg_match('/.*\[([0-9]+)\]$/', $description, $codes)) { + if (isset($codes[1])) { + $helpCode = $codes[1]; + $description = trim(preg_replace('/\[[0-9]+\]$/', '', $description)); + } + } + + echo $_this->_output($level, $error, $code, $helpCode, $description, $file, $line, $context); + + if (Configure::read('log')) { + CakeLog::write($level, "{$error} ({$code}): {$description} in [{$file}, line {$line}]"); + } + + if ($error == 'Fatal Error') { + die(); + } + return true; + } +/** + * Outputs a stack trace based on the supplied options. + * + * @param array $options Format for outputting stack trace + * @return string Formatted stack trace + * @access public + * @static + * @link http://book.cakephp.org/view/460/Using-the-Debugger-Class + */ + function trace($options = array()) { + $options = array_merge(array( + 'depth' => 999, + 'format' => '', + 'args' => false, + 'start' => 0, + 'scope' => null, + 'exclude' => null + ), + $options + ); + + $backtrace = debug_backtrace(); + $back = array(); + $count = count($backtrace); + + 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( + array( + 'line' => '??', + 'file' => '[internal]', + 'class' => null, + 'function' => '[main]' + ), + $backtrace[$i + 1] + ); + $function = $next['function']; + + if (!empty($next['class'])) { + $function = $next['class'] . '::' . $function . '('; + if ($options['args'] && isset($next['args'])) { + $args = array(); + foreach ($next['args'] as $arg) { + $args[] = Debugger::exportVar($arg); + } + $function .= join(', ', $args); + } + $function .= ')'; + } + } else { + $function = '[main]'; + } + if (in_array($function, array('call_user_func_array', 'trigger_error'))) { + continue; + } + if ($options['format'] == 'points' && $trace['file'] != '[internal]') { + $back[] = array('file' => $trace['file'], 'line' => $trace['line']); + } elseif (empty($options['format'])) { + $back[] = $function . ' - ' . Debugger::trimPath($trace['file']) . ', line ' . $trace['line']; + } else { + $back[] = $trace; + } + } + + if ($options['format'] == 'array' || $options['format'] == 'points') { + return $back; + } + return join("\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 = Configure::corePaths('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/460/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/460/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 (count($vars) > 0) { + $n = "\n"; + } + return $out . join(",", $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 join("\n", $out); + } +/** + * Handles object conversion to debug string. + * + * @param string $var Object to convert + * @access protected + */ + function output($format = 'js') { + $_this = Debugger::getInstance(); + $data = null; + + if ($format === true && !empty($_this->__data)) { + $data = $_this->__data; + $_this->__data = array(); + $format = false; + } + $_this->_outputFormat = $format; + + return $data; + } +/** + * Handles object conversion to debug string. + * + * @param string $var Object to convert + * @access private + */ + function _output($level, $error, $code, $helpCode, $description, $file, $line, $kontext) { + $files = $this->trace(array('start' => 2, 'format' => 'points')); + $listing = $this->excerpt($files[0]['file'], $files[0]['line'] - 1, 1); + $trace = $this->trace(array('start' => 2, 'depth' => '20')); + $context = array(); + + foreach ((array)$kontext as $var => $value) { + $context[] = "\${$var}\t=\t" . $this->exportVar($value, 1); + } + + switch ($this->_outputFormat) { + default: + case 'js': + $link = "document.getElementById(\"CakeStackTrace" . count($this->errors) . "\").style.display = (document.getElementById(\"CakeStackTrace" . count($this->errors) . "\").style.display == \"none\" ? \"\" : \"none\")"; + $out = "<a href='javascript:void(0);' onclick='{$link}'><b>{$error}</b> ({$code})</a>: {$description} [<b>{$file}</b>, line <b>{$line}</b>]"; + if (Configure::read() > 0) { + debug($out, false, false); + echo '<div id="CakeStackTrace' . count($this->errors) . '" class="cake-stack-trace" style="display: none;">'; + $link = "document.getElementById(\"CakeErrorCode" . count($this->errors) . "\").style.display = (document.getElementById(\"CakeErrorCode" . count($this->errors) . "\").style.display == \"none\" ? \"\" : \"none\")"; + echo "<a href='javascript:void(0);' onclick='{$link}'>Code</a>"; + + if (!empty($context)) { + $link = "document.getElementById(\"CakeErrorContext" . count($this->errors) . "\").style.display = (document.getElementById(\"CakeErrorContext" . count($this->errors) . "\").style.display == \"none\" ? \"\" : \"none\")"; + echo " | <a href='javascript:void(0);' onclick='{$link}'>Context</a>"; + + if (!empty($helpCode)) { + echo " | <a href='{$this->helpPath}{$helpCode}' target='_blank'>Help</a>"; + } + + echo "<pre id=\"CakeErrorContext" . count($this->errors) . "\" class=\"cake-context\" style=\"display: none;\">"; + echo implode("\n", $context); + echo "</pre>"; + } + + if (!empty($listing)) { + echo "<div id=\"CakeErrorCode" . count($this->errors) . "\" class=\"cake-code-dump\" style=\"display: none;\">"; + pr(implode("\n", $listing) . "\n", false); + echo '</div>'; + } + pr($trace, false); + echo '</div>'; + } + break; + case 'html': + echo "<pre class=\"cake-debug\"><b>{$error}</b> ({$code}) : {$description} [<b>{$file}</b>, line <b>{$line}]</b></pre>"; + if (!empty($context)) { + echo "Context:\n" .implode("\n", $context) . "\n"; + } + echo "<pre class=\"cake-debug context\"><b>Context</b> <p>" . implode("\n", $context) . "</p></pre>"; + echo "<pre class=\"cake-debug trace\"><b>Trace</b> <p>" . $trace. "</p></pre>"; + break; + case 'text': + case 'txt': + echo "{$error}: {$code} :: {$description} on line {$line} of {$file}\n"; + if (!empty($context)) { + echo "Context:\n" .implode("\n", $context) . "\n"; + } + echo "Trace:\n" . $trace; + break; + case 'log': + $this->log(compact('error', 'code', 'description', 'line', 'file', 'context', 'trace')); + break; + case false: + $this->__data[] = compact('error', 'code', 'description', 'line', 'file', 'context', 'trace'); + break; + } + } +/** + * Verifies that the application's salt value has been changed from the default value. + * + * @access public + * @static + */ + function checkSessionKey() { + 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); + } + } +/** + * 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/460/Using-the-Debugger-Class + */ + function invoke(&$debugger) { + set_error_handler(array(&$debugger, 'handleError')); + } +} + +if (!defined('DISABLE_DEFAULT_ERROR_HANDLING')) { + Debugger::invoke(Debugger::getInstance()); +} +?> \ No newline at end of file diff --git a/cake/libs/error.php b/cake/libs/error.php new file mode 100755 index 0000000..e8db827 --- /dev/null +++ b/cake/libs/error.php @@ -0,0 +1,378 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Error handler + * + * Provides Error Capturing for Framework errors. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v 0.10.5.1732 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +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() == 0) { + $method = 'error404'; + if (isset($code) && $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); + 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'); + } +/** + * 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->set(array( + '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->set(array( + '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->set(array( + '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 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; + } +} +?> \ No newline at end of file diff --git a/cake/libs/file.php b/cake/libs/file.php new file mode 100755 index 0000000..b57cb4c --- /dev/null +++ b/cake/libs/file.php @@ -0,0 +1,508 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Convenience class for reading, writing and appending to files. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Included libraries. + * + */ +if (!class_exists('Object')) { + uses('object'); +} +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(); + + if (!$this->exists()) { + if ($create === true) { + if ($this->safe($path) && $this->create() === false) { + return false; + } + } else { + return false; + } + } + } +/** + * 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 + * @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); + } + $data = trim($data); + + if ($this->lock !== null) { + flock($this->handle, LOCK_UN); + } + if ($bytes === false) { + $this->close(); + } + return $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 + * fixes line endings + * + * @param string $data Data to prepare for writing. + * @return string + * @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 extension. + * + * @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 + * @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 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; + } +} +?> \ No newline at end of file diff --git a/cake/libs/flay.php b/cake/libs/flay.php new file mode 100755 index 0000000..ed23cf7 --- /dev/null +++ b/cake/libs/flay.php @@ -0,0 +1,285 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Text-to-HTML parser. + * + * Text-to-html parser, similar to {@link http://textism.com/tools/textile/ Textile} or {@link http://www.whytheluckystiff.net/ruby/redcloth/ RedCloth}. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Included libraries. + * + */ +if (!class_exists('Object')) { + uses('object'); +} +/** + * Text-to-HTML parser. + * + * Text-to-html parser, similar to Textile or RedCloth, only with a little different syntax. + * + * @package cake + * @subpackage cake.cake.libs + */ +class Flay extends Object{ +/** + * Text to be parsed. + * + * @var string + * @access public + */ + var $text = null; +/** + * Set this to allow HTML in the markup. + * + * @var boolean + * @access public + */ + var $allow_html = false; +/** + * Constructor. + * + * @param string $text Text to transform + */ + function __construct($text = null) { + $this->text = $text; + parent::__construct(); + } +/** + * Returns given text translated to HTML using the Flay syntax. + * + * @param string $text String to format + * @param boolean $bare Set this to only do <p> transforms and > to &gt;, no typography additions. + * @param boolean $allowHtml Set this to trim whitespace and disable all HTML + * @return string Formatted text + * @access public + */ + function toHtml($text = null, $bare = false, $allowHtml = false) { + if (empty($text) && empty($this->text)) { + return false; + } + $text = $text ? $text : $this->text; + // trim whitespace and disable all HTML + if ($allowHtml) { + $text = trim($text); + } else { + $text = str_replace('<', '&lt;', str_replace('>', '&gt;', trim($text))); + } + + if (!$bare) { + // multi-paragraph functions + $text=preg_replace('#(?:[\n]{0,2})"""(.*)"""(?:[\n]{0,2})#s', "\n\n%BLOCKQUOTE%\n\n\\1\n\n%ENDBLOCKQUOTE%\n\n", $text); + $text=preg_replace('#(?:[\n]{0,2})===(.*)===(?:[\n]{0,2})#s', "\n\n%CENTER%\n\n\\1\n\n%ENDCENTER%\n\n", $text); + } + + // pre-parse newlines + $text=preg_replace("#\r\n#", "\n", $text); + $text=preg_replace("#[\n]{2,}#", "%PARAGRAPH%", $text); + $text=preg_replace('#[\n]{1}#', "%LINEBREAK%", $text); + $out =''; + + foreach (split('%PARAGRAPH%', $text)as $line) { + if ($line) { + if (!$bare) { + $links = array(); + $regs = null; + + if (preg_match_all('#\[([^\[]{4,})\]#', $line, $regs)) { + foreach ($regs[1] as $reg) { + $links[] = $reg; + $line = str_replace("[{$reg}]", '%LINK' . (count($links) - 1) . '%', $line); + } + } + // bold + $line = ereg_replace("\*([^\*]*)\*", "<strong>\\1</strong>", $line); + // italic + $line = ereg_replace("_([^_]*)_", "<em>\\1</em>", $line); + } + // entities + $line = str_replace(' - ', ' &ndash; ', $line); + $line = str_replace(' -- ', ' &mdash; ', $line); + $line = str_replace('(C)', '&copy;', $line); + $line = str_replace('(R)', '&reg;', $line); + $line = str_replace('(TM)', '&trade;', $line); + // guess e-mails + $emails = null; + if (preg_match_all("#([_A-Za-z0-9+-+]+(?:\.[_A-Za-z0-9+-]+)*@[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)*)#", $line, $emails)) { + foreach ($emails[1] as $email) { + $line = str_replace($email, "<a href=\"mailto:{$email}\">{$email}</a>", $line); + } + } + + if (!$bare) { + $urls = null; + if (preg_match_all("#((?:http|https|ftp|nntp)://[^ ]+)#", $line, $urls)) { + foreach ($urls[1] as $url) { + $line = str_replace($url, "<a href=\"{$url}\">{$url}</a>", $line); + } + } + + if (preg_match_all("#(www\.[^\n\%\ ]+[^\n\%\,\.\ ])#", $line, $urls)) { + foreach ($urls[1] as $url) { + $line = str_replace($url, "<a href=\"http://{$url}\">{$url}</a>", $line); + } + } + + if ($count = count($links)) { + for ($ii = 0; $ii < $count; $ii++) { + if (preg_match("#^(http|https|ftp|nntp)://#", $links[$ii])) { + $prefix = null; + } else { + $prefix = 'http://'; + } + if (preg_match('#^[^\ ]+\.(jpg|jpeg|gif|png)$#', $links[$ii])) { + $with = "<img src=\"{$prefix}{$links[$ii]}\" alt=\"\" />"; + } elseif (preg_match('#^([^\]\ ]+)(?:\ ([^\]]+))?$#', $links[$ii], $regs)) { + if (isset($regs[2])) { + if (preg_match('#\.(jpg|jpeg|gif|png)$#', $regs[2])) { + $body = "<img src=\"{$prefix}{$regs[2]}\" alt=\"\" />"; + } else { + $body = $regs[2]; + } + } else { + $body = $links[$ii]; + } + $with = "<a href=\"{$prefix}{$regs[1]}\" target=\"_blank\">{$body}</a>"; + } else { + $with = $prefix . $links[$ii]; + } + $line = str_replace("%LINK{$ii}%", $with, $line); + } + } + } + $out .= str_replace('%LINEBREAK%', "<br />\n", "<p>{$line}</p>\n"); + } + } + + if (!$bare) { + $out = str_replace('<p>%BLOCKQUOTE%</p>', "<blockquote>", $out); + $out = str_replace('<p>%ENDBLOCKQUOTE%</p>', "</blockquote>", $out); + $out = str_replace('<p>%CENTER%</p>', "<center>", $out); + $out = str_replace('<p>%ENDCENTER%</p>', "</center>", $out); + } + return $out; + } +/** + * Return the words of the string as an array. + * + * @param string $string + * @return array Array of words + * @access public + */ + function extractWords($string) { + $split = preg_split('/[\s,\.:\/="!\(\)<>~\[\]]+/', $string); + return $split; + } +/** + * Return given string with words in array colorMarked, up to a number of times (defaults to 5). + * + * @param array $words Words to look for and markup + * @param string $string String to look in + * @param integer $max_snippets Max number of snippets to extract + * @return string String with words marked + * @see colorMark + * @access public + */ + function markedSnippets($words, $string, $max_snippets = 5) { + $string = strip_tags($string); + $snips = array(); + $rest = $string; + foreach ($words as $word) { + if (preg_match_all("/[\s,]+.{0,40}{$word}.{0,40}[\s,]+/i", $rest, $r)) { + foreach ($r as $result) { + $rest = str_replace($result, '', $rest); + } + $snips = array_merge($snips, $r[0]); + } + } + + if (count($snips) > $max_snippets) { + $snips = array_slice($snips, 0, $max_snippets); + } + $joined = join(' <b>...</b> ', $snips); + $snips = $joined ? "<b>...</b> {$joined} <b>...</b>" : substr($string, 0, 80) . '<b>...</b>'; + return $this->colorMark($words, $snips); + } +/** + * Returns string with EM elements with color classes added. + * + * @param array $words Array of words to be colorized + * @param string $string Text in which the words might be found + * @return string String with words colorized + * @access public + */ + function colorMark($words, $string) { + $colors=array('yl', 'gr', 'rd', 'bl', 'fu', 'cy'); + $nextColorIndex = 0; + foreach ($words as $word) { + $string = preg_replace("/({$word})/i", '<em class="' . $colors[$nextColorIndex % count($colors)] . "\">\\1</em>", $string); + $nextColorIndex++; + } + return $string; + } +/** + * Returns given text with tags stripped out. + * + * @param string $text Text to clean + * @return string Cleaned text + * @access public + */ + function toClean($text) { + $strip = strip_tags(html_entity_decode($text, ENT_QUOTES)); + return $strip; + } +/** + * Return parsed text with tags stripped out. + * + * @param string $text Text to parse and clean + * @return string Cleaned text + * @access public + */ + function toParsedAndClean($text) { + return $this->toClean(Flay::toHtml($text)); + } +/** + * Return a fragment of a text, up to $length characters long, with an ellipsis after it. + * + * @param string $text Text to be truncated. + * @param integer $length Max length of text. + * @param string $ellipsis Sign to print after truncated text. + * @return string Fragment + * @access public + */ + function fragment($text, $length, $ellipsis = '...') { + $soft = $length - 5; + $hard = $length + 5; + $rx = '/(.{' . $soft . ',' . $hard . '})[\s,\.:\/="!\(\)<>~\[\]]+.*/'; + + if (preg_match($rx, $text, $r)) { + $out = $r[1]; + } else { + $out = substr($text, 0, $length); + } + $out = $out . (strlen($out) < strlen($text) ? $ellipsis : null); + return $out; + } +} +?> \ No newline at end of file diff --git a/cake/libs/folder.php b/cake/libs/folder.php new file mode 100755 index 0000000..28f3267 --- /dev/null +++ b/cake/libs/folder.php @@ -0,0 +1,773 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Convenience class for handling directories. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Included libraries. + * + */ +if (!class_exists('Object')) { + uses('object'); +} +/** + * Folder structure browser, lists folders and files. + * + * Long description for class + * + * @package cake + * @subpackage cake.cake.libs + */ +class Folder extends Object { +/** + * Path to Folder. + * + * @var string + * @access public + */ + var $path = null; +/** + * Sortedness. + * + * @var boolean + * @access public + */ + var $sort = false; +/** + * mode to be used on create. + * + * @var boolean + * @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 + * @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 (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: .*) + * @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: .*) + * @return array Files matching $pattern + * @access public + */ + function findRecursive($pattern = '.*', $sort = false) { + $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 + * @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) { + if (preg_match('/^[A-Z]:\\\\/i', $path)) { + return true; + } + return false; + } +/** + * Returns true if given $path is an absolute path. + * + * @param string $path Path to check + * @return bool + * @access public + * @static + */ + function isAbsolute($path) { + $match = preg_match('/^\\//', $path) || preg_match('/^[A-Z]:\\\\/i', $path); + return $match; + } +/** + * 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) { + if (Folder::isWindowsPath($path)) { + return '\\'; + } + return '/'; + } +/** + * 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 Folder::slashTerm($path) . $element; + } +/** + * Returns true if the File is in a given CakePath. + * + * @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. + * + * @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); + } + if ($return == 1) { + return true; + } else { + return false; + } + } +/** + * 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 + * @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 boolean $hidden return hidden files and directories + * @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); + $this->__files = array(); + $this->__directories = array($path); + $directories = array(); + + if ($exceptions === false) { + $exceptions = true; + } + while (count($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 + * @param = boolean $hidden + * @access private + */ + function __tree($path, $exceptions) { + if ($this->cd($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. + * + * @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; + } + $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 true; + } +/** + * Returns the size in bytes of this Folder. + * + * @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 system allow. + * + * @param string $path Path of directory to delete + * @return boolean Success + * @access public + */ + function delete($path = null) { + if (!$path) { + $path = $this->pwd(); + } + $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. + * + * @param array $options (to, from, chmod, skip) + * @return bool + * @access public + */ + function copy($options = array()) { + $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->mkdir($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. + * + * @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; + } +/** + * nix flavored alias + * + * @see read + * @access public + */ + function ls($sort = true, $exceptions = false) { + return $this->read($sort, $exceptions); + } +/** + * nix flavored alias + * + * @see create + * @access public + */ + function mkdir($pathname, $mode = 0755) { + return $this->create($pathname, $mode); + } +/** + * nix flavored alias + * + * @see copy + * @access public + */ + function cp($options) { + return $this->copy($options); + } +/** + * nix flavored alias + * + * @see move + * @access public + */ + function mv($options) { + return $this->move($options); + } +/** + * nix flavored alias + * + * @see delete + * @access public + */ + function rm($path) { + return $this->delete($path); + } +/** + * 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 (count($newparts) > 0) { + 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 === '\\'; + } +} +?> \ No newline at end of file diff --git a/cake/libs/http_socket.php b/cake/libs/http_socket.php new file mode 100755 index 0000000..76d12d7 --- /dev/null +++ b/cake/libs/http_socket.php @@ -0,0 +1,992 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * HTTP Socket connection class. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v 1.2.0 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +App::import('Core', array('Socket', 'Set', 'Router')); +/** + * Cake network socket connection class. + * + * Core base class for HTTP network communication. + * + * @package cake + * @subpackage cake.cake.libs + */ +class HttpSocket extends CakeSocket { +/** + * Object description + * + * @var string + * @access public + */ + var $description = 'HTTP-based DataSource Interface'; +/** + * When one activates the $quirksMode by setting it to true, all checks meant to enforce RFC 2616 (HTTP/1.1 specs) + * will be disabled and additional measures to deal with non-standard responses will be enabled. + * + * @var boolean + * @access public + */ + var $quirksMode = false; +/** + * The default values to use for a request + * + * @var array + * @access public + */ + var $request = array( + 'method' => 'GET', + 'uri' => array( + 'scheme' => 'http', + 'host' => null, + 'port' => 80, + 'user' => null, + 'pass' => null, + 'path' => null, + 'query' => null, + 'fragment' => null + ), + 'auth' => array( + 'method' => 'Basic', + 'user' => null, + 'pass' => null + ), + 'version' => '1.1', + 'body' => '', + 'line' => null, + 'header' => array( + 'Connection' => 'close', + 'User-Agent' => 'CakePHP' + ), + 'raw' => null, + 'cookies' => array() + ); +/** +* The default structure for storing the response +* +* @var array +* @access public +*/ + var $response = array( + 'raw' => array( + 'status-line' => null, + 'header' => null, + 'body' => null, + 'response' => null + ), + 'status' => array( + 'http-version' => null, + 'code' => null, + 'reason-phrase' => null + ), + 'header' => array(), + 'body' => '', + 'cookies' => array() + ); +/** + * Default configuration settings for the HttpSocket + * + * @var array + * @access public + */ + var $config = array( + 'persistent' => false, + 'host' => 'localhost', + 'protocol' => 'tcp', + 'port' => 80, + 'timeout' => 30, + 'request' => array( + 'uri' => array( + 'scheme' => 'http', + 'host' => 'localhost', + 'port' => 80 + ), + 'auth' => array( + 'method' => 'Basic', + 'user' => null, + 'pass' => null + ), + 'cookies' => array() + ) + ); +/** + * String that represents a line break. + * + * @var string + * @access public + */ + var $lineBreak = "\r\n"; + +/** + * Build an HTTP Socket using the specified configuration. + * + * @param array $config Configuration + */ + function __construct($config = array()) { + if (is_string($config)) { + $this->configUri($config); + } elseif (is_array($config)) { + if (isset($config['request']['uri']) && is_string($config['request']['uri'])) { + $this->configUri($config['request']['uri']); + unset($config['request']['uri']); + } + $this->config = Set::merge($this->config, $config); + } + parent::__construct($this->config); + } +/** + * Issue the specified request. + * + * @param mixed $request Either an URI string, or an array defining host/uri + * @return mixed false on error, request body on success + * @access public + */ + function request($request = array()) { + $this->reset(false); + + if (is_string($request)) { + $request = array('uri' => $request); + } elseif (!is_array($request)) { + return false; + } + + if (!isset($request['uri'])) { + $request['uri'] = null; + } + $uri = $this->parseUri($request['uri']); + + if (!isset($uri['host'])) { + $host = $this->config['host']; + } + if (isset($request['host'])) { + $host = $request['host']; + unset($request['host']); + } + + $request['uri'] = $this->url($request['uri']); + $request['uri'] = $this->parseUri($request['uri'], true); + $this->request = Set::merge($this->request, $this->config['request'], $request); + + $this->configUri($this->request['uri']); + + if (isset($host)) { + $this->config['host'] = $host; + } + $cookies = null; + + if (is_array($this->request['header'])) { + $this->request['header'] = $this->parseHeader($this->request['header']); + if (!empty($this->request['cookies'])) { + $cookies = $this->buildCookies($this->request['cookies']); + } + $this->request['header'] = array_merge(array('Host' => $this->request['uri']['host']), $this->request['header']); + } + + if (isset($this->request['auth']['user']) && isset($this->request['auth']['pass'])) { + $this->request['header']['Authorization'] = $this->request['auth']['method'] . " " . base64_encode($this->request['auth']['user'] . ":" . $this->request['auth']['pass']); + } + if (isset($this->request['uri']['user']) && isset($this->request['uri']['pass'])) { + $this->request['header']['Authorization'] = $this->request['auth']['method'] . " " . base64_encode($this->request['uri']['user'] . ":" . $this->request['uri']['pass']); + } + + if (is_array($this->request['body'])) { + $this->request['body'] = $this->httpSerialize($this->request['body']); + } + + if (!empty($this->request['body']) && !isset($this->request['header']['Content-Type'])) { + $this->request['header']['Content-Type'] = 'application/x-www-form-urlencoded'; + } + + if (!empty($this->request['body']) && !isset($this->request['header']['Content-Length'])) { + $this->request['header']['Content-Length'] = strlen($this->request['body']); + } + + $connectionType = null; + if (isset($this->request['header']['Connection'])) { + $connectionType = $this->request['header']['Connection']; + } + $this->request['header'] = $this->buildHeader($this->request['header']).$cookies; + + if (empty($this->request['line'])) { + $this->request['line'] = $this->buildRequestLine($this->request); + } + + if ($this->quirksMode === false && $this->request['line'] === false) { + return $this->response = false; + } + + if ($this->request['line'] !== false) { + $this->request['raw'] = $this->request['line']; + } + + if ($this->request['header'] !== false) { + $this->request['raw'] .= $this->request['header']; + } + + $this->request['raw'] .= "\r\n"; + $this->request['raw'] .= $this->request['body']; + $this->write($this->request['raw']); + + $response = null; + while ($data = $this->read()) { + $response .= $data; + } + + if ($connectionType == 'close') { + $this->disconnect(); + } + + $this->response = $this->parseResponse($response); + if (!empty($this->response['cookies'])) { + $this->config['request']['cookies'] = array_merge($this->config['request']['cookies'], $this->response['cookies']); + } + + return $this->response['body']; + } +/** + * Issues a GET request to the specified URI, query, and request. + * + * @param mixed $uri URI to request (see {@link parseUri()}) + * @param array $query Query to append to URI + * @param array $request An indexed array with indexes such as 'method' or uri + * @return mixed Result of request + * @access public + */ + function get($uri = null, $query = array(), $request = array()) { + if (!empty($query)) { + $uri =$this->parseUri($uri); + if (isset($uri['query'])) { + $uri['query'] = array_merge($uri['query'], $query); + } else { + $uri['query'] = $query; + } + $uri = $this->buildUri($uri); + } + + $request = Set::merge(array('method' => 'GET', 'uri' => $uri), $request); + return $this->request($request); + } + +/** + * Issues a POST request to the specified URI, query, and request. + * + * @param mixed $uri URI to request (see {@link parseUri()}) + * @param array $query Query to append to URI + * @param array $request An indexed array with indexes such as 'method' or uri + * @return mixed Result of request + * @access public + */ + function post($uri = null, $data = array(), $request = array()) { + $request = Set::merge(array('method' => 'POST', 'uri' => $uri, 'body' => $data), $request); + return $this->request($request); + } +/** + * Issues a PUT request to the specified URI, query, and request. + * + * @param mixed $uri URI to request (see {@link parseUri()}) + * @param array $query Query to append to URI + * @param array $request An indexed array with indexes such as 'method' or uri + * @return mixed Result of request + * @access public + */ + function put($uri = null, $data = array(), $request = array()) { + $request = Set::merge(array('method' => 'PUT', 'uri' => $uri, 'body' => $data), $request); + return $this->request($request); + } +/** + * Issues a DELETE request to the specified URI, query, and request. + * + * @param mixed $uri URI to request (see {@link parseUri()}) + * @param array $query Query to append to URI + * @param array $request An indexed array with indexes such as 'method' or uri + * @return mixed Result of request + * @access public + */ + function delete($uri = null, $data = array(), $request = array()) { + $request = Set::merge(array('method' => 'DELETE', 'uri' => $uri, 'body' => $data), $request); + return $this->request($request); + } +/** + * undocumented function + * + * @param unknown $url + * @param unknown $uriTemplate + * @return void + * @access public + */ + function url($url = null, $uriTemplate = null) { + if (is_null($url)) { + $url = '/'; + } + if (is_string($url)) { + if ($url{0} == '/') { + $url = $this->config['request']['uri']['host'].':'.$this->config['request']['uri']['port'] . $url; + } + if (!preg_match('/^.+:\/\/|\*|^\//', $url)) { + $url = $this->config['request']['uri']['scheme'].'://'.$url; + } + } elseif (!is_array($url) && !empty($url)) { + return false; + } + + $base = array_merge($this->config['request']['uri'], array('scheme' => array('http', 'https'), 'port' => array(80, 443))); + $url = $this->parseUri($url, $base); + + if (empty($url)) { + $url = $this->config['request']['uri']; + } + + if (!empty($uriTemplate)) { + return $this->buildUri($url, $uriTemplate); + } + return $this->buildUri($url); + } +/** + * Parses the given message and breaks it down in parts. + * + * @param string $message Message to parse + * @return array Parsed message (with indexed elements such as raw, status, header, body) + * @access protected + */ + function parseResponse($message) { + if (is_array($message)) { + return $message; + } elseif (!is_string($message)) { + return false; + } + + static $responseTemplate; + + if (empty($responseTemplate)) { + $classVars = get_class_vars(__CLASS__); + $responseTemplate = $classVars['response']; + } + + $response = $responseTemplate; + + if (!preg_match("/^(.+\r\n)(.*)(?<=\r\n)\r\n/Us", $message, $match)) { + return false; + } + + list($null, $response['raw']['status-line'], $response['raw']['header']) = $match; + $response['raw']['response'] = $message; + $response['raw']['body'] = substr($message, strlen($match[0])); + + if (preg_match("/(.+) ([0-9]{3}) (.+)\r\n/DU", $response['raw']['status-line'], $match)) { + $response['status']['http-version'] = $match[1]; + $response['status']['code'] = (int)$match[2]; + $response['status']['reason-phrase'] = $match[3]; + } + + $response['header'] = $this->parseHeader($response['raw']['header']); + $transferEncoding = null; + if (isset($response['header']['Transfer-Encoding'])) { + $transferEncoding = $response['header']['Transfer-Encoding']; + } + $decoded = $this->decodeBody($response['raw']['body'], $transferEncoding); + $response['body'] = $decoded['body']; + + if (!empty($decoded['header'])) { + $response['header'] = $this->parseHeader($this->buildHeader($response['header']).$this->buildHeader($decoded['header'])); + } + + if (!empty($response['header'])) { + $response['cookies'] = $this->parseCookies($response['header']); + } + + foreach ($response['raw'] as $field => $val) { + if ($val === '') { + $response['raw'][$field] = null; + } + } + + return $response; + } +/** + * Generic function to decode a $body with a given $encoding. Returns either an array with the keys + * 'body' and 'header' or false on failure. + * + * @param string $body A string continaing the body to decode + * @param mixed $encoding Can be false in case no encoding is being used, or a string representing the encoding + * @return mixed Array or false + * @access protected + */ + function decodeBody($body, $encoding = 'chunked') { + if (!is_string($body)) { + return false; + } + if (empty($encoding)) { + return array('body' => $body, 'header' => false); + } + $decodeMethod = 'decode'.Inflector::camelize(str_replace('-', '_', $encoding)).'Body'; + + if (!is_callable(array(&$this, $decodeMethod))) { + if (!$this->quirksMode) { + trigger_error(sprintf(__('HttpSocket::decodeBody - Unknown encoding: %s. Activate quirks mode to surpress error.', true), h($encoding)), E_USER_WARNING); + } + return array('body' => $body, 'header' => false); + } + return $this->{$decodeMethod}($body); + } +/** + * Decodes a chunked message $body and returns either an array with the keys 'body' and 'header' or false as + * a result. + * + * @param string $body A string continaing the chunked body to decode + * @return mixed Array or false + * @access protected + */ + function decodeChunkedBody($body) { + if (!is_string($body)) { + return false; + } + + $decodedBody = null; + $chunkLength = null; + + while ($chunkLength !== 0) { + if (!preg_match("/^([0-9a-f]+) *(?:;(.+)=(.+))?\r\n/iU", $body, $match)) { + if (!$this->quirksMode) { + trigger_error(__('HttpSocket::decodeChunkedBody - Could not parse malformed chunk. Activate quirks mode to do this.', true), E_USER_WARNING); + return false; + } + break; + } + + $chunkSize = 0; + $hexLength = 0; + $chunkExtensionName = ''; + $chunkExtensionValue = ''; + if (isset($match[0])) { + $chunkSize = $match[0]; + } + if (isset($match[1])) { + $hexLength = $match[1]; + } + if (isset($match[2])) { + $chunkExtensionName = $match[2]; + } + if (isset($match[3])) { + $chunkExtensionValue = $match[3]; + } + + $body = substr($body, strlen($chunkSize)); + $chunkLength = hexdec($hexLength); + $chunk = substr($body, 0, $chunkLength); + if (!empty($chunkExtensionName)) { + /** + * @todo See if there are popular chunk extensions we should implement + */ + } + $decodedBody .= $chunk; + if ($chunkLength !== 0) { + $body = substr($body, $chunkLength+strlen("\r\n")); + } + } + + $entityHeader = false; + if (!empty($body)) { + $entityHeader = $this->parseHeader($body); + } + return array('body' => $decodedBody, 'header' => $entityHeader); + } +/** + * Parses and sets the specified URI into current request configuration. + * + * @param mixed $uri URI (see {@link parseUri()}) + * @return array Current configuration settings + * @access protected + */ + function configUri($uri = null) { + if (empty($uri)) { + return false; + } + + if (is_array($uri)) { + $uri = $this->parseUri($uri); + } else { + $uri = $this->parseUri($uri, true); + } + + if (!isset($uri['host'])) { + return false; + } + + $config = array( + 'request' => array( + 'uri' => array_intersect_key($uri, $this->config['request']['uri']), + 'auth' => array_intersect_key($uri, $this->config['request']['auth']) + ) + ); + $this->config = Set::merge($this->config, $config); + $this->config = Set::merge($this->config, array_intersect_key($this->config['request']['uri'], $this->config)); + return $this->config; + } +/** + * Takes a $uri array and turns it into a fully qualified URL string + * + * @param array $uri A $uri array, or uses $this->config if left empty + * @param string $uriTemplate The Uri template/format to use + * @return string A fully qualified URL formated according to $uriTemplate + * @access protected + */ + function buildUri($uri = array(), $uriTemplate = '%scheme://%user:%pass@%host:%port/%path?%query#%fragment') { + if (is_string($uri)) { + $uri = array('host' => $uri); + } + $uri = $this->parseUri($uri, true); + + if (!is_array($uri) || empty($uri)) { + return false; + } + + $uri['path'] = preg_replace('/^\//', null, $uri['path']); + $uri['query'] = $this->httpSerialize($uri['query']); + $stripIfEmpty = array( + 'query' => '?%query', + 'fragment' => '#%fragment', + 'user' => '%user:%pass@' + ); + + foreach ($stripIfEmpty as $key => $strip) { + if (empty($uri[$key])) { + $uriTemplate = str_replace($strip, null, $uriTemplate); + } + } + + $defaultPorts = array('http' => 80, 'https' => 443); + if (array_key_exists($uri['scheme'], $defaultPorts) && $defaultPorts[$uri['scheme']] == $uri['port']) { + $uriTemplate = str_replace(':%port', null, $uriTemplate); + } + + foreach ($uri as $property => $value) { + $uriTemplate = str_replace('%'.$property, $value, $uriTemplate); + } + + if ($uriTemplate === '/*') { + $uriTemplate = '*'; + } + return $uriTemplate; + } +/** + * Parses the given URI and breaks it down into pieces as an indexed array with elements + * such as 'scheme', 'port', 'query'. + * + * @param string $uri URI to parse + * @param mixed $base If true use default URI config, otherwise indexed array to set 'scheme', 'host', 'port', etc. + * @return array Parsed URI + * @access protected + */ + function parseUri($uri = null, $base = array()) { + $uriBase = array( + 'scheme' => array('http', 'https'), + 'host' => null, + 'port' => array(80, 443), + 'user' => null, + 'pass' => null, + 'path' => '/', + 'query' => null, + 'fragment' => null + ); + + if (is_string($uri)) { + $uri = parse_url($uri); + } + if (!is_array($uri) || empty($uri)) { + return false; + } + if ($base === true) { + $base = $uriBase; + } + + if (isset($base['port'], $base['scheme']) && is_array($base['port']) && is_array($base['scheme'])) { + if (isset($uri['scheme']) && !isset($uri['port'])) { + $base['port'] = $base['port'][array_search($uri['scheme'], $base['scheme'])]; + } elseif (isset($uri['port']) && !isset($uri['scheme'])) { + $base['scheme'] = $base['scheme'][array_search($uri['port'], $base['port'])]; + } + } + + if (is_array($base) && !empty($base)) { + $uri = array_merge($base, $uri); + } + + if (isset($uri['scheme']) && is_array($uri['scheme'])) { + $uri['scheme'] = array_shift($uri['scheme']); + } + if (isset($uri['port']) && is_array($uri['port'])) { + $uri['port'] = array_shift($uri['port']); + } + + if (array_key_exists('query', $uri)) { + $uri['query'] = $this->parseQuery($uri['query']); + } + + if (!array_intersect_key($uriBase, $uri)) { + return false; + } + return $uri; + } +/** + * This function can be thought of as a reverse to PHP5's http_build_query(). It takes a given query string and turns it into an array and + * supports nesting by using the php bracket syntax. So this menas you can parse queries like: + * + * - ?key[subKey]=value + * - ?key[]=value1&key[]=value2 + * + * A leading '?' mark in $query is optional and does not effect the outcome of this function. For the complete capabilities of this implementation + * take a look at HttpSocketTest::testParseQuery() + * + * @param mixed $query A query string to parse into an array or an array to return directly "as is" + * @return array The $query parsed into a possibly multi-level array. If an empty $query is given, an empty array is returned. + * @access protected + */ + function parseQuery($query) { + if (is_array($query)) { + return $query; + } + $parsedQuery = array(); + + if (is_string($query) && !empty($query)) { + $query = preg_replace('/^\?/', '', $query); + $items = explode('&', $query); + + foreach ($items as $item) { + if (strpos($item, '=') !== false) { + list($key, $value) = explode('=', $item); + } else { + $key = $item; + $value = null; + } + + $key = urldecode($key); + $value = urldecode($value); + + if (preg_match_all('/\[([^\[\]]*)\]/iUs', $key, $matches)) { + $subKeys = $matches[1]; + $rootKey = substr($key, 0, strpos($key, '[')); + if (!empty($rootKey)) { + array_unshift($subKeys, $rootKey); + } + $queryNode =& $parsedQuery; + + foreach ($subKeys as $subKey) { + if (!is_array($queryNode)) { + $queryNode = array(); + } + + if ($subKey === '') { + $queryNode[] = array(); + end($queryNode); + $subKey = key($queryNode); + } + $queryNode =& $queryNode[$subKey]; + } + $queryNode = $value; + } else { + $parsedQuery[$key] = $value; + } + } + } + return $parsedQuery; + } +/** + * Builds a request line according to HTTP/1.1 specs. Activate quirks mode to work outside specs. + * + * @param array $request Needs to contain a 'uri' key. Should also contain a 'method' key, otherwise defaults to GET. + * @param string $versionToken The version token to use, defaults to HTTP/1.1 + * @return string Request line + * @access protected + */ + function buildRequestLine($request = array(), $versionToken = 'HTTP/1.1') { + $asteriskMethods = array('OPTIONS'); + + if (is_string($request)) { + $isValid = preg_match("/(.+) (.+) (.+)\r\n/U", $request, $match); + if (!$this->quirksMode && (!$isValid || ($match[2] == '*' && !in_array($match[3], $asteriskMethods)))) { + trigger_error(__('HttpSocket::buildRequestLine - Passed an invalid request line string. Activate quirks mode to do this.', true), E_USER_WARNING); + return false; + } + return $request; + } elseif (!is_array($request)) { + return false; + } elseif (!array_key_exists('uri', $request)) { + return false; + } + + $request['uri'] = $this->parseUri($request['uri']); + $request = array_merge(array('method' => 'GET'), $request); + $request['uri'] = $this->buildUri($request['uri'], '/%path?%query'); + + if (!$this->quirksMode && $request['uri'] === '*' && !in_array($request['method'], $asteriskMethods)) { + trigger_error(sprintf(__('HttpSocket::buildRequestLine - The "*" asterisk character is only allowed for the following methods: %s. Activate quirks mode to work outside of HTTP/1.1 specs.', true), join(',', $asteriskMethods)), E_USER_WARNING); + return false; + } + return $request['method'].' '.$request['uri'].' '.$versionToken.$this->lineBreak; + } +/** + * Serializes an array for transport. + * + * @param array $data Data to serialize + * @return string Serialized variable + * @access protected + */ + function httpSerialize($data = array()) { + if (is_string($data)) { + return $data; + } + if (empty($data) || !is_array($data)) { + return false; + } + return substr(Router::queryString($data), 1); + } +/** + * Builds the header. + * + * @param array $header Header to build + * @return string Header built from array + * @access protected + */ + function buildHeader($header, $mode = 'standard') { + if (is_string($header)) { + return $header; + } elseif (!is_array($header)) { + return false; + } + + $returnHeader = ''; + foreach ($header as $field => $contents) { + if (is_array($contents) && $mode == 'standard') { + $contents = join(',', $contents); + } + foreach ((array)$contents as $content) { + $contents = preg_replace("/\r\n(?![\t ])/", "\r\n ", $content); + $field = $this->escapeToken($field); + + $returnHeader .= $field.': '.$contents.$this->lineBreak; + } + } + return $returnHeader; + } + +/** + * Parses an array based header. + * + * @param array $header Header as an indexed array (field => value) + * @return array Parsed header + * @access protected + */ + function parseHeader($header) { + if (is_array($header)) { + foreach ($header as $field => $value) { + unset($header[$field]); + $field = strtolower($field); + preg_match_all('/(?:^|(?<=-))[a-z]/U', $field, $offsets, PREG_OFFSET_CAPTURE); + + foreach ($offsets[0] as $offset) { + $field = substr_replace($field, strtoupper($offset[0]), $offset[1], 1); + } + $header[$field] = $value; + } + return $header; + } elseif (!is_string($header)) { + return false; + } + + preg_match_all("/(.+):(.+)(?:(?<![\t ])" . $this->lineBreak . "|\$)/Uis", $header, $matches, PREG_SET_ORDER); + + $header = array(); + foreach ($matches as $match) { + list(, $field, $value) = $match; + + $value = trim($value); + $value = preg_replace("/[\t ]\r\n/", "\r\n", $value); + + $field = $this->unescapeToken($field); + + $field = strtolower($field); + preg_match_all('/(?:^|(?<=-))[a-z]/U', $field, $offsets, PREG_OFFSET_CAPTURE); + foreach ($offsets[0] as $offset) { + $field = substr_replace($field, strtoupper($offset[0]), $offset[1], 1); + } + + if (!isset($header[$field])) { + $header[$field] = $value; + } else { + $header[$field] = array_merge((array)$header[$field], (array)$value); + } + } + return $header; + } +/** + * undocumented function + * + * @param unknown $header + * @return void + * @access public + * @todo Make this 100% RFC 2965 confirm + */ + function parseCookies($header) { + if (!isset($header['Set-Cookie'])) { + return false; + } + + $cookies = array(); + foreach ((array)$header['Set-Cookie'] as $cookie) { + if (strpos($cookie, '";"') !== false) { + $cookie = str_replace('";"', "{__cookie_replace__}", $cookie); + $parts = str_replace("{__cookie_replace__}", '";"', preg_split('/\;/', $cookie)); + } else { + $parts = preg_split('/\;[ \t]*/', $cookie); + } + + list($name, $value) = explode('=', array_shift($parts), 2); + $cookies[$name] = compact('value'); + + foreach ($parts as $part) { + if (strpos($part, '=') !== false) { + list($key, $value) = explode('=', $part); + } else { + $key = $part; + $value = true; + } + + $key = strtolower($key); + if (!isset($cookies[$name][$key])) { + $cookies[$name][$key] = $value; + } + } + } + return $cookies; + } +/** + * undocumented function + * + * @param unknown $cookies + * @return void + * @access public + * @todo Refactor token escape mechanism to be configurable + */ + function buildCookies($cookies) { + $header = array(); + foreach ($cookies as $name => $cookie) { + $header[] = $name.'='.$this->escapeToken($cookie['value'], array(';')); + } + $header = $this->buildHeader(array('Cookie' => $header), 'pragmatic'); + return $header; + } +/** + * undocumented function + * + * @return void + * @access public + */ + function saveCookies() { + + } +/** + * undocumented function + * + * @return void + * @access public + */ + function loadCookies() { + + } +/** + * Unescapes a given $token according to RFC 2616 (HTTP 1.1 specs) + * + * @param string $token Token to unescape + * @return string Unescaped token + * @access protected + * @todo Test $chars parameter + */ + function unescapeToken($token, $chars = null) { + $regex = '/"(['.join('', $this->__tokenEscapeChars(true, $chars)).'])"/'; + $token = preg_replace($regex, '\\1', $token); + return $token; + } +/** + * Escapes a given $token according to RFC 2616 (HTTP 1.1 specs) + * + * @param string $token Token to escape + * @return string Escaped token + * @access protected + * @todo Test $chars parameter + */ + function escapeToken($token, $chars = null) { + $regex = '/(['.join('', $this->__tokenEscapeChars(true, $chars)).'])/'; + $token = preg_replace($regex, '"\\1"', $token); + return $token; + } +/** + * Gets escape chars according to RFC 2616 (HTTP 1.1 specs). + * + * @param boolean $hex true to get them as HEX values, false otherwise + * @return array Escape chars + * @access private + * @todo Test $chars parameter + */ + function __tokenEscapeChars($hex = true, $chars = null) { + if (!empty($chars)) { + $escape = $chars; + } else { + $escape = array('"', "(", ")", "<", ">", "@", ",", ";", ":", "\\", "/", "[", "]", "?", "=", "{", "}", " "); + for ($i = 0; $i <= 31; $i++) { + $escape[] = chr($i); + } + $escape[] = chr(127); + } + + if ($hex == false) { + return $escape; + } + $regexChars = ''; + foreach ($escape as $key => $char) { + $escape[$key] = '\\x'.str_pad(dechex(ord($char)), 2, '0', STR_PAD_LEFT); + } + return $escape; + } +/** + * Resets the state of this HttpSocket instance to it's initial state (before Object::__construct got executed) or does + * the same thing partially for the request and the response property only. + * + * @param boolean $full If set to false only HttpSocket::response and HttpSocket::request are reseted + * @return boolean True on success + * @access public + */ + function reset($full = true) { + static $initalState = array(); + if (empty($initalState)) { + $initalState = get_class_vars(__CLASS__); + } + + if ($full == false) { + $this->request = $initalState['request']; + $this->response = $initalState['response']; + return true; + } + parent::reset($initalState); + return true; + } +} +?> \ No newline at end of file diff --git a/cake/libs/i18n.php b/cake/libs/i18n.php new file mode 100755 index 0000000..6e21ca5 --- /dev/null +++ b/cake/libs/i18n.php @@ -0,0 +1,451 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Short description for file. + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v 1.2.0.4116 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Included libraries. + */ +App::import('Core', 'l10n'); +/** + * Short description for file. + * + * Long description for file + * + * @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; +/** + * Determine if $__domains cache should be wrote + * + * @var boolean + * @access private + */ + var $__cache = 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 + * @param string $category Category + * @param integer $count Count + * @return string translated strings. + * @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->locale; + + if (empty($_this->__domains)) { + $_this->__domains = Cache::read($_this->domain, '_cake_core_'); + } + + if (!isset($_this->__domains[$_this->category][$_this->__lang][$domain])) { + $_this->__bindTextDomain($domain); + $_this->__cache = true; + } + + if (!isset($count)) { + $plurals = 0; + } elseif (!empty($_this->__domains[$_this->category][$_this->__lang][$domain]["%plural-c"]) && $_this->__noLocale === false) { + $header = $_this->__domains[$_this->category][$_this->__lang][$domain]["%plural-c"]; + $plurals = $_this->__pluralGuess($header, $count); + } else { + if ($count != 1) { + $plurals = 1; + } else { + $plurals = 0; + } + } + + if (!empty($_this->__domains[$_this->category][$_this->__lang][$domain][$singular])) { + if (($trans = $_this->__domains[$_this->category][$_this->__lang][$domain][$singular]) || ($plurals) && ($trans = $_this->__domains[$_this->category][$_this->__lang][$domain][$plural])) { + if (is_array($trans)) { + if (isset($trans[$plurals])) { + $trans = $trans[$plurals]; + } + } + if (strlen($trans)) { + $singular = $trans; + return $singular; + } + } + } + + if (!empty($plurals)) { + return($plural); + } + return($singular); + } +/** + * 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 = Configure::read('localePaths'); + $plugins = Configure::listObjects('plugin'); + + if (!empty($plugins)) { + $pluginPaths = Configure::read('pluginPaths'); + + foreach ($plugins as $plugin) { + $plugin = Inflector::underscore($plugin); + if ($plugin === $domain) { + foreach ($pluginPaths as $pluginPath) { + $searchPaths[] = $pluginPath . DS . $plugin . DS . 'locale'; + } + $searchPaths = array_reverse($searchPaths); + break; + } + } + } + + foreach ($searchPaths as $directory) { + foreach ($this->l10n->languagePath as $lang) { + $file = $directory . DS . $lang . DS . $this->category . DS . $domain; + + if ($core) { + $app = $directory . DS . $lang . DS . $this->category . DS . 'core'; + if (file_exists($fn = "$app.mo")) { + $this->__loadMo($fn, $domain); + $this->__noLocale = false; + $merge[$this->category][$this->__lang][$domain] = $this->__domains[$this->category][$this->__lang][$domain]; + $core = null; + } elseif (file_exists($fn = "$app.po") && ($f = fopen($fn, "r"))) { + $this->__loadPo($f, $domain); + $this->__noLocale = false; + $merge[$this->category][$this->__lang][$domain] = $this->__domains[$this->category][$this->__lang][$domain]; + $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; + } + } + } + + if (empty($this->__domains[$this->category][$this->__lang][$domain])) { + $this->__domains[$this->category][$this->__lang][$domain] = array(); + return($domain); + } + + if ($head = $this->__domains[$this->category][$this->__lang][$domain][""]) { + foreach (explode("\n", $head) as $line) { + $header = strtok($line,":"); + $line = trim(strtok("\n")); + $this->__domains[$this->category][$this->__lang][$domain]["%po-header"][strtolower($header)] = $line; + } + + if (isset($this->__domains[$this->category][$this->__lang][$domain]["%po-header"]["plural-forms"])) { + $switch = preg_replace("/(?:[() {}\\[\\]^\\s*\\]]+)/", "", $this->__domains[$this->category][$this->__lang][$domain]["%po-header"]["plural-forms"]); + $this->__domains[$this->category][$this->__lang][$domain]["%plural-c"] = $switch; + unset($this->__domains[$this->category][$this->__lang][$domain]["%po-header"]); + } + $this->__domains = Set::pushDiff($this->__domains, $merge); + + if (isset($this->__domains[$this->category][$this->__lang][$domain][null])) { + unset($this->__domains[$this->category][$this->__lang][$domain][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[$this->category][$this->__lang][$domain][$msgid] = $msgstr; + + if (isset($msgid_plural)) { + $this->__domains[$this->category][$this->__lang][$domain][$msgid_plural] =& $this->__domains[$this->category][$this->__lang][$domain][$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, 1024)); + 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[$this->category][$this->__lang][$domain] = array_merge($merge ,$translations); + } +/** + * Object destructor + * + * Write cache file if changes have been made to the $__map or $__paths + * @access private + */ + function __destruct() { + if ($this->__cache) { + Cache::write($this->domain, array_filter($this->__domains), '_cake_core_'); + } + } +} +?> \ No newline at end of file diff --git a/cake/libs/inflector.php b/cake/libs/inflector.php new file mode 100755 index 0000000..71f07b4 --- /dev/null +++ b/cake/libs/inflector.php @@ -0,0 +1,531 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * 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://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Included libraries. + * + */ +if (!class_exists('Object')) { + uses('object'); +} +if (!class_exists('Set')) { + require LIBS . 'set.php'; +} +/** + * Pluralize and singularize English words. + * + * Inflector pluralizes and singularizes English nouns. + * Used by Cake's naming conventions throughout the framework. + * Test with $i = new Inflector(); $i->test(); + * + * @package cake + * @subpackage cake.cake.libs + * @link http://book.cakephp.org/view/491/Inflector + */ +class Inflector extends Object { +/** + * Pluralized words. + * + * @var array + * @access private + **/ + var $pluralized = array(); +/** + * List of pluralization rules in the form of pattern => replacement. + * + * @var array + * @access public + * @link http://book.cakephp.org/view/47/Custom-Inflections + **/ + var $pluralRules = array(); +/** + * Singularized words. + * + * @var array + * @access private + **/ + var $singularized = array(); +/** + * List of singularization rules in the form of pattern => replacement. + * + * @var array + * @access public + * @link http://book.cakephp.org/view/47/Custom-Inflections + **/ + var $singularRules = array(); +/** + * Plural rules from inflections.php + * + * @var array + * @access private + **/ + var $__pluralRules = array(); +/** + * Un-inflected plural rules from inflections.php + * + * @var array + * @access private + **/ + var $__uninflectedPlural = array(); +/** + * Irregular plural rules from inflections.php + * + * @var array + * @access private + **/ + var $__irregularPlural = array(); +/** + * Singular rules from inflections.php + * + * @var array + * @access private + **/ + var $__singularRules = array(); +/** + * Un-inflectd singular rules from inflections.php + * + * @var array + * @access private + **/ + var $__uninflectedSingular = array(); +/** + * Irregular singular rules from inflections.php + * + * @var array + * @access private + **/ + var $__irregularSingular = array(); +/** + * Gets a reference to the Inflector object instance + * + * @return object + * @access public + */ + function &getInstance() { + static $instance = array(); + + if (!$instance) { + $instance[0] =& new Inflector(); + if (file_exists(CONFIGS.'inflections.php')) { + include(CONFIGS.'inflections.php'); + $instance[0]->__pluralRules = $pluralRules; + $instance[0]->__uninflectedPlural = $uninflectedPlural; + $instance[0]->__irregularPlural = $irregularPlural; + $instance[0]->__singularRules = $singularRules; + $instance[0]->__uninflectedSingular = $uninflectedPlural; + $instance[0]->__irregularSingular = array_flip($irregularPlural); + } + } + return $instance[0]; + } +/** + * Initializes plural inflection rules. + * + * @return void + * @access private + */ + function __initPluralRules() { + $corePluralRules = 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$/' => 'uses', + '/(alias)$/i' => '\1es', + '/(ax|cris|test)is$/i' => '\1es', + '/s$/' => 's', + '/^$/' => '', + '/$/' => 's'); + + $coreUninflectedPlural = array( + '.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', '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', 'People', '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'); + + $coreIrregularPlural = 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', + 'numen' => 'numina', + 'occiput' => 'occiputs', + 'octopus' => 'octopuses', + 'opus' => 'opuses', + 'ox' => 'oxen', + 'penis' => 'penises', + 'person' => 'people', + 'sex' => 'sexes', + 'soliloquy' => 'soliloquies', + 'testis' => 'testes', + 'trilby' => 'trilbys', + 'turf' => 'turfs'); + + $pluralRules = Set::pushDiff($this->__pluralRules, $corePluralRules); + $uninflected = Set::pushDiff($this->__uninflectedPlural, $coreUninflectedPlural); + $irregular = Set::pushDiff($this->__irregularPlural, $coreIrregularPlural); + + $this->pluralRules = array('pluralRules' => $pluralRules, 'uninflected' => $uninflected, 'irregular' => $irregular); + $this->pluralized = array(); + } +/** + * 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/572/Class-methods + */ + function pluralize($word) { + $_this =& Inflector::getInstance(); + if (!isset($_this->pluralRules) || empty($_this->pluralRules)) { + $_this->__initPluralRules(); + } + + if (isset($_this->pluralized[$word])) { + return $_this->pluralized[$word]; + } + extract($_this->pluralRules); + + if (!isset($regexUninflected) || !isset($regexIrregular)) { + $regexUninflected = __enclose(join( '|', $uninflected)); + $regexIrregular = __enclose(join( '|', array_keys($irregular))); + $_this->pluralRules['regexUninflected'] = $regexUninflected; + $_this->pluralRules['regexIrregular'] = $regexIrregular; + } + + if (preg_match('/^(' . $regexUninflected . ')$/i', $word, $regs)) { + $_this->pluralized[$word] = $word; + return $word; + } + + if (preg_match('/(.*)\\b(' . $regexIrregular . ')$/i', $word, $regs)) { + $_this->pluralized[$word] = $regs[1] . substr($word, 0, 1) . substr($irregular[strtolower($regs[2])], 1); + return $_this->pluralized[$word]; + } + + foreach ($pluralRules as $rule => $replacement) { + if (preg_match($rule, $word)) { + $_this->pluralized[$word] = preg_replace($rule, $replacement, $word); + return $_this->pluralized[$word]; + } + } + } +/** + * Initializes singular inflection rules. + * + * @return void + * @access protected + */ + function __initSingularRules() { + $coreSingularRules = 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/' => '\1', + '/(cris|ax|test)es$/i' => '\1is', + '/(shoe)s$/i' => '\1', + '/(o)es$/i' => '\1', + '/ouses$/' => 'ouse', + '/uses$/' => 'us', + '/([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', + '/((a)naly|(b)a|(d)iagno|(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', + '/^(.*us)$/' => '\\1', + '/s$/i' => ''); + + $coreUninflectedSingular = array( + '.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', '.*ss', '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' + ); + + $coreIrregularSingular = array( + 'atlases' => 'atlas', + 'beefs' => 'beef', + 'brothers' => 'brother', + 'children' => 'child', + 'corpuses' => 'corpus', + 'cows' => 'cow', + 'ganglions' => 'ganglion', + 'genies' => 'genie', + 'genera' => 'genus', + 'graffiti' => 'graffito', + 'hoofs' => 'hoof', + 'loaves' => 'loaf', + 'men' => 'man', + 'monies' => 'money', + 'mongooses' => 'mongoose', + 'moves' => 'move', + 'mythoi' => 'mythos', + 'numina' => 'numen', + 'occiputs' => 'occiput', + 'octopuses' => 'octopus', + 'opuses' => 'opus', + 'oxen' => 'ox', + 'penises' => 'penis', + 'people' => 'person', + 'sexes' => 'sex', + 'soliloquies' => 'soliloquy', + 'testes' => 'testis', + 'trilbys' => 'trilby', + 'turfs' => 'turf', + 'waves' => 'wave' + ); + + $singularRules = Set::pushDiff($this->__singularRules, $coreSingularRules); + $uninflected = Set::pushDiff($this->__uninflectedSingular, $coreUninflectedSingular); + $irregular = Set::pushDiff($this->__irregularSingular, $coreIrregularSingular); + + $this->singularRules = array('singularRules' => $singularRules, 'uninflected' => $uninflected, 'irregular' => $irregular); + $this->singularized = array(); + } +/** + * 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/572/Class-methods + */ + function singularize($word) { + $_this =& Inflector::getInstance(); + if (!isset($_this->singularRules) || empty($_this->singularRules)) { + $_this->__initSingularRules(); + } + + if (isset($_this->singularized[$word])) { + return $_this->singularized[$word]; + } + extract($_this->singularRules); + + if (!isset($regexUninflected) || !isset($regexIrregular)) { + $regexUninflected = __enclose(join( '|', $uninflected)); + $regexIrregular = __enclose(join( '|', array_keys($irregular))); + $_this->singularRules['regexUninflected'] = $regexUninflected; + $_this->singularRules['regexIrregular'] = $regexIrregular; + } + + if (preg_match('/^(' . $regexUninflected . ')$/i', $word, $regs)) { + $_this->singularized[$word] = $word; + return $word; + } + + if (preg_match('/(.*)\\b(' . $regexIrregular . ')$/i', $word, $regs)) { + $_this->singularized[$word] = $regs[1] . substr($word, 0, 1) . substr($irregular[strtolower($regs[2])], 1); + return $_this->singularized[$word]; + } + + foreach ($singularRules 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/572/Class-methods + */ + function camelize($lowerCaseAndUnderscoredWord) { + return str_replace(" ", "", ucwords(str_replace("_", " ", $lowerCaseAndUnderscoredWord))); + } +/** + * 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/572/Class-methods + */ + function underscore($camelCasedWord) { + return strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $camelCasedWord)); + } +/** + * 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/572/Class-methods + */ + function humanize($lowerCaseAndUnderscoredWord) { + return ucwords(str_replace("_", " ", $lowerCaseAndUnderscoredWord)); + } +/** + * 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/572/Class-methods + */ + function tableize($className) { + return Inflector::pluralize(Inflector::underscore($className)); + } +/** + * 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/572/Class-methods + */ + function classify($tableName) { + return Inflector::camelize(Inflector::singularize($tableName)); + } +/** + * Returns camelBacked version of an underscored string. + * + * @param string $string + * @return string in variable form + * @access public + * @static + * @link http://book.cakephp.org/view/572/Class-methods + */ + function variable($string) { + $string = Inflector::camelize(Inflector::underscore($string)); + $replace = strtolower(substr($string, 0, 1)); + return preg_replace('/\\w/', $replace, $string, 1); + } +/** + * 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 + * @param string $replacement + * @return string + * @access public + * @static + * @link http://book.cakephp.org/view/572/Class-methods + */ + function slug($string, $replacement = '_') { + if (!class_exists('String')) { + require LIBS . 'string.php'; + } + $map = array( + '/à|á|å|â/' => 'a', + '/è|é|ê|ẽ|ë/' => 'e', + '/ì|í|î/' => 'i', + '/ò|ó|ô|ø/' => 'o', + '/ù|ú|ů|û/' => 'u', + '/ç/' => 'c', + '/ñ/' => 'n', + '/ä|æ/' => 'ae', + '/ö/' => 'oe', + '/ü/' => 'ue', + '/Ä/' => 'Ae', + '/Ü/' => 'Ue', + '/Ö/' => 'Oe', + '/ß/' => 'ss', + '/[^\w\s]/' => ' ', + '/\\s+/' => $replacement, + String::insert('/^[:replacement]+|[:replacement]+$/', array('replacement' => preg_quote($replacement, '/'))) => '', + ); + return preg_replace(array_keys($map), array_values($map), $string); + } +} +/** + * Enclose a string for preg matching. + * + * @param string $string String to enclose + * @return string Enclosed string + */ + function __enclose($string) { + return '(?:' . $string . ')'; + } +?> \ No newline at end of file diff --git a/cake/libs/l10n.php b/cake/libs/l10n.php new file mode 100755 index 0000000..95f66c2 --- /dev/null +++ b/cake/libs/l10n.php @@ -0,0 +1,466 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Short description for file. + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v 1.2.0.4116 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Short description for file. + * + * Long description for file + * + * @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'; +/** + * 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', + /* 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'), + 'ar' => array('language' => 'Arabic', 'locale' => 'ara', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-ae' => array('language' => 'Arabic (U.A.E.)', 'locale' => 'ar_ae', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-bh' => array('language' => 'Arabic (Bahrain)', 'locale' => 'ar_bh', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-dz' => array('language' => 'Arabic (Algeria)', 'locale' => 'ar_dz', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-eg' => array('language' => 'Arabic (Egypt)', 'locale' => 'ar_eg', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-iq' => array('language' => 'Arabic (Iraq)', 'locale' => 'ar_iq', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-jo' => array('language' => 'Arabic (Jordan)', 'locale' => 'ar_jo', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-kw' => array('language' => 'Arabic (Kuwait)', 'locale' => 'ar_kw', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-lb' => array('language' => 'Arabic (Lebanon)', 'locale' => 'ar_lb', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-ly' => array('language' => 'Arabic (Libya)', 'locale' => 'ar_ly', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-ma' => array('language' => 'Arabic (Morocco)', 'locale' => 'ar_ma', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-om' => array('language' => 'Arabic (Oman)', 'locale' => 'ar_om', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-qa' => array('language' => 'Arabic (Qatar)', 'locale' => 'ar_qa', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-sa' => array('language' => 'Arabic (Saudi Arabia)', 'locale' => 'ar_sa', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-sy' => array('language' => 'Arabic (Syria)', 'locale' => 'ar_sy', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-tn' => array('language' => 'Arabic (Tunisia)', 'locale' => 'ar_tn', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-ye' => array('language' => 'Arabic (Yemen)', 'locale' => 'ar_ye', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'be' => array('language' => 'Byelorussian', 'locale' => 'bel', 'localeFallback' => 'bel', 'charset' => 'utf-8'), + 'bg' => array('language' => 'Bulgarian', 'locale' => 'bul', 'localeFallback' => 'bul', 'charset' => 'utf-8'), + 'bs' => array('language' => 'Bosnian', 'locale' => 'bos', 'localeFallback' => 'bos', 'charset' => 'utf-8'), + 'ca' => array('language' => 'Catalan', 'locale' => 'cat', 'localeFallback' => 'cat', 'charset' => 'utf-8'), + 'cs' => array('language' => 'Czech', 'locale' => 'cze', 'localeFallback' => 'cze', 'charset' => 'utf-8'), + 'da' => array('language' => 'Danish', 'locale' => 'dan', 'localeFallback' => 'dan', 'charset' => 'utf-8'), + 'de' => array('language' => 'German (Standard)', 'locale' => 'deu', 'localeFallback' => 'deu', 'charset' => 'utf-8'), + 'de-at' => array('language' => 'German (Austria)', 'locale' => 'de_at', 'localeFallback' => 'deu', 'charset' => 'utf-8'), + 'de-ch' => array('language' => 'German (Swiss)', 'locale' => 'de_ch', 'localeFallback' => 'deu', 'charset' => 'utf-8'), + 'de-de' => array('language' => 'German (Germany)', 'locale' => 'de_de', 'localeFallback' => 'deu', 'charset' => 'utf-8'), + 'de-li' => array('language' => 'German (Liechtenstein)', 'locale' => 'de_li', 'localeFallback' => 'deu', 'charset' => 'utf-8'), + 'de-lu' => array('language' => 'German (Luxembourg)', 'locale' => 'de_lu', 'localeFallback' => 'deu', 'charset' => 'utf-8'), + 'e' => array('language' => 'Greek', 'locale' => 'gre', 'localeFallback' => 'gre', 'charset' => 'utf-8'), + 'el' => array('language' => 'Greek', 'locale' => 'gre', 'localeFallback' => 'gre', 'charset' => 'utf-8'), + 'en' => array('language' => 'English', 'locale' => 'eng', 'localeFallback' => 'eng', 'charset' => 'utf-8'), + 'en-au' => array('language' => 'English (Australian)', 'locale' => 'en_au', 'localeFallback' => 'eng', 'charset' => 'utf-8'), + 'en-bz' => array('language' => 'English (Belize)', 'locale' => 'en_bz', 'localeFallback' => 'eng', 'charset' => 'utf-8'), + 'en-ca' => array('language' => 'English (Canadian)', 'locale' => 'en_ca', 'localeFallback' => 'eng', 'charset' => 'utf-8'), + 'en-gb' => array('language' => 'English (British)', 'locale' => 'en_gb', 'localeFallback' => 'eng', 'charset' => 'utf-8'), + 'en-ie' => array('language' => 'English (Ireland)', 'locale' => 'en_ie', 'localeFallback' => 'eng', 'charset' => 'utf-8'), + 'en-jm' => array('language' => 'English (Jamaica)', 'locale' => 'en_jm', 'localeFallback' => 'eng', 'charset' => 'utf-8'), + 'en-nz' => array('language' => 'English (New Zealand)', 'locale' => 'en_nz', 'localeFallback' => 'eng', 'charset' => 'utf-8'), + 'en-tt' => array('language' => 'English (Trinidad)', 'locale' => 'en_tt', 'localeFallback' => 'eng', 'charset' => 'utf-8'), + 'en-us' => array('language' => 'English (United States)', 'locale' => 'en_us', 'localeFallback' => 'eng', 'charset' => 'utf-8'), + 'en-za' => array('language' => 'English (South Africa)', 'locale' => 'en_za', 'localeFallback' => 'eng', 'charset' => 'utf-8'), + 'es' => array('language' => 'Spanish (Spain - Traditional)', 'locale' => 'spa', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-ar' => array('language' => 'Spanish (Argentina)', 'locale' => 'es_ar', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-bo' => array('language' => 'Spanish (Bolivia)', 'locale' => 'es_bo', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-cl' => array('language' => 'Spanish (Chile)', 'locale' => 'es_cl', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-co' => array('language' => 'Spanish (Colombia)', 'locale' => 'es_co', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-cr' => array('language' => 'Spanish (Costa Rica)', 'locale' => 'es_cr', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-do' => array('language' => 'Spanish (Dominican Republic)', 'locale' => 'es_do', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-ec' => array('language' => 'Spanish (Ecuador)', 'locale' => 'es_ec', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-es' => array('language' => 'Spanish (Spain)', 'locale' => 'es_es', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-gt' => array('language' => 'Spanish (Guatemala)', 'locale' => 'es_gt', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-hn' => array('language' => 'Spanish (Honduras)', 'locale' => 'es_hn', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-mx' => array('language' => 'Spanish (Mexican)', 'locale' => 'es_mx', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-ni' => array('language' => 'Spanish (Nicaragua)', 'locale' => 'es_ni', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-pa' => array('language' => 'Spanish (Panama)', 'locale' => 'es_pa', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-pe' => array('language' => 'Spanish (Peru)', 'locale' => 'es_pe', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-pr' => array('language' => 'Spanish (Puerto Rico)', 'locale' => 'es_pr', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-py' => array('language' => 'Spanish (Paraguay)', 'locale' => 'es_py', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-sv' => array('language' => 'Spanish (El Salvador)', 'locale' => 'es_sv', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-uy' => array('language' => 'Spanish (Uruguay)', 'locale' => 'es_uy', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-ve' => array('language' => 'Spanish (Venezuela)', 'locale' => 'es_ve', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'et' => array('language' => 'Estonian', 'locale' => 'est', 'localeFallback' => 'est', 'charset' => 'utf-8'), + 'eu' => array('language' => 'Basque', 'locale' => 'baq', 'localeFallback' => 'baq', 'charset' => 'utf-8'), + 'fa' => array('language' => 'Farsi', 'locale' => 'per', 'localeFallback' => 'per', 'charset' => 'utf-8'), + 'fi' => array('language' => 'Finnish', 'locale' => 'fin', 'localeFallback' => 'fin', 'charset' => 'utf-8'), + 'fo' => array('language' => 'Faeroese', 'locale' => 'fao', 'localeFallback' => 'fao', 'charset' => 'utf-8'), + 'fr' => array('language' => 'French (Standard)', 'locale' => 'fre', 'localeFallback' => 'fre', 'charset' => 'utf-8'), + 'fr-be' => array('language' => 'French (Belgium)', 'locale' => 'fr_be', 'localeFallback' => 'fre', 'charset' => 'utf-8'), + 'fr-ca' => array('language' => 'French (Canadian)', 'locale' => 'fr_ca', 'localeFallback' => 'fre', 'charset' => 'utf-8'), + 'fr-ch' => array('language' => 'French (Swiss)', 'locale' => 'fr_ch', 'localeFallback' => 'fre', 'charset' => 'utf-8'), + 'fr-fr' => array('language' => 'French (France)', 'locale' => 'fr_fr', 'localeFallback' => 'fre', 'charset' => 'utf-8'), + 'fr-lu' => array('language' => 'French (Luxembourg)', 'locale' => 'fr_lu', 'localeFallback' => 'fre', 'charset' => 'utf-8'), + 'ga' => array('language' => 'Irish', 'locale' => 'gle', 'localeFallback' => 'gle', 'charset' => 'utf-8'), + 'gd' => array('language' => 'Gaelic (Scots)', 'locale' => 'gla', 'localeFallback' => 'gla', 'charset' => 'utf-8'), + 'gd-ie' => array('language' => 'Gaelic (Irish)', 'locale' => 'gd_ie', 'localeFallback' => 'gla', 'charset' => 'utf-8'), + 'gl' => array('language' => 'Galician', 'locale' => 'glg', 'localeFallback' => 'glg', 'charset' => 'utf-8'), + 'he' => array('language' => 'Hebrew', 'locale' => 'heb', 'localeFallback' => 'heb', 'charset' => 'utf-8'), + 'hi' => array('language' => 'Hindi', 'locale' => 'hin', 'localeFallback' => 'hin', 'charset' => 'utf-8'), + 'hr' => array('language' => 'Croatian', 'locale' => 'hrv', 'localeFallback' => 'hrv', 'charset' => 'utf-8'), + 'hu' => array('language' => 'Hungarian', 'locale' => 'hun', 'localeFallback' => 'hun', 'charset' => 'utf-8'), + 'hy' => array('language' => 'Armenian - Armenia', 'locale' => 'hye', 'localeFallback' => 'hye', 'charset' => 'utf-8'), + 'id' => array('language' => 'Indonesian', 'locale' => 'ind', 'localeFallback' => 'ind', 'charset' => 'utf-8'), + 'in' => array('language' => 'Indonesian', 'locale' => 'ind', 'localeFallback' => 'ind', 'charset' => 'utf-8'), + 'is' => array('language' => 'Icelandic', 'locale' => 'ice', 'localeFallback' => 'ice', 'charset' => 'utf-8'), + 'it' => array('language' => 'Italian', 'locale' => 'ita', 'localeFallback' => 'ita', 'charset' => 'utf-8'), + 'it-ch' => array('language' => 'Italian (Swiss) ', 'locale' => 'it_ch', 'localeFallback' => 'ita', 'charset' => 'utf-8'), + 'ja' => array('language' => 'Japanese', 'locale' => 'jpn', 'localeFallback' => 'jpn', 'charset' => 'utf-8'), + 'ko' => array('language' => 'Korean', 'locale' => 'kor', 'localeFallback' => 'kor', 'charset' => 'kr'), + 'ko-kp' => array('language' => 'Korea (North)', 'locale' => 'ko_kp', 'localeFallback' => 'kor', 'charset' => 'kr'), + 'ko-kr' => array('language' => 'Korea (South)', 'locale' => 'ko_kr', 'localeFallback' => 'kor', 'charset' => 'kr'), + 'koi8-r' => array('language' => 'Russian', 'locale' => 'koi8_r', 'localeFallback' => 'rus', 'charset' => 'koi8-r'), + 'lt' => array('language' => 'Lithuanian', 'locale' => 'lit', 'localeFallback' => 'lit', 'charset' => 'utf-8'), + 'lv' => array('language' => 'Latvian', 'locale' => 'lav', 'localeFallback' => 'lav', 'charset' => 'utf-8'), + 'mk' => array('language' => 'FYRO Macedonian', 'locale' => 'mk', 'localeFallback' => 'mac', 'charset' => 'utf-8'), + 'mk-mk' => array('language' => 'Macedonian', 'locale' => 'mk_mk', 'localeFallback' => 'mac', 'charset' => 'utf-8'), + 'ms' => array('language' => 'Malaysian', 'locale' => 'may', 'localeFallback' => 'may', 'charset' => 'utf-8'), + 'mt' => array('language' => 'Maltese', 'locale' => 'mlt', 'localeFallback' => 'mlt', 'charset' => 'utf-8'), + 'n' => array('language' => 'Dutch (Standard)', 'locale' => 'dut', 'localeFallback' => 'dut', 'charset' => 'utf-8'), + 'nb' => array('language' => 'Norwegian Bokmal', 'locale' => 'nob', 'localeFallback' => 'nor', 'charset' => 'utf-8'), + 'nl' => array('language' => 'Dutch (Standard)', 'locale' => 'dut', 'localeFallback' => 'dut', 'charset' => 'utf-8'), + 'nl-be' => array('language' => 'Dutch (Belgium)', 'locale' => 'nl_be', 'localeFallback' => 'dut', 'charset' => 'utf-8'), + 'nn' => array('language' => 'Norwegian Nynorsk', 'locale' => 'nno', 'localeFallback' => 'nor', 'charset' => 'utf-8'), + 'no' => array('language' => 'Norwegian', 'locale' => 'nor', 'localeFallback' => 'nor', 'charset' => 'utf-8'), + 'p' => array('language' => 'Polish', 'locale' => 'pol', 'localeFallback' => 'pol', 'charset' => 'utf-8'), + 'pl' => array('language' => 'Polish', 'locale' => 'pol', 'localeFallback' => 'pol', 'charset' => 'utf-8'), + 'pt' => array('language' => 'Portuguese (Portugal)', 'locale' => 'por', 'localeFallback' => 'por', 'charset' => 'utf-8'), + 'pt-br' => array('language' => 'Portuguese (Brazil)', 'locale' => 'pt_br', 'localeFallback' => 'por', 'charset' => 'utf-8'), + 'rm' => array('language' => 'Rhaeto-Romanic', 'locale' => 'roh', 'localeFallback' => 'roh', 'charset' => 'utf-8'), + 'ro' => array('language' => 'Romanian', 'locale' => 'rum', 'localeFallback' => 'rum', 'charset' => 'utf-8'), + 'ru' => array('language' => 'Russian', 'locale' => 'rus', 'localeFallback' => 'rus', 'charset' => 'utf-8'), + 'ro-mo' => array('language' => 'Romanian (Moldavia)', 'locale' => 'ro_mo', 'localeFallback' => 'rum', 'charset' => 'utf-8'), + 'ru-mo' => array('language' => 'Russian (Moldavia)', 'locale' => 'ru_mo', 'localeFallback' => 'rus', 'charset' => 'utf-8'), + 'sb' => array('language' => 'Sorbian', 'locale' => 'wen', 'localeFallback' => 'wen', 'charset' => 'utf-8'), + 'sk' => array('language' => 'Slovak', 'locale' => 'slo', 'localeFallback' => 'slo', 'charset' => 'utf-8'), + 'sl' => array('language' => 'Slovenian', 'locale' => 'slv', 'localeFallback' => 'slv', 'charset' => 'utf-8'), + 'sq' => array('language' => 'Albanian', 'locale' => 'alb', 'localeFallback' => 'alb', 'charset' => 'utf-8'), + 'sr' => array('language' => 'Serbian', 'locale' => 'scc', 'localeFallback' => 'scc', 'charset' => 'utf-8'), + 'sv' => array('language' => 'Swedish', 'locale' => 'swe', 'localeFallback' => 'swe', 'charset' => 'utf-8'), + 'sv-fi' => array('language' => 'Swedish (Findland)', 'locale' => 'sv_fi', 'localeFallback' => 'swe', 'charset' => 'utf-8'), + 'sx' => array('language' => 'Sutu', 'locale' => 'sx', 'localeFallback' => 'sx', 'charset' => 'utf-8'), + 'sz' => array('language' => 'Sami (Lappish)', 'locale' => 'smi', 'localeFallback' => 'smi', 'charset' => 'utf-8'), + 'th' => array('language' => 'Thai', 'locale' => 'tha', 'localeFallback' => 'tha', 'charset' => 'utf-8'), + 'tn' => array('language' => 'Tswana', 'locale' => 'tsn', 'localeFallback' => 'tsn', 'charset' => 'utf-8'), + 'tr' => array('language' => 'Turkish', 'locale' => 'tur', 'localeFallback' => 'tur', 'charset' => 'utf-8'), + 'ts' => array('language' => 'Tsonga', 'locale' => 'tso', 'localeFallback' => 'tso', 'charset' => 'utf-8'), + 'uk' => array('language' => 'Ukrainian', 'locale' => 'ukr', 'localeFallback' => 'ukr', 'charset' => 'utf-8'), + 'ur' => array('language' => 'Urdu', 'locale' => 'urd', 'localeFallback' => 'urd', 'charset' => 'utf-8'), + 've' => array('language' => 'Venda', 'locale' => 'ven', 'localeFallback' => 'ven', 'charset' => 'utf-8'), + 'vi' => array('language' => 'Vietnamese', 'locale' => 'vie', 'localeFallback' => 'vie', 'charset' => 'utf-8'), + 'xh' => array('language' => 'Xhosa', 'locale' => 'xho', 'localeFallback' => 'xho', 'charset' => 'utf-8'), + 'yi' => array('language' => 'Yiddish', 'locale' => 'yid', 'localeFallback' => 'yid', 'charset' => 'utf-8'), + 'zh' => array('language' => 'Chinese', 'locale' => 'chi', 'localeFallback' => 'chi', 'charset' => 'utf-8'), + 'zh-cn' => array('language' => 'Chinese (PRC)', 'locale' => 'zh_cn', 'localeFallback' => 'chi', 'charset' => 'GB2312'), + 'zh-hk' => array('language' => 'Chinese (Hong Kong)', 'locale' => 'zh_hk', 'localeFallback' => 'chi', 'charset' => 'utf-8'), + 'zh-sg' => array('language' => 'Chinese (Singapore)', 'locale' => 'zh_sg', 'localeFallback' => 'chi', 'charset' => 'utf-8'), + 'zh-tw' => array('language' => 'Chinese (Taiwan)', 'locale' => 'zh_tw', 'localeFallback' => 'chi', 'charset' => 'utf-8'), + 'zu' => array('language' => 'Zulu', 'locale' => 'zul', 'localeFallback' => 'zul', 'charset' => 'utf-8')); +/** + * 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 I10n::__autoLanguage(); if this fails + * the method will get the settings from I10n::__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 = 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']; + } 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 = 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 + */ + 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]; + } + return false; + } + return $this->__l10nCatalog; + } +} +?> \ No newline at end of file diff --git a/cake/libs/magic_db.php b/cake/libs/magic_db.php new file mode 100755 index 0000000..73b1601 --- /dev/null +++ b/cake/libs/magic_db.php @@ -0,0 +1,297 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * MagicDb parser and file analyzer + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v 1.2.0 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +if (!class_exists('File')) { + uses('object', 'file'); +} +/** + * 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; + } + } +} + +?> \ No newline at end of file diff --git a/cake/libs/model/app_model.php b/cake/libs/model/app_model.php new file mode 100755 index 0000000..120aa56 --- /dev/null +++ b/cake/libs/model/app_model.php @@ -0,0 +1,40 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * 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://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.model + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * 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 { +} +?> \ No newline at end of file diff --git a/cake/libs/model/behavior.php b/cake/libs/model/behavior.php new file mode 100755 index 0000000..fdbc64e --- /dev/null +++ b/cake/libs/model/behavior.php @@ -0,0 +1,494 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Model behaviors base class. + * + * Adds methods and automagic functionality to Cake Models. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.model + * @since CakePHP(tm) v 1.2.0.0 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * 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 boolean True if the operation should continue, false if it should abort + * @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 Result of the find operation + * @access public + */ + function afterFind(&$model, $results, $primary) { } +/** + * Before validate callback + * + * @param object $model Model using this behavior + * @return boolean True if validate operation should continue, false to abort + * @access public + */ + function beforeValidate(&$model) { } +/** + * Before save callback + * + * @param object $model Model using this behavior + * @return boolean True if the operation should continue, false if it should abort + * @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 boolean True if the operation should continue, false if it should abort + * @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: + array_unshift($params, $model); + 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()) { + $name = $behavior; + if (strpos($behavior, '.')) { + list($plugin, $name) = explode('.', $behavior, 2); + } + $class = $name . 'Behavior'; + + if (!App::import('Behavior', $behavior)) { + 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}); + } + } 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) { + 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("BehaviorCollection::dispatchMethod() - Method {$method} not found in any attached behavior", 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; + } + $_params = $params; + $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; + } +} +?> \ No newline at end of file diff --git a/cake/libs/model/behaviors/acl.php b/cake/libs/model/behaviors/acl.php new file mode 100755 index 0000000..0b867a2 --- /dev/null +++ b/cake/libs/model/behaviors/acl.php @@ -0,0 +1,119 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * ACL behavior class. + * + * Enables objects to easily tie into an ACL system + * + * PHP versions 4 and 5 + * + * CakePHP : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2006-2008, Cake Software Foundation, Inc. + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2006-2008, Cake Software Foundation, Inc. + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP Project + * @package cake + * @subpackage cake.cake.libs.model.behaviors + * @since CakePHP v 1.2.0.4487 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Short description for file + * + * Long description for file + * + * @package cake + * @subpackage cake.cake.libs.model.behaviors + */ +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); + + $type = $this->__typeMaps[$this->settings[$model->name]['type']]; + if (!class_exists('AclNode')) { + uses('model' . DS . 'db_acl'); + } + $model->{$type} =& ClassRegistry::init($type); + if (!method_exists($model, 'parentNode')) { + trigger_error("Callback parentNode() not defined in {$model->alias}", E_USER_WARNING); + } + } +/** + * Retrieves the Aro/Aco node for this model + * + * @param mixed $ref + * @return array + * @access public + */ + function node(&$model, $ref = null) { + $type = $this->__typeMaps[strtolower($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) { + if ($created) { + $type = $this->__typeMaps[strtolower($this->settings[$model->name]['type'])]; + $parent = $model->parentNode(); + if (!empty($parent)) { + $parent = $this->node($model, $parent); + } else { + $parent = null; + } + + $model->{$type}->create(); + $model->{$type}->save(array( + 'parent_id' => Set::extract($parent, "0.{$type}.id"), + 'model' => $model->name, + 'foreign_key' => $model->id + )); + } + } +/** + * Destroys the ARO/ACO node bound to the deleted record + * + * @return void + * @access public + */ + function afterDelete(&$model) { + $type = $this->__typeMaps[strtolower($this->settings[$model->name]['type'])]; + $node = Set::extract($this->node($model), "0.{$type}.id"); + if (!empty($node)) { + $model->{$type}->delete($node); + } + } +} + +?> \ No newline at end of file diff --git a/cake/libs/model/behaviors/containable.php b/cake/libs/model/behaviors/containable.php new file mode 100755 index 0000000..a602eff --- /dev/null +++ b/cake/libs/model/behaviors/containable.php @@ -0,0 +1,429 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * 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://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.console.libs + * @since CakePHP(tm) v 1.2.0.5669 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * 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 + */ +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) { + $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] = $instance->{$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']; + if (!empty($Model->belongsTo)) { + foreach ($Model->belongsTo as $assoc => $data) { + if (!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); + $field = $modelName . '.' . (($field === '--primaryKey--') ? $Model->$modelName->primaryKey : $field); + } + $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 + */ + 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(join('.', $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; + } +} +?> \ No newline at end of file diff --git a/cake/libs/model/behaviors/translate.php b/cake/libs/model/behaviors/translate.php new file mode 100755 index 0000000..7e959f5 --- /dev/null +++ b/cake/libs/model/behaviors/translate.php @@ -0,0 +1,512 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Short description for file. + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.model.behaviors + * @since CakePHP(tm) v 1.2.0.4525 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Short description for file. + * + * Long description for file + * + * @package cake + * @subpackage cake.cake.libs.model.behaviors + */ +class TranslateBehavior extends ModelBehavior { +/** + * Used for runtime configuration of model + */ + 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 array $config + * @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); + } +/** + * Callback + * + * @return void + * @access public + */ + function cleanup(&$model) { + $this->unbindTranslation($model); + unset($this->settings[$model->alias]); + unset($this->runtime[$model->alias]); + } +/** + * beforeFind Callback + * + * @param array $query + * @return array Modified query + * @access public + */ + function beforeFind(&$model, $query) { + $locale = $this->_getLocale($model); + if (empty($locale)) { + return $query; + } + $db =& ConnectionManager::getDataSource($model->useDbConfig); + $tablePrefix = $db->config['prefix']; + $RuntimeModel =& $this->translateModel($model); + + 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 array $results + * @param boolean $primary + * @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 + * + * @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 boolean $created + * @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 + * + * @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 + * + * @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 + * + * @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'); + } + return $this->runtime[$model->alias]['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 instance of model + * @param mixed 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)) { + 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'; + } +} +?> \ No newline at end of file diff --git a/cake/libs/model/behaviors/tree.php b/cake/libs/model/behaviors/tree.php new file mode 100755 index 0000000..da23c19 --- /dev/null +++ b/cake/libs/model/behaviors/tree.php @@ -0,0 +1,941 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Tree behavior class. + * + * Enables a model object to act as a node-based tree. + * + * PHP versions 4 and 5 + * + * CakePHP : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2006-2008, Cake Software Foundation, Inc. + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2006-2008, Cake Software Foundation, Inc. + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP Project + * @package cake + * @subpackage cake.cake.libs.model.behaviors + * @since CakePHP v 1.2.0.4487 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * 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 + */ +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]); + + if (isset($Model->data[$Model->alias][$Model->primaryKey])) { + if ($Model->data[$Model->alias][$Model->primaryKey]) { + if (!$Model->id) { + $Model->id = $Model->data[$Model->alias][$Model->primaryKey]; + } + } + unset($Model->data[$Model->alias][$Model->primaryKey]); + } + + $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 { + list($node) = array_values($Model->find('first', array( + 'conditions' => array($scope,$Model->escapeField() => $Model->id), + 'fields' => array($Model->primaryKey, $parent, $left, $right ), 'recursive' => $recursive) + )); + + $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 + */ + 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 + */ + 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($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 + */ + 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 + */ + 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->read($parent, $id); + + 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 + */ + 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 + */ + 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 + */ + 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 + */ + 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 + */ + 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); + + 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')); + } + } + } + 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 + */ + 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($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 + */ + 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 { + $parentNode = array_values($Model->find('first', array( + 'conditions' => array($scope, $Model->escapeField() => $parentId), + 'fields' => array($Model->primaryKey, $left, $right), + 'recursive' => $recursive + ))); + + 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; + } +} +?> \ No newline at end of file diff --git a/cake/libs/model/connection_manager.php b/cake/libs/model/connection_manager.php new file mode 100755 index 0000000..32f1698 --- /dev/null +++ b/cake/libs/model/connection_manager.php @@ -0,0 +1,263 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * 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://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.model + * @since CakePHP(tm) v 0.10.x.1402 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +uses ('model' . DS . 'datasources' . DS . 'datasource'); +config('database'); + +/** + * Manages loaded instances of DataSource objects + * + * Long description for file + * + * @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(); + } + } +/** + * 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; + } + + $connections = $_this->enumConnectionObjects(); + if (!empty($connections[$name])) { + $conn = $connections[$name]; + $class = $conn['classname']; + $_this->loadDataSource($name); + $_this->_dataSources[$name] =& new $class($_this->config->{$name}); + $_this->_dataSources[$name]->configKeyName = $name; + } else { + trigger_error(sprintf(__("ConnectionManager::getDataSource - Non-existent data source %s", true), $name), E_USER_ERROR); + return null; + } + + $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 + * + * @param object $source DataSource object + * @return string Datasource name + * @access public + * @static + */ + function getSourceName(&$source) { + $_this =& ConnectionManager::getInstance(); + $names = array_keys($_this->_dataSources); + for ($i = 0; $i < count($names); $i++) { + if ($_this->_dataSources[$names[$i]] === $source) { + return $names[$i]; + } + } + return null; + } +/** + * 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 { + $connections = $_this->enumConnectionObjects(); + $conn = $connections[$connName]; + } + + if (!empty($conn['parent'])) { + $_this->loadDataSource($conn['parent']); + } + + if (class_exists($conn['classname'])) { + return false; + } + + if (file_exists(MODELS . 'datasources' . DS . $conn['filename'] . '.php')) { + require (MODELS . 'datasources' . DS . $conn['filename'] . '.php'); + } elseif (fileExistsInPath(LIBS . 'model' . DS . 'datasources' . DS . $conn['filename'] . '.php')) { + require (LIBS . 'model' . DS . 'datasources' . DS . $conn['filename'] . '.php'); + } else { + $error = __('Unable to load DataSource file %s.php', true); + trigger_error(sprintf($error, $conn['filename']), E_USER_ERROR); + return null; + } + } +/** + * Gets a list of class and file names associated with the user-defined DataSource 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(); + + if (!empty($_this->_connectionsEnum)) { + return $_this->_connectionsEnum; + } + $connections = get_object_vars($_this->config); + + if ($connections != null) { + foreach ($connections as $name => $config) { + $_this->_connectionsEnum[$name] = $_this->__getDriver($config); + } + return $_this->_connectionsEnum; + } else { + $_this->cakeError('missingConnection', array(array('className' => 'ConnectionManager'))); + } + } +/** + * 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->__getDriver($config); + $return =& $_this->getDataSource($name); + return $return; + } +/** + * Returns the file, class name, and parent for the given driver. + * + * @return array An indexed array with: filename, classname, and parent + * @access private + */ + function __getDriver($config) { + if (!isset($config['datasource'])) { + $config['datasource'] = 'dbo'; + } + + if (isset($config['driver']) && $config['driver'] != null && !empty($config['driver'])) { + $filename = $config['datasource'] . DS . $config['datasource'] . '_' . $config['driver']; + $classname = Inflector::camelize(strtolower($config['datasource'] . '_' . $config['driver'])); + $parent = $this->__getDriver(array('datasource' => $config['datasource'])); + } else { + $filename = $config['datasource'] . '_source'; + $classname = Inflector::camelize(strtolower($config['datasource'] . '_source')); + $parent = null; + } + return array('filename' => $filename, 'classname' => $classname, 'parent' => $parent); + } +/** + * Destructor. + * + * @access private + */ + function __destruct() { + if (Configure::read('Session.save') == 'database' && function_exists('session_write_close')) { + session_write_close(); + } + } +} +?> diff --git a/cake/libs/model/datasources/datasource.php b/cake/libs/model/datasources/datasource.php new file mode 100755 index 0000000..b1b2bae --- /dev/null +++ b/cake/libs/model/datasources/datasource.php @@ -0,0 +1,510 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * DataSource base class + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.model.datasources + * @since CakePHP(tm) v 0.10.5.1790 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * DataSource base class + * + * Long description for file + * + * @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 + */ + var $startQuote = null; +/** + * The ending character that this DataSource uses for quoted identifiers. + * + * @var string + */ + var $endQuote = null; +/** + * Enter description here... + * + * @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, to prevent query log taking over + * too much memory on large amounts of queries -- I we've had problems at + * >6000 queries on one system. + * + * @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 + */ + var $cacheSources = true; +/** + * Constructor. + */ + function __construct($config = array()) { + parent::__construct(); + $this->setConfig($config); + } +/** + * Caches/returns cached results for child instances + * + * @return array + */ + 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. + * + * @return array + */ + 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 mixed + */ + function describe($model) { + if ($this->cacheSources === false) { + return null; + } + $table = $this->fullTableName($model, false); + 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 + */ + function begin(&$model) { + return !$this->_transactionStarted; + } +/** + * Commit a transaction + * + * @return boolean Returns true if a transaction is in progress + */ + function commit(&$model) { + return $this->_transactionStarted; + } +/** + * Rollback a transaction + * + * @return boolean Returns true if a transaction is in progress + */ + 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") + */ + function column($real) { + return false; + } +/** + * To-be-overridden in subclasses. + * + * @param unknown_type $model + * @param unknown_type $fields + * @param unknown_type $values + * @return unknown + */ + function create(&$model, $fields = null, $values = null) { + return false; + } +/** + * To-be-overridden in subclasses. + * + * @param unknown_type $model + * @param unknown_type $queryData + * @return unknown + */ + function read(&$model, $queryData = array()) { + return false; + } +/** + * To-be-overridden in subclasses. + * + * @param unknown_type $model + * @param unknown_type $fields + * @param unknown_type $values + * @return unknown + */ + function update(&$model, $fields = null, $values = null) { + return false; + } +/** + * To-be-overridden in subclasses. + * + * @param unknown_type $model + * @param unknown_type $id + */ + 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 in + */ + function lastInsertId($source = null) { + return false; + } +/** + * Returns the ID generated from the previous INSERT operation. + * + * @param unknown_type $source + * @return in + */ + function lastNumRows($source = null) { + return false; + } +/** + * Returns the ID generated from the previous INSERT operation. + * + * @param unknown_type $source + * @return in + */ + function lastAffected($source = null) { + return false; + } +/** + * Returns true if the DataSource supports the given interface (method) + * + * @param string $interface The name of the interface (method) + * @return boolean True on success + */ + function isInterfaceSupported($interface) { + $methods = get_class_methods(get_class($this)); + $methods = strtolower(implode('|', $methods)); + $methods = explode('|', $methods); + $return = in_array(strtolower($interface), $methods); + return $return; + } +/** + * Sets the configuration for the DataSource + * + * @param array $config The configuration array + * @return void + */ + 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 + */ + 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; + } +/** + * Enter description here... + * + * @param unknown_type $query + * @param unknown_type $data + * @param unknown_type $association + * @param unknown_type $assocData + * @param Model $model + * @param Model $linkModel + * @param array $stack + * @return unknown + */ + 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 unknown_type $model + * @param unknown_type $key + * @return unknown + */ + function resolveKey($model, $key) { + return $model->alias . $key; + } +/** + * Closes the current datasource. + * + */ + function __destruct() { + if ($this->_transactionStarted) { + $null = null; + $this->rollback($null); + } + if ($this->connected) { + $this->close(); + } + } +} +?> \ No newline at end of file diff --git a/cake/libs/model/datasources/dbo/dbo_adodb.php b/cake/libs/model/datasources/dbo/dbo_adodb.php new file mode 100755 index 0000000..53d6f96 --- /dev/null +++ b/cake/libs/model/datasources/dbo/dbo_adodb.php @@ -0,0 +1,516 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * AdoDB layer for DBO. + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.model.datasources.dbo + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Include AdoDB files. + */ +App::import('Vendor', 'NewADOConnection', array('file' => 'adodb' . DS . 'adodb.inc.php')); +/** + * AdoDB DBO implementation. + * + * Database abstraction implementation for the AdoDB library. + * + * @package cake + * @subpackage cake.cake.libs.model.datasources.dbo + */ +class DboAdodb extends DboSource { +/** + * Enter description here... + * + * @var string + */ + var $description = "ADOdb DBO Driver"; +/** + * ADOConnection object with which we connect. + * + * @var ADOConnection The connection object. + * @access private + */ + var $_adodb = null; +/** + * Array translating ADOdb column MetaTypes to cake-supported metatypes + * + * @var array + * @access private + */ + var $_adodbColumnTypes = array( + 'string' => 'C', + 'text' => 'X', + 'date' => 'D', + 'timestamp' => 'T', + 'time' => 'T', + 'datetime' => 'T', + 'boolean' => 'L', + 'float' => 'N', + 'integer' => 'I', + 'binary' => 'R', + ); +/** + * ADOdb column definition + * + * @var array + */ + var $columns = array( + 'primary_key' => array('name' => 'R', 'limit' => 11), + 'string' => array('name' => 'C', 'limit' => '255'), + 'text' => array('name' => 'X'), + 'integer' => array('name' => 'I', 'limit' => '11', 'formatter' => 'intval'), + 'float' => array('name' => 'N', 'formatter' => 'floatval'), + 'timestamp' => array('name' => 'T', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'), + 'time' => array('name' => 'T', 'format' => 'H:i:s', 'formatter' => 'date'), + 'datetime' => array('name' => 'T', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'), + 'date' => array('name' => 'D', 'format' => 'Y-m-d', 'formatter' => 'date'), + 'binary' => array('name' => 'B'), + 'boolean' => array('name' => 'L', 'limit' => '1') + ); +/** + * Connects to the database using options in the given configuration array. + * + * @param array $config Configuration array for connecting + */ + function connect() { + $config = $this->config; + $persistent = strrpos($config['connect'], '|p'); + + if ($persistent === false) { + $adodb_driver = $config['connect']; + $connect = 'Connect'; + } else { + $adodb_driver = substr($config['connect'], 0, $persistent); + $connect = 'PConnect'; + } + + $this->_adodb = NewADOConnection($adodb_driver); + + $this->_adodbDataDict = NewDataDictionary($this->_adodb, $adodb_driver); + + $this->startQuote = $this->_adodb->nameQuote; + $this->endQuote = $this->_adodb->nameQuote; + + $this->connected = $this->_adodb->$connect($config['host'], $config['login'], $config['password'], $config['database']); + $this->_adodbMetatyper = &$this->_adodb->execute('Select 1'); + return $this->connected; + } +/** + * Disconnects from database. + * + * @return boolean True if the database could be disconnected, else false + */ + function disconnect() { + return $this->_adodb->Close(); + } +/** + * Executes given SQL statement. + * + * @param string $sql SQL statement + * @return resource Result resource identifier + */ + function _execute($sql) { + global $ADODB_FETCH_MODE; + $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; + return $this->_adodb->execute($sql); + } +/** + * Returns a row from current resultset as an array . + * + * @return array The fetched row as an array + */ + function fetchRow($sql = null) { + if (!empty($sql) && is_string($sql) && strlen($sql) > 5) { + if (!$this->execute($sql)) { + return null; + } + } + + if (!$this->hasResult()) { + return null; + } else { + $resultRow = $this->_result->FetchRow(); + $this->resultSet($resultRow); + return $this->fetchResult(); + } + } +/** + * Begin a transaction + * + * @param unknown_type $model + * @return boolean True on success, false on fail + * (i.e. if the database/model does not support transactions). + */ + function begin(&$model) { + if (parent::begin($model)) { + if ($this->_adodb->BeginTrans()) { + $this->_transactionStarted = true; + return true; + } + } + return false; + } +/** + * Commit a transaction + * + * @param unknown_type $model + * @return boolean True on success, false on fail + * (i.e. if the database/model does not support transactions, + * or a transaction has not started). + */ + function commit(&$model) { + if (parent::commit($model)) { + $this->_transactionStarted = false; + return $this->_adodb->CommitTrans(); + } + return false; + } +/** + * Rollback a transaction + * + * @param unknown_type $model + * @return boolean True on success, false on fail + * (i.e. if the database/model does not support transactions, + * or a transaction has not started). + */ + function rollback(&$model) { + if (parent::rollback($model)) { + return $this->_adodb->RollbackTrans(); + } + return false; + } +/** + * 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() { + $tables = $this->_adodb->MetaTables('TABLES'); + + if (!sizeof($tables) > 0) { + trigger_error(ERROR_NO_TABLE_LIST, E_USER_NOTICE); + exit; + } + return $tables; + } +/** + * Returns an array of the fields in the table used by the given model. + * + * @param AppModel $model Model object + * @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->_adodb->MetaColumns($this->fullTableName($model, false)); + + foreach ($cols as $column) { + $fields[$column->name] = array( + 'type' => $this->column($column->type), + 'null' => !$column->not_null, + 'length' => $column->max_length, + ); + if ($column->has_default) { + $fields[$column->name]['default'] = $column->default_value; + } + if ($column->primary_key == 1) { + $fields[$column->name]['key'] = 'primary'; + } + } + + $this->__cacheDescription($this->fullTableName($model, false), $fields); + return $fields; + } +/** + * Returns a formatted error message from previous database operation. + * + * @return string Error message + */ + function lastError() { + return $this->_adodb->ErrorMsg(); + } +/** + * Returns number of affected rows in previous database operation, or false if no previous operation exists. + * + * @return integer Number of affected rows + */ + function lastAffected() { + return $this->_adodb->Affected_Rows(); + } +/** + * Returns number of rows in previous resultset, or false if no previous resultset exists. + * + * @return integer Number of rows in resultset + */ + function lastNumRows() { + return $this->_result ? $this->_result->RecordCount() : false; + } +/** + * Returns the ID generated from the previous INSERT operation. + * + * @return int + * + * @Returns the last autonumbering ID inserted. Returns false if function not supported. + */ + function lastInsertId() { + return $this->_adodb->Insert_ID(); + } +/** + * 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 + * @todo Please change output string to whatever select your database accepts. adodb doesn't allow us to get the correct limit string out of it. + */ + function limit($limit, $offset = null) { + if ($limit) { + $rt = ''; + if (!strpos(strtolower($limit), 'limit') || strpos(strtolower($limit), 'limit') === 0) { + $rt = ' LIMIT'; + } + + if ($offset) { + $rt .= ' ' . $offset . ','; + } + + $rt .= ' ' . $limit; + return $rt; + } + return null; + // please change to whatever select your database accepts + // adodb doesn't allow us to get the correct limit string out of it + } +/** + * 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) { + $metaTypes = array_flip($this->_adodbColumnTypes); + + $interpreted_type = $this->_adodbMetatyper->MetaType($real); + + if (!isset($metaTypes[$interpreted_type])) { + return 'text'; + } + return $metaTypes[$interpreted_type]; + } +/** + * 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_type The type of 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 ($data === '') { + return "''"; + } + return $this->_adodb->qstr($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 && $fields[0] != '*' && strpos($fields[0], 'COUNT(*)') === false) { + for ($i = 0; $i < $count; $i++) { + if (!preg_match('/^.+\\(.*\\)/', $fields[$i]) && !preg_match('/\s+AS\s+/', $fields[$i])) { + $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]); + } + } + } + } + return $fields; + } +/** + * Build ResultSets and map data + * + * @param array $results + */ + function resultSet(&$results) { + $num_fields = count($results); + $fields = array_keys($results); + $this->results =& $results; + $this->map = array(); + $index = 0; + $j = 0; + + while ($j < $num_fields) { + $columnName = $fields[$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 (!empty($this->results)) { + $row = $this->results; + $this->results = null; + } else { + $row = $this->_result->FetchRow(); + } + + if (empty($row)) { + return false; + } + + $resultRow = array(); + $fields = array_keys($row); + $count = count($fields); + $i = 0; + for ($i = 0; $i < $count; $i++) { //$row as $index => $field) { + list($table, $column) = $this->map[$i]; + $resultRow[$table][$column] = $row[$fields[$i]]; + } + return $resultRow; + } +/** + * 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; + extract(array_merge(array('null' => true), $column)); + + if (empty($name) || empty($type)) { + trigger_error('Column name or type not defined in schema', E_USER_WARNING); + return null; + } + + //$metaTypes = array_flip($this->_adodbColumnTypes); + if (!isset($this->_adodbColumnTypes[$type])) { + trigger_error("Column type {$type} does not exist", E_USER_WARNING); + return null; + } + $metaType = $this->_adodbColumnTypes[$type]; + $concreteType = $this->_adodbDataDict->ActualType($metaType); + $real = $this->columns[$type]; + + //UUIDs are broken so fix them. + if ($type == 'string' && isset($real['length']) && $real['length'] == 36) { + $concreteType = 'CHAR'; + } + + $out = $this->name($name) . ' ' . $concreteType; + + if (isset($real['limit']) || isset($real['length']) || isset($column['limit']) || isset($column['length'])) { + if (isset($column['length'])) { + $length = $column['length']; + } elseif (isset($column['limit'])) { + $length = $column['limit']; + } elseif (isset($real['length'])) { + $length = $real['length']; + } else { + $length = $real['limit']; + } + $out .= '(' . $length . ')'; + } + $_notNull = $_default = $_autoInc = $_constraint = $_unsigned = false; + + if (isset($column['key']) && $column['key'] == 'primary' && $type == 'integer') { + $_constraint = ''; + $_autoInc = true; + } elseif (isset($column['key']) && $column['key'] == 'primary') { + $_notNull = ''; + } elseif (isset($column['default']) && isset($column['null']) && $column['null'] == false) { + $_notNull = true; + $_default = $column['default']; + } elseif ( isset($column['null']) && $column['null'] == true) { + $_notNull = false; + $_default = 'NULL'; + } + if (isset($column['default']) && $_default == false) { + $_default = $this->value($column['default']); + } + if (isset($column['null']) && $column['null'] == false) { + $_notNull = true; + } + //use concrete instance of DataDict to make the suffixes for us. + $out .= $this->_adodbDataDict->_CreateSuffix($out, $metaType, $_notNull, $_default, $_autoInc, $_constraint, $_unsigned); + return $out; + + } +/** + * Checks if the result is valid + * + * @return boolean True if the result is valid, else false + */ + function hasResult() { + return is_object($this->_result) && !$this->_result->EOF; + } +} +?> \ No newline at end of file diff --git a/cake/libs/model/datasources/dbo/dbo_db2.php b/cake/libs/model/datasources/dbo/dbo_db2.php new file mode 100755 index 0000000..4e65d3b --- /dev/null +++ b/cake/libs/model/datasources/dbo/dbo_db2.php @@ -0,0 +1,564 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * IBM DB2 for DBO + * + * This file supports IBM DB2 and Cloudscape (aka Apache Derby, + * Sun Java DB) using the native ibm_db2 extension: + * http://pecl.php.net/package/ibm_db2 + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2007, Cake Software Foundation, Inc. + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.model.datasources.dbo + * @since CakePHP(tm) v 0.10.5.1790 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * IBM DB2 for DBO + * + * This file supports IBM DB2 and Cloudscape (aka Apache Derby, + * Sun Java DB) using the native ibm_db2 extension: + * http://pecl.php.net/package/ibm_db2 + * + * @package cake + * @subpackage cake.cake.libs.model.datasources.dbo + */ +class DboDb2 extends DboSource { +/** + * A short description of the type of driver. + * + * @var string + */ + var $description = 'IBM DB2 DBO Driver'; +/** + * The start quote in which database column and table names should be wrapped. + * + * @var string + */ + var $startQuote = ''; +/** + * The end quote in which database column and table names should be wrapped. + * + * @var string + */ + var $endQuote = ''; +/** + * An array of base configuration settings to be used if settings are not + * provided, i.e. default host, port, and connection method. + * + * @var array + */ + var $_baseConfig = array( + 'persistent' => true, + 'login' => 'db2inst1', + 'password' => '', + 'database' => 'cake', + 'schema' => '', + 'hostname' => '127.0.0.1', + 'port' => '50001', + 'encoding' => 'UTF-8', + 'cataloged' => true, + 'autocommit' => true + ); +/** + * An array that maps Cake column types to database native column types. + * The mapped information can include a reference to a function that should + * be used to format the data, as well as a string that defines the + * formatting according to that function. + * + * @var array + */ + var $columns = array( + 'primary_key' => array('name' => 'not null generated by default as identity (start with 1, increment by 1)'), + 'string' => array('name' => 'varchar', 'limit' => '255'), + 'text' => array('name' => 'clob'), + 'integer' => array('name' => 'integer', 'limit' => '10', 'formatter' => 'intval'), + 'float' => array('name' => 'double', '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' => 'blob'), + 'boolean' => array('name' => 'smallint', 'limit' => '1') + ); +/** + * A map for every result mapping tables to columns + * + * @var array result -> ( table -> column ) + */ + var $_resultMap = array(); +/** + * 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; + $connect = 'db2_connect'; + if ($config['persistent']) { + $connect = 'db2_pconnect'; + } + $this->connected = false; + + if ($config['cataloged']) { + $this->connection = $connect($config['database'], $config['login'], $config['password']); + } else { + $connString = sprintf( + "DRIVER={IBM DB2 ODBC DRIVER};DATABASE=%s;HOSTNAME=%s;PORT=%d;PROTOCOL=TCPIP;UID=%s;PWD=%s;", + $config['database'], + $config['hostname'], + $config['port'], + $config['login'], + $config['password'] + ); + $this->connection = db2_connect($connString, '', ''); + } + + if ($this->connection) { + $this->connected = true; + } + + if ($config['schema'] !== '') { + $this->_execute('SET CURRENT SCHEMA = ' . $config['schema']); + } + return $this->connected; + } +/** + * Disconnects from database. + * + * @return boolean True if the database could be disconnected, else false + */ + function disconnect() { + @db2_free_result($this->results); + $this->connected = !@db2_close($this->connection); + return !$this->connected; + } +/** + * Executes given SQL statement. We should use prepare / execute to allow the + * database server to reuse its access plan and increase the efficiency + * of your database access + * + * @param string $sql SQL statement + * @return resource Result resource identifier + * @access protected + */ + function _execute($sql) { + // get result from db + $result = db2_exec($this->connection, $sql); + + if (!is_bool($result)) { + // build table/column map for this result + $map = array(); + $numFields = db2_num_fields($result); + $index = 0; + $j = 0; + $offset = 0; + + while ($j < $numFields) { + $columnName = strtolower(db2_field_name($result, $j)); + $tmp = strpos($sql, '.' . $columnName, $offset); + $tableName = substr($sql, $offset, ($tmp-$offset)); + $tableName = substr($tableName, strrpos($tableName, ' ') + 1); + $map[$index++] = array($tableName, $columnName); + $j++; + $offset = strpos($sql, ' ', $tmp); + } + + $this->_resultMap[$result] = $map; + } + + return $result; + } +/** + * Returns an array of all the tables in the database. + * Should call parent::listSources twice in the method: + * once to see if the list is cached, and once to cache + * the list if not. + * + * @return array Array of tablenames in the database + */ + function listSources() { + $cache = parent::listSources(); + + if ($cache != null) { + return $cache; + } + $result = db2_tables($this->connection); + $tables = array(); + + while (db2_fetch_row($result)) { + $tables[] = strtolower(db2_result($result, '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; + } + $fields = array(); + $result = db2_columns($this->connection, '', '', strtoupper($this->fullTableName($model))); + + while (db2_fetch_row($result)) { + $fields[strtolower(db2_result($result, 'COLUMN_NAME'))] = array( + 'type' => $this->column(strtolower(db2_result($result, 'TYPE_NAME'))), + 'null' => db2_result($result, 'NULLABLE'), + 'default' => db2_result($result, 'COLUMN_DEF'), + 'length' => db2_result($result, 'COLUMN_SIZE') + ); + } + $this->__cacheDescription($model->tablePrefix . $model->table, $fields); + return $fields; + } +/** + * Returns a quoted name of $data for use in an SQL statement. + * + * @param string $data Name (table.field) to be prepared for use in an SQL statement + * @return string Quoted for MySQL + */ + function name($data) { + return $data; + } +/** + * 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 + * @return string Quoted and escaped + * @todo Add logic that formats/escapes data based on column type + */ + 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 === '') { + return "''"; + } + + switch ($column) { + case 'boolean': + $data = $this->boolean((bool)$data); + break; + case 'integer': + $data = intval($data); + break; + default: + $data = str_replace("'", "''", $data); + break; + } + + if ($column == 'integer' || $column == 'float') { + return $data; + } + return "'" . $data . "'"; + } +/** + * Not sure about this one, MySQL needs it but does ODBC? Safer just to leave it + * Translates between PHP boolean values and MySQL (faked) boolean values + * + * @param mixed $data Value to be translated + * @return mixed Converted boolean value + */ + function boolean($data) { + if ($data === true || $data === false) { + if ($data === true) { + return 1; + } + return 0; + } else { + if (intval($data !== 0)) { + return true; + } + return false; + } + } +/** + * Begins a transaction. Returns true if the transaction was + * started successfully, otherwise false. + * + * @param unknown_type $model + * @return boolean True on success, false on fail + * (i.e. if the database/model does not support transactions). + */ + function begin(&$model) { + if (parent::begin($model)) { + if (db2_autocommit($this->connection, DB2_AUTOCOMMIT_OFF)) { + $this->_transactionStarted = true; + return true; + } + } + return false; + } +/** + * Commit a transaction + * + * @param unknown_type $model + * @return boolean True on success, false on fail + * (i.e. if the database/model does not support transactions, + * or a transaction has not started). + */ + function commit(&$model) { + if (parent::commit($model)) { + if (db2_commit($this->connection)) { + $this->_transactionStarted = false; + db2_autocommit($this->connection, DB2_AUTOCOMMIT_ON); + return true; + } + } + return false; + } +/** + * Rollback a transaction + * + * @param unknown_type $model + * @return boolean True on success, false on fail + * (i.e. if the database/model does not support transactions, + * or a transaction has not started). + */ + function rollback(&$model) { + if (parent::rollback($model)) { + $this->_transactionStarted = false; + db2_autocommit($this->connection, DB2_AUTOCOMMIT_ON); + return db2_rollback($this->connection); + } + return false; + } +/** + * Removes Identity (primary key) column from update data before returning to parent + * + * @param Model $model + * @param array $fields + * @param array $values + * @return array + */ + function update(&$model, $fields = array(), $values = array()) { + foreach ($fields as $i => $field) { + if ($field == $model->primaryKey) { + unset ($fields[$i]); + unset ($values[$i]); + break; + } + } + return parent::update($model, $fields, $values); + } +/** + * Returns a formatted error message from previous database operation. + * DB2 distinguishes between statement and connnection errors so we + * must check for both. + * + * @return string Error message with error number + */ + function lastError() { + if (db2_stmt_error()) { + return db2_stmt_error() . ': ' . db2_stmt_errormsg(); + } elseif (db2_conn_error()) { + return db2_conn_error() . ': ' . db2_conn_errormsg(); + } + 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 db2_num_rows($this->_result); + } + 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 db2_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) { + $data = $this->fetchRow(sprintf('SELECT SYSIBM.IDENTITY_VAL_LOCAL() AS ID FROM %s FETCH FIRST ROW ONLY', $source)); + + if ($data && isset($data[0]['id'])) { + return $data[0]['id']; + } + return null; + } +/** + * 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 limit is not in the passed value already, add a limit clause. + if (!strpos(strtolower($limit), 'limit') || strpos(strtolower($limit), 'limit') === 0) { + $rt = sprintf('FETCH FIRST %d ROWS ONLY', $limit); + } + + // TODO: Implement paging with the offset. This could get hairy. + /* + WITH WHOLE AS + (SELECT FIRSTNME, MIDINIT, LASTNAME, SALARY, + ROW_NUMBER() OVER (ORDER BY SALARY DESC) AS RN + FROM EMPLOYEE) + SELECT FIRSTNME, MIDINIT, LASTNAME, SALARY, RN + FROM WHOLE + WHERE RN BETWEEN 10 AND 15 + */ + + /* + if ($offset) { + $rt .= ' ' . $offset . ','; + } + + $rt .= ' ' . $limit; + */ + + 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 == 'smallint') { + return 'boolean'; + } + + if (strpos($col, 'char') !== false) { + return 'string'; + } + + if (strpos($col, 'clob') !== false) { + return 'text'; + } + + if (strpos($col, 'blob') !== false || $col == 'image') { + return 'binary'; + } + + if (in_array($col, array('double', 'real', 'decimal'))) { + return 'float'; + } + return 'text'; + } +/** + * Maps a result set to an array so that returned fields are + * grouped by model. Any calculated fields, or fields that + * do not correspond to a particular model belong under array + * key 0. + * + * 1. Gets the column headers + * {{{ + * Post.id + * Post.title + * + * [0] => Array + * ( + * [0] => Post + * [1] => id + * ) + * + * [1] => Array + * ( + * [0] => Post + * [1] => title + * ) + * }}} + * @param unknown_type $results + */ + function resultSet(&$results, $sql = null) { + $this->results =& $results; + $this->map = $this->_resultMap[$this->results]; + } +/** + * Fetches the next row from the current result set + * Maps the records in the $result property to the map + * created in resultSet(). + * + * 2. Gets the actual values. + * + * @return unknown + */ + function fetchResult() { + if ($row = db2_fetch_array($this->results)) { + $resultRow = array(); + $i = 0; + + foreach ($row as $index => $field) { + $table = $this->map[$index][0]; + $column = strtolower($this->map[$index][1]); + $resultRow[$table][$column] = $row[$index]; + $i++; + } + return $resultRow; + } + return false; + } +} +?> \ No newline at end of file diff --git a/cake/libs/model/datasources/dbo/dbo_firebird.php b/cake/libs/model/datasources/dbo/dbo_firebird.php new file mode 100755 index 0000000..d02c45b --- /dev/null +++ b/cake/libs/model/datasources/dbo/dbo_firebird.php @@ -0,0 +1,513 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Firebird/Interbase layer for DBO + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.model.dbo + * @since CakePHP(tm) v 1.2.0.5152 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Short description for class. + * + * Long description for class + * + * @package cake + * @subpackage cake.cake.libs.model.dbo + */ +class DboFirebird extends DboSource { +/** + * Enter description here... + * + * @var unknown_type + */ + var $description = "Firebird/Interbase DBO Driver"; +/** + * Saves the original table name + * + * @var unknown_type + */ + var $modeltmp = array(); +/** + * Enter description here... + * + * @var unknown_type + */ + var $startQuote = "\'"; +/** + * Enter description here... + * + * @var unknown_type + */ + var $endQuote = "\'"; +/** + * Enter description here... + * + * @var unknown_type + */ + var $alias = ' '; +/** + * Enter description here... + * + * @var unknown_type + */ + var $goofyLimit = true; +/** + * Creates a map between field aliases and numeric indexes. + * + * @var array + */ + var $__fieldMappings = array(); +/** + * Base configuration settings for Firebird driver + * + * @var array + */ + var $_baseConfig = array( + 'persistent' => true, + 'host' => 'localhost', + 'login' => 'SYSDBA', + 'password' => 'masterkey', + 'database' => 'c:\\CAKE.FDB', + 'port' => '3050', + 'connect' => 'ibase_connect' + ); +/** + * Firebird 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' => 'BLOB SUB_TYPE 1 SEGMENT SIZE 100 CHARACTER SET NONE'), + 'integer' => array('name' => 'integer'), + 'float' => array('name' => 'float', 'formatter' => 'floatval'), + 'datetime' => array('name' => 'timestamp', 'format' => 'd.m.Y H:i:s', 'formatter' => 'date'), + 'timestamp' => array('name' => 'timestamp', 'format' => 'd.m.Y H:i:s', 'formatter' => 'date'), + 'time' => array('name' => 'time', 'format' => 'H:i:s', 'formatter' => 'date'), + 'date' => array('name' => 'date', 'format' => 'd.m.Y', 'formatter' => 'date'), + 'binary' => array('name' => 'blob'), + 'boolean' => array('name' => 'smallint') + ); +/** + * Firebird Transaction commands. + * + * @var array + **/ + var $_commands = array( + 'begin' => 'SET TRANSACTION', + 'commit' => 'COMMIT', + 'rollback' => 'ROLLBACK' + ); +/** + * 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; + $connect = $config['connect']; + + $this->connected = false; + $this->connection = $connect($config['host'] . ':' . $config['database'], $config['login'], $config['password']); + $this->connected = true; + } +/** + * Disconnects from database. + * + * @return boolean True if the database could be disconnected, else false + */ + function disconnect() { + $this->connected = false; + return @ibase_close($this->connection); + } +/** + * Executes given SQL statement. + * + * @param string $sql SQL statement + * @return resource Result resource identifier + * @access protected + */ + function _execute($sql) { + return @ibase_query($this->connection, $sql); + } +/** + * Returns a row from given resultset as an array . + * + * @return array The fetched row as an array + */ + function fetchRow() { + if ($this->hasResult()) { + $this->resultSet($this->_result); + $resultRow = $this->fetchResult(); + return $resultRow; + } else { + return null; + } + } +/** + * 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; + } + $sql = "select RDB" . "$" . "RELATION_NAME as name + FROM RDB" ."$" . "RELATIONS + Where RDB" . "$" . "SYSTEM_FLAG =0"; + + $result = @ibase_query($this->connection,$sql); + $tables = array(); + while ($row = ibase_fetch_row ($result)) { + $tables[] = strtolower(trim($row[0])); + } + 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) { + $this->modeltmp[$model->table] = $model->alias; + $cache = parent::describe($model); + + if ($cache != null) { + return $cache; + } + $fields = false; + $sql = "SELECT * FROM " . $this->fullTableName($model, false); + $rs = ibase_query($sql); + $coln = ibase_num_fields($rs); + $fields = false; + + for ($i = 0; $i < $coln; $i++) { + $col_info = ibase_field_info($rs, $i); + $fields[strtolower($col_info['name'])] = array( + 'type' => $this->column($col_info['type']), + 'null' => '', + 'length' => $col_info['length'] + ); + } + $this->__cacheDescription($this->fullTableName($model, false), $fields); + return $fields; + } +/** + * Returns a quoted name of $data for use in an SQL statement. + * + * @param string $data Name (table.field) to be prepared for use in an SQL statement + * @return string Quoted for Firebird + */ + function name($data) { + if ($data == '*') { + return '*'; + } + $pos = strpos($data, '"'); + + if ($pos === false) { + if (!strpos($data, ".")) { + $data = '"' . strtoupper($data) . '"'; + } else { + $build = explode('.', $data); + $data = '"' . strtoupper($build[0]) . '"."' . strtoupper($build[1]) . '"'; + } + } + return $data; + } +/** + * 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 ($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; + } + return "'" . $data . "'"; + } +/** + * Removes Identity (primary key) column from update data before returning to parent + * + * @param Model $model + * @param array $fields + * @param array $values + * @return array + */ + function update(&$model, $fields = array(), $values = array()) { + foreach ($fields as $i => $field) { + if ($field == $model->primaryKey) { + unset ($fields[$i]); + unset ($values[$i]); + break; + } + } + return parent::update($model, $fields, $values); + } +/** + * Returns a formatted error message from previous database operation. + * + * @return string Error message with error number + */ + function lastError() { + $error = ibase_errmsg(); + + if ($error !== false) { + 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 ibase_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() { + return $this->_result? /*ibase_affected_rows($this->_result)*/ 1: false; + } +/** + * Returns the ID generated from the previous INSERT operation. + * + * @param unknown_type $source + * @return in + */ + function lastInsertId($source = null, $field = 'id') { + $query = "SELECT RDB\$TRIGGER_SOURCE + FROM RDB\$TRIGGERS WHERE RDB\$RELATION_NAME = '". strtoupper($source) . "' AND + RDB\$SYSTEM_FLAG IS NULL AND RDB\$TRIGGER_TYPE = 1 "; + + $result = @ibase_query($this->connection,$query); + $generator = ""; + + while ($row = ibase_fetch_row($result, IBASE_TEXT)) { + if (strpos($row[0], "NEW." . strtoupper($field))) { + $pos = strpos($row[0], "GEN_ID("); + + if ($pos > 0) { + $pos2 = strpos($row[0],",",$pos + 7); + + if ($pos2 > 0) { + $generator = substr($row[0], $pos +7, $pos2 - $pos- 7); + } + } + break; + } + } + + if (!empty($generator)) { + $sql = "SELECT GEN_ID(". $generator . ",0) AS maxi FROM RDB" . "$" . "DATABASE"; + $res = $this->rawQuery($sql); + $data = $this->fetchRow($res); + return $data['maxi']; + } 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), 'top') || strpos(strtolower($limit), 'top') === 0) { + $rt = ' FIRST'; + } + $rt .= ' ' . $limit; + + if (is_int($offset) && $offset > 0) { + $rt .= ' SKIP ' . $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'))) { + return strtolower($col); + } + if ($col == 'TIMESTAMP') { + return 'datetime'; + } + if ($col == 'SMALLINT') { + return 'boolean'; + } + if (strpos($col, 'int') !== false || $col == 'numeric' || $col == 'INTEGER') { + return 'integer'; + } + if (strpos($col, 'char') !== false) { + return 'string'; + } + if (strpos($col, 'text') !== false) { + return 'text'; + } + if (strpos($col, 'VARCHAR') !== false) { + return 'string'; + } + if (strpos($col, 'BLOB') !== false) { + return 'text'; + } + if (in_array($col, array('FLOAT', 'NUMERIC', 'DECIMAL'))) { + return 'float'; + } + return 'text'; + } +/** + * Enter description here... + * + * @param unknown_type $results + */ + function resultSet(&$results) { + $this->results =& $results; + $this->map = array(); + $num_fields = ibase_num_fields($results); + $index = 0; + $j = 0; + + while ($j < $num_fields) { + $column = ibase_field_info($results, $j); + if (!empty($column[2])) { + $this->map[$index++] = array(ucfirst(strtolower($this->modeltmp[strtolower($column[2])])), strtolower($column[1])); + } else { + $this->map[$index++] = array(0, strtolower($column[1])); + } + $j++; + } + } +/** + * Builds final SQL statement + * + * @param string $type Query type + * @param array $data Query data + * @return string + */ + function renderStatement($type, $data) { + extract($data); + + if (strtolower($type) == 'select') { + 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} {$order}) AS Set1 {$rOrder}) AS Set2 {$order2}"; + } else { + return "SELECT {$limit} {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$order}"; + } + } else { + return parent::renderStatement($type, $data); + } + } +/** + * Fetches the next row from the current result set + * + * @return unknown + */ + function fetchResult() { + if ($row = ibase_fetch_row($this->results, IBASE_TEXT)) { + $resultRow = array(); + $i = 0; + + foreach ($row as $index => $field) { + list($table, $column) = $this->map[$index]; + + if (trim($table) == "") { + $resultRow[0][$column] = $row[$index]; + } else { + $resultRow[$table][$column] = $row[$index]; + $i++; + } + } + return $resultRow; + } else { + return false; + } + } +} +?> \ No newline at end of file diff --git a/cake/libs/model/datasources/dbo/dbo_mssql.php b/cake/libs/model/datasources/dbo/dbo_mssql.php new file mode 100755 index 0000000..1b1219d --- /dev/null +++ b/cake/libs/model/datasources/dbo/dbo_mssql.php @@ -0,0 +1,746 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * MS SQL layer for DBO + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.model.datasources.dbo + * @since CakePHP(tm) v 0.10.5.1790 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Short description for class. + * + * 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' + ); +/** + * 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/", 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; + } +/** + * 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) { + return mssql_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->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() { + $error = mssql_get_last_message($this->connection); + + if ($error) { + if (!preg_match('/contexto de la base de datos a|contesto di database|changed database|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" . join(",\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'] = join(', ', 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; + } +} +?> \ No newline at end of file diff --git a/cake/libs/model/datasources/dbo/dbo_mysql.php b/cake/libs/model/datasources/dbo/dbo_mysql.php new file mode 100755 index 0000000..2d97e14 --- /dev/null +++ b/cake/libs/model/datasources/dbo/dbo_mysql.php @@ -0,0 +1,665 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * MySQL layer for DBO + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.model.datasources.dbo + * @since CakePHP(tm) v 0.10.5.1790 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * 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' + ); +/** + * 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') + ); +/** + * 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 = join(', ', $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 = 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 '.$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)); + $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 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 .' (' . join(', ', 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 = join(', ', array_map(array(&$this, 'name'), $fields)); + } + $values = implode(', ', $values); + $this->query("INSERT INTO {$table} ({$fields}) VALUES {$values}"); + } +} + +/** + * 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 { +/** + * Enter description here... + * + * @var unknown_type + */ + 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', + 'connect' => 'mysql_pconnect' + ); +/** + * 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; + $connect = $config['connect']; + $this->connected = false; + + if (!$config['persistent']) { + $this->connection = mysql_connect($config['host'] . ':' . $config['port'], $config['login'], $config['password'], true); + } else { + $this->connection = $connect($config['host'] . ':' . $config['port'], $config['login'], $config['password']); + } + + if (mysql_select_db($config['database'], $this->connection)) { + $this->connected = true; + } + + if (isset($config['encoding']) && !empty($config['encoding'])) { + $this->setEncoding($config['encoding']); + } + + $this->_useAlias = (bool)version_compare(mysql_get_server_info($this->connection), "4.1", ">="); + + return $this->connected; + } +/** + * 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_array($result)) { + $tables[] = $line[0]; + } + 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) { + $cache = parent::describe($model); + if ($cache != null) { + return $cache; + } + $fields = false; + $cols = $this->query('DESCRIBE ' . $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']]; + } + } + } + $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 || (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_int($data) || is_float($data) || $data === '0') || ( + is_numeric($data) && strpos($data, ',') === false && + $data[0] != '0' && strpos($data, 'e') === false)) { + return $data; + } + default: + $data = "'" . mysql_real_escape_string($data, $this->connection) . "'"; + break; + } + return $data; + } +/** + * 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; + } +/** + * 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'; + } +/** + * 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)) { + $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); + } +} +?> \ No newline at end of file diff --git a/cake/libs/model/datasources/dbo/dbo_mysqli.php b/cake/libs/model/datasources/dbo/dbo_mysqli.php new file mode 100755 index 0000000..b0adf8d --- /dev/null +++ b/cake/libs/model/datasources/dbo/dbo_mysqli.php @@ -0,0 +1,411 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * MySQLi layer for DBO + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.model.datasources.dbo + * @since CakePHP(tm) v 1.1.4.2974 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +App::import('Core', '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 { +/** + * Enter description here... + * + * @var unknown_type + */ + 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', + 'connect' => 'mysqli_connect' + ); +/** + * 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; + } +/** + * 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_array($result)) { + $tables[] = $line[0]; + } + 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) { + + $cache = parent::describe($model); + if ($cache != null) { + return $cache; + } + + $fields = false; + $cols = $this->query('DESCRIBE ' . $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']]; + } + } + } + + $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 || (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; + } +/** + * 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'; + } +/** + * 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 integer 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 ($limit != null) { + return intval($limit); + } + 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(); + $i = 0; + 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]; + $i++; + } + return $resultRow; + } + return false; + } +/** + * Gets the database encoding + * + * @return string The database encoding + */ + function getEncoding() { + return mysqli_client_encoding($this->connection); + } +/** + * Checks if the result is valid + * + * @return boolean True if the result is valid, else false + */ + function hasResult() { + return is_object($this->_result); + } +} +?> \ No newline at end of file diff --git a/cake/libs/model/datasources/dbo/dbo_odbc.php b/cake/libs/model/datasources/dbo/dbo_odbc.php new file mode 100755 index 0000000..a8572a7 --- /dev/null +++ b/cake/libs/model/datasources/dbo/dbo_odbc.php @@ -0,0 +1,360 @@ +<?php +/* SVN FILE: $Id$ */ + +/** + * ODBC for DBO + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.model.dbo + * @since CakePHP(tm) v 0.10.5.1790 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ + +/** + * Short description for class. + * + * Long description for class + * + * @package cake + * @subpackage cake.cake.libs.model.datasources.dbo + */ +class DboOdbc extends DboSource { +/** + * Driver description + * + * @var string + */ + var $description = "ODBC DBO Driver"; +/** + * Table/column starting quote + * + * @var string + */ + var $startQuote = "`"; +/** + * Table/column end quote + * + * @var string + */ + var $endQuote = "`"; +/** + * Driver base configuration + * + * @var array + */ + var $_baseConfig = array( + 'persistent' => true, + 'login' => 'root', + 'password' => '', + 'database' => 'cake', + 'connect' => 'odbc_pconnect' + ); +/** + * Enter description here... + * + * @var unknown_type + */ + var $columns = array(); + + // var $columns = array('primary_key' => array('name' => 'int(11) DEFAULT NULL auto_increment'), + // 'string' => array('name' => 'varchar', 'limit' => '255'), + // 'text' => array('name' => 'text'), + // 'integer' => array('name' => 'int', 'limit' => '11'), + // 'float' => array('name' => 'float'), + // 'datetime' => array('name' => 'datetime', 'format' => 'Y-m-d h:i:s', 'formatter' => 'date'), + // 'timestamp' => array('name' => 'datetime', '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')); +/** + * 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; + $connect = $config['connect']; + if (!$config['persistent']) { + $connect = 'odbc_connect'; + } + if (!function_exists($connect)) { + die('no odbc?'); + } + $this->connected = false; + $this->connection = $connect($config['database'], $config['login'], $config['password'], SQL_CUR_USE_ODBC); + if ($this->connection) { + $this->connected = true; + } + + return $this->connected; + } +/** + * Disconnects from database. + * + * @return boolean True if the database could be disconnected, else false + */ + function disconnect() { + return @odbc_close($this->connection); + } +/** + * Executes given SQL statement. + * + * @param string $sql SQL statement + * @return resource Result resource identifier + * @access protected + */ + function _execute($sql) { + switch ($sql) { + case 'BEGIN': + return odbc_autocommit($this->connection, false); + case 'COMMIT': + return odbc_commit($this->connection); + case 'ROLLBACK': + return odbc_rollback($this->connection); + } + // TODO: should flags be set? possible requirement: SQL_CURSOR_STATIC + return odbc_exec($this->connection, $sql); + } +/** + * 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 = odbc_tables($this->connection); + + $tables = array(); + while (odbc_fetch_row($result)) { + array_push($tables, odbc_result($result, '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; + } + + $fields = array(); + $sql = 'SELECT * FROM ' . $this->fullTableName($model); + $result = odbc_exec($this->connection, $sql); + + $count = odbc_num_fields($result); + + for ($i = 1; $i <= $count; $i++) { + $cols[$i - 1] = odbc_field_name($result, $i); + } + + foreach ($cols as $column) { + $type = odbc_field_type(odbc_exec($this->connection, 'SELECT ' . $column . ' FROM ' . $this->fullTableName($model)), 1); + $fields[$column] = array('type' => $type); + } + + $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 + * @param string $column The column into which this data will be inserted + * @return string Quoted and escaped + * @todo Add logic that formats/escapes data based on column type + */ + function value($data, $column = null) { + $parent=parent::value($data, $column); + + if ($parent != null) { + return $parent; + } + + if ($data === null) { + return 'NULL'; + } + + if (!is_numeric($data)) { + return "'" . $data . "'"; + } + + return $data; + } +/** + * Returns a formatted error message from previous database operation. + * + * @return string Error message with error number + */ + function lastError() { + if ($error = odbc_errormsg($this->connection)) { + return odbc_error($this->connection) . ': ' . $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->hasResult()) { + return odbc_num_rows($this->_result); + } + return null; + } +/** + * Returns number of rows in previous resultset. If no previous resultset exists, + * this returns false. + * + * @return int Number of rows in resultset + */ + function lastNumRows() { + if ($this->hasResult()) { + return odbc_num_rows($this->_result); + } + return null; + } +/** + * Returns the ID generated from the previous INSERT operation. + * + * @param unknown_type $source + * @return int + */ + function lastInsertId($source = null) { + $result = $this->fetchRow('SELECT @@IDENTITY'); + return $result[0]; + } +/** + * Enter description here... + * + * @param string $real Real database-layer column type (i.e. "varchar(255)") + */ + function column($real) { + if (is_array($real)) { + $col=$real['name']; + if (isset($real['limit'])) { + $col .= '(' . $real['limit'] . ')'; + } + return $col; + } + return $real; + } +/** +* Enter description here... +* +* @param unknown_type $results +*/ + function resultSet(&$results) { + $this->results =& $results; + $num_fields = odbc_num_fields($results); + $this->map = array(); + $index = 0; + $j = 0; + while ($j < $num_fields) { + $column = odbc_field_name($results, $j+1); + + if (strpos($column, '_dot_') !== false) { + list($table, $column) = explode('_dot_', $column); + $this->map[$index++] = array($table, $column); + } else { + $this->map[$index++] = array(0, $column); + } + $j++; + } + } +/** +* 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 = null, $quote = true) { + if (empty($alias)) { + $alias = $model->name; + } + if (!is_array($fields)) { + if ($fields != null) { + $fields = array_map('trim', explode(',', $fields)); + } else { + foreach($model->tableToModel as $tableName => $modelName) { + foreach($this->__descriptions[$model->tablePrefix .$tableName] as $field => $type) { + $fields[] = $modelName .'.' .$field; + } + } + } + } + + $count = count($fields); + + if ($count >= 1 && $fields[0] != '*' && strpos($fields[0], 'COUNT(*)') === false) { + for ($i = 0; $i < $count; $i++) { + if (!preg_match('/^.+\\(.*\\)/', $fields[$i])) { + $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 . '_dot_' . $fields[$i]); + } else { + $build = explode('.', $fields[$i]); + $fields[$i] = $prepend . $this->name($build[0]) . '.' . $this->name($build[1]) . ' AS ' . $this->name($build[0] . '_dot_' . $build[1]); + } + } + } + } + return $fields; + } +/** + * Fetches the next row from the current result set + * + * @return unknown + */ + function fetchResult() { + if ($row = odbc_fetch_row($this->results)) { + $resultRow = array(); + $numFields = odbc_num_fields($this->results); + $i = 0; + for($i = 0; $i < $numFields; $i++) { + list($table, $column) = $this->map[$i]; + $resultRow[$table][$column] = odbc_result($this->results, $i + 1); + } + return $resultRow; + } + return false; + } +} +?> \ No newline at end of file diff --git a/cake/libs/model/datasources/dbo/dbo_oracle.php b/cake/libs/model/datasources/dbo/dbo_oracle.php new file mode 100755 index 0000000..4aa25a6 --- /dev/null +++ b/cake/libs/model/datasources/dbo/dbo_oracle.php @@ -0,0 +1,1121 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Oracle layer for DBO. + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.model.datasources.dbo + * @since CakePHP v 1.2.0.4041 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Short description for class. + * + * Long description for class + * + * @package cake + * @subpackage cake.cake.libs.model.datasources.dbo + */ +class DboOracle extends DboSource { +/** + * Enter description here... + * + * @var unknown_type + * @access public + */ + var $config = array(); +/** + * Enter description here... + * + * @var unknown_type + */ + var $alias = ''; +/** + * Sequence names as introspected from the database + */ + var $_sequences = array(); +/** + * Transaction in progress flag + * + * @var boolean + */ + var $__transactionStarted = false; +/** + * Enter description here... + * + * @var unknown_type + * @access public + */ + var $columns = array( + 'primary_key' => array('name' => ''), + 'string' => array('name' => 'varchar2', 'limit' => '255'), + 'text' => array('name' => 'varchar2'), + 'integer' => array('name' => 'number'), + 'float' => array('name' => 'float'), + 'datetime' => array('name' => 'date', 'format' => 'Y-m-d H:i:s'), + 'timestamp' => array('name' => 'date', 'format' => 'Y-m-d H:i:s'), + 'time' => array('name' => 'date', 'format' => 'Y-m-d H:i:s'), + 'date' => array('name' => 'date', 'format' => 'Y-m-d H:i:s'), + 'binary' => array('name' => 'bytea'), + 'boolean' => array('name' => 'boolean'), + 'number' => array('name' => 'number'), + 'inet' => array('name' => 'inet')); +/** + * Enter description here... + * + * @var unknown_type + * @access protected + */ + var $connection; +/** + * Enter description here... + * + * @var unknown_type + * @access protected + */ + var $_limit = -1; +/** + * Enter description here... + * + * @var unknown_type + * @access protected + */ + var $_offset = 0; +/** + * Enter description here... + * + * @var unknown_type + * @access protected + */ + var $_map; +/** + * Enter description here... + * + * @var unknown_type + * @access protected + */ + var $_currentRow; +/** + * Enter description here... + * + * @var unknown_type + * @access protected + */ + var $_numRows; +/** + * Enter description here... + * + * @var unknown_type + * @access protected + */ + var $_results; +/** + * Last error issued by oci extension + * + * @var unknown_type + */ + var $_error; +/** + * Base configuration settings for MySQL driver + * + * @var array + */ + var $_baseConfig = array( + 'persistent' => true, + 'host' => 'localhost', + 'login' => 'system', + 'password' => '', + 'database' => 'cake', + 'nls_sort' => '', + 'nls_sort' => '' + ); +/** + * Table-sequence map + * + * @var unknown_type + */ + var $_sequenceMap = array(); +/** + * Connects to the database using options in the given configuration array. + * + * @return boolean True if the database could be connected, else false + * @access public + */ + function connect() { + $config = $this->config; + $this->connected = false; + $config['charset'] = !empty($config['charset']) ? $config['charset'] : null; + + if ($this->config['persistent']) { + $connect = 'ociplogon'; + } else { + $connect = 'ocilogon'; + } + $this->connection = @$connect($config['login'], $config['password'], $config['database'], $config['charset']); + + if ($this->connection) { + $this->connected = true; + if (!empty($config['nls_sort'])) { + $this->execute('ALTER SESSION SET NLS_SORT='.$config['nls_sort']); + } + + if (!empty($config['nls_comp'])) { + $this->execute('ALTER SESSION SET NLS_COMP='.$config['nls_comp']); + } + $this->execute("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'"); + } else { + $this->connected = false; + $this->_setError(); + return false; + } + return $this->connected; + } + /** + * Keeps track of the most recent Oracle error + * + */ + function _setError($source = null, $clear = false) { + if ($source) { + $e = ocierror($source); + } else { + $e = ocierror(); + } + $this->_error = $e['message']; + if ($clear) { + $this->_error = null; + } + } +/** + * Sets the encoding language of the session + * + * @param string $lang language constant + * @return bool + */ + function setEncoding($lang) { + if (!$this->execute('ALTER SESSION SET NLS_LANGUAGE='.$lang)) { + return false; + } + return true; + } +/** + * Gets the current encoding language + * + * @return string language constant + */ + function getEncoding() { + $sql = 'SELECT VALUE FROM NLS_SESSION_PARAMETERS WHERE PARAMETER=\'NLS_LANGUAGE\''; + if (!$this->execute($sql)) { + return false; + } + + if (!$row = $this->fetchRow()) { + return false; + } + return $row[0]['VALUE']; + } +/** + * Disconnects from database. + * + * @return boolean True if the database could be disconnected, else false + * @access public + */ + function disconnect() { + if ($this->connection) { + $this->connected = !ocilogoff($this->connection); + return !$this->connected; + } + } +/** + * Scrape the incoming SQL to create the association map. This is an extremely + * experimental method that creates the association maps since Oracle will not tell us. + * + * @param string $sql + * @return false if sql is nor a SELECT + * @access protected + */ + function _scrapeSQL($sql) { + $sql = str_replace("\"", '', $sql); + $preFrom = preg_split('/\bFROM\b/', $sql); + $preFrom = $preFrom[0]; + $find = array('SELECT'); + $replace = array(''); + $fieldList = trim(str_replace($find, $replace, $preFrom)); + $fields = preg_split('/,\s+/', $fieldList);//explode(', ', $fieldList); + $lastTableName = ''; + + foreach($fields as $key => $value) { + if ($value != 'COUNT(*) AS count') { + if (preg_match('/\s+(\w+(\.\w+)*)$/', $value, $matches)) { + $fields[$key] = $matches[1]; + + if (preg_match('/^(\w+\.)/', $value, $matches)) { + $fields[$key] = $matches[1] . $fields[$key]; + $lastTableName = $matches[1]; + } + } + /* + if (preg_match('/(([[:alnum:]_]+)\.[[:alnum:]_]+)(\s+AS\s+(\w+))?$/i', $value, $matches)) { + $fields[$key] = isset($matches[4]) ? $matches[2] . '.' . $matches[4] : $matches[1]; + } + */ + } + } + $this->_map = array(); + + foreach($fields as $f) { + $e = explode('.', $f); + if (count($e) > 1) { + $table = $e[0]; + $field = strtolower($e[1]); + } else { + $table = 0; + $field = $e[0]; + } + $this->_map[] = array($table, $field); + } + } +/** + * Modify a SQL query to limit (and offset) the result set + * + * @param integer $limit Maximum number of rows to return + * @param integer $offset Row to begin returning + * @return modified SQL Query + * @access public + */ + function limit($limit = -1, $offset = 0) { + $this->_limit = (int) $limit; + $this->_offset = (int) $offset; + } +/** + * Returns number of rows in previous resultset. If no previous resultset exists, + * this returns false. + * + * @return integer Number of rows in resultset + * @access public + */ + function lastNumRows() { + return $this->_numRows; + } +/** + * Executes given SQL statement. This is an overloaded method. + * + * @param string $sql SQL statement + * @return resource Result resource identifier or null + * @access protected + */ + function _execute($sql) { + $this->_statementId = @ociparse($this->connection, $sql); + if (!$this->_statementId) { + $this->_setError($this->connection); + return false; + } + + if ($this->__transactionStarted) { + $mode = OCI_DEFAULT; + } else { + $mode = OCI_COMMIT_ON_SUCCESS; + } + + if (!@ociexecute($this->_statementId, $mode)) { + $this->_setError($this->_statementId); + return false; + } + + $this->_setError(null, true); + + switch(ocistatementtype($this->_statementId)) { + case 'DESCRIBE': + case 'SELECT': + $this->_scrapeSQL($sql); + break; + default: + return $this->_statementId; + break; + } + + if ($this->_limit >= 1) { + ocisetprefetch($this->_statementId, $this->_limit); + } else { + ocisetprefetch($this->_statementId, 3000); + } + $this->_numRows = ocifetchstatement($this->_statementId, $this->_results, $this->_offset, $this->_limit, OCI_NUM | OCI_FETCHSTATEMENT_BY_ROW); + $this->_currentRow = 0; + $this->limit(); + return $this->_statementId; + } +/** + * Enter description here... + * + * @return unknown + * @access public + */ + function fetchRow() { + if ($this->_currentRow >= $this->_numRows) { + ocifreestatement($this->_statementId); + $this->_map = null; + $this->_results = null; + $this->_currentRow = null; + $this->_numRows = null; + return false; + } + $resultRow = array(); + + foreach($this->_results[$this->_currentRow] as $index => $field) { + list($table, $column) = $this->_map[$index]; + + if (strpos($column, ' count')) { + $resultRow[0]['count'] = $field; + } else { + $resultRow[$table][$column] = $this->_results[$this->_currentRow][$index]; + } + } + $this->_currentRow++; + return $resultRow; + } +/** + * Fetches the next row from the current result set + * + * @return unknown + */ + function fetchResult() { + return $this->fetchRow(); + } +/** + * Checks to see if a named sequence exists + * + * @param string $sequence + * @return bool + * @access public + */ + function sequenceExists($sequence) { + $sql = "SELECT SEQUENCE_NAME FROM USER_SEQUENCES WHERE SEQUENCE_NAME = '$sequence'"; + if (!$this->execute($sql)) { + return false; + } + return $this->fetchRow(); + } +/** + * Creates a database sequence + * + * @param string $sequence + * @return bool + * @access public + */ + function createSequence($sequence) { + $sql = "CREATE SEQUENCE $sequence"; + return $this->execute($sql); + } +/** + * Enter description here... + * + * @param unknown_type $table + * @return unknown + * @access public + */ + function createTrigger($table) { + $sql = "CREATE OR REPLACE TRIGGER pk_$table" . "_trigger BEFORE INSERT ON $table FOR EACH ROW BEGIN SELECT pk_$table.NEXTVAL INTO :NEW.ID FROM DUAL; END;"; + return $this->execute($sql); + } +/** + * Returns an array of tables in the database. If there are no tables, an error is + * raised and the application exits. + * + * @return array tablenames in the database + * @access public + */ + function listSources() { + $cache = parent::listSources(); + if ($cache != null) { + return $cache; + } + $sql = 'SELECT view_name AS name FROM all_views UNION SELECT table_name AS name FROM all_tables'; + + if (!$this->execute($sql)) { + return false; + } + $sources = array(); + + while($r = $this->fetchRow()) { + $sources[] = strtolower($r[0]['name']); + } + parent::listSources($sources); + return $sources; + } +/** + * Returns an array of the fields in given table name. + * + * @param object instance of a model to inspect + * @return array Fields in table. Keys are name and type + * @access public + */ + function describe(&$model) { + $table = $this->fullTableName($model, false); + + if (!empty($model->sequence)) { + $this->_sequenceMap[$table] = $model->sequence; + } elseif (!empty($model->table)) { + $this->_sequenceMap[$table] = $model->table . '_seq'; + } + + $cache = parent::describe($model); + + if ($cache != null) { + return $cache; + } + + $sql = 'SELECT COLUMN_NAME, DATA_TYPE, DATA_LENGTH FROM all_tab_columns WHERE table_name = \''; + $sql .= strtoupper($this->fullTableName($model)) . '\''; + + if (!$this->execute($sql)) { + return false; + } + + $fields = array(); + + for ($i = 0; $row = $this->fetchRow(); $i++) { + $fields[strtolower($row[0]['COLUMN_NAME'])] = array( + 'type'=> $this->column($row[0]['DATA_TYPE']), + 'length'=> $row[0]['DATA_LENGTH'] + ); + } + $this->__cacheDescription($this->fullTableName($model, false), $fields); + + return $fields; + } +/** + * Deletes all the records in a table and drops all associated auto-increment sequences. + * Using DELETE instead of TRUNCATE because it causes locking problems. + * + * @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 (empty($this->_sequences)) { + $sql = "SELECT sequence_name FROM all_sequences"; + $this->execute($sql); + while ($row = $this->fetchRow()) { + $this->_sequences[] = strtolower($row[0]['sequence_name']); + } + } + + $this->execute('DELETE FROM ' . $this->fullTableName($table)); + if (!isset($this->_sequenceMap[$table]) || !in_array($this->_sequenceMap[$table], $this->_sequences)) { + return true; + } + if ($reset === 0) { + $this->execute("SELECT {$this->_sequenceMap[$table]}.nextval FROM dual"); + $row = $this->fetchRow(); + $currval = $row[$this->_sequenceMap[$table]]['nextval']; + + $this->execute("SELECT min_value FROM all_sequences WHERE sequence_name = '{$this->_sequenceMap[$table]}'"); + $row = $this->fetchRow(); + $min_value = $row[0]['min_value']; + + if ($min_value == 1) $min_value = 0; + $offset = -($currval - $min_value); + + $this->execute("ALTER SEQUENCE {$this->_sequenceMap[$table]} INCREMENT BY $offset MINVALUE $min_value"); + $this->execute("SELECT {$this->_sequenceMap[$table]}.nextval FROM dual"); + $this->execute("ALTER SEQUENCE {$this->_sequenceMap[$table]} INCREMENT BY 1"); + } else { + //$this->execute("DROP SEQUENCE {$this->_sequenceMap[$table]}"); + } + return true; + } +/** + * Enables, disables, and lists table constraints + * + * Note: This method could have been written using a subselect for each table, + * however the effort Oracle expends to run the constraint introspection is very high. + * Therefore, this method caches the result once and loops through the arrays to find + * what it needs. It reduced my query time by 50%. YMMV. + * + * @param string $action + * @param string $table + * @return mixed boolean true or array of constraints + */ + function constraint($action, $table) { + if (empty($table)) { + trigger_error(__('Must specify table to operate on constraints')); + } + + $table = strtoupper($table); + + if (empty($this->_keyConstraints)) { + $sql = "SELECT + table_name, + c.constraint_name + FROM all_cons_columns cc + LEFT JOIN all_indexes i ON (cc.constraint_name = i.index_name) + LEFT JOIN all_constraints c ON(c.constraint_name = cc.constraint_name)"; + $this->execute($sql); + while ($row = $this->fetchRow()) { + $this->_keyConstraints[] = array($row[0]['table_name'], $row['c']['constraint_name']); + } + } + + $relatedKeys = array(); + foreach ($this->_keyConstraints as $c) { + if ($c[0] == $table) { + $relatedKeys[] = $c[1]; + } + } + + if (empty($this->_constraints)) { + $sql = "SELECT + table_name, + constraint_name, + r_constraint_name + FROM + all_constraints"; + $this->execute($sql); + while ($row = $this->fetchRow()) { + $this->_constraints[] = $row[0]; + } + } + + $constraints = array(); + foreach ($this->_constraints as $c) { + if (in_array($c['r_constraint_name'], $relatedKeys)) { + $constraints[] = array($c['table_name'], $c['constraint_name']); + } + } + + foreach ($constraints as $c) { + list($table, $constraint) = $c; + switch ($action) { + case 'enable': + $this->execute("ALTER TABLE $table ENABLE CONSTRAINT $constraint"); + break; + case 'disable': + $this->execute("ALTER TABLE $table DISABLE CONSTRAINT $constraint"); + break; + case 'list': + return $constraints; + break; + default: + trigger_error(__('DboOracle::constraint() accepts only enable, disable, or list')); + } + } + return true; + } +/** + * 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, false); + if ($table) { + $indexes = $this->query('SELECT + cc.table_name, + cc.column_name, + cc.constraint_name, + c.constraint_type, + i.index_name, + i.uniqueness + FROM all_cons_columns cc + LEFT JOIN all_indexes i ON(cc.constraint_name = i.index_name) + LEFT JOIN all_constraints c ON(c.constraint_name = cc.constraint_name) + WHERE cc.table_name = \'' . strtoupper($table) .'\''); + foreach ($indexes as $i => $idx) { + if ($idx['c']['constraint_type'] == 'P') { + $key = 'PRIMARY'; + } else { + continue; + } + if (!isset($index[$key])) { + $index[$key]['column'] = strtolower($idx['cc']['column_name']); + $index[$key]['unique'] = intval($idx['i']['uniqueness'] == 'UNIQUE'); + } else { + if (!is_array($index[$key]['column'])) { + $col[] = $index[$key]['column']; + } + $col[] = strtolower($idx['cc']['column_name']); + $index[$key]['column'] = $col; + } + } + } + return $index; + } +/** + * Generate a Oracle Alter Table syntax for the given Schema comparison + * + * @param unknown_type $schema + * @return unknown + */ + function alterSchema($compare, $table = null) { + if (!is_array($compare)) { + return false; + } + $out = ''; + $colList = array(); + foreach($compare as $curTable => $types) { + if (!$table || $table == $curTable) { + $out .= 'ALTER TABLE ' . $this->fullTableName($curTable) . " \n"; + foreach($types as $type => $column) { + 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; + } + } + $out .= "\t" . join(",\n\t", $colList) . ";\n\n"; + } + } + return $out; + } +/** + * This method should quote Oracle identifiers. Well it doesn't. + * It would break all scaffolding and all of Cake's default assumptions. + * + * @param unknown_type $var + * @return unknown + * @access public + */ + function name($name) { + if (strpos($name, '.') !== false && strpos($name, '"') === false) { + list($model, $field) = explode('.', $name); + if ($field[0] == "_") { + $name = "$model.\"$field\""; + } + } else { + if ($name[0] == "_") { + $name = "\"$name\""; + } + } + return $name; + } +/** + * Begin a transaction + * + * @param unknown_type $model + * @return boolean True on success, false on fail + * (i.e. if the database/model does not support transactions). + */ + function begin() { + $this->__transactionStarted = true; + return true; + } +/** + * Rollback a transaction + * + * @param unknown_type $model + * @return boolean True on success, false on fail + * (i.e. if the database/model does not support transactions, + * or a transaction has not started). + */ + function rollback() { + return ocirollback($this->connection); + } +/** + * Commit a transaction + * + * @param unknown_type $model + * @return boolean True on success, false on fail + * (i.e. if the database/model does not support transactions, + * or a transaction has not started). + */ + function commit() { + $this->__transactionStarted = false; + return ocicommit($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") + * @access public + */ + function column($real) { + if (is_array($real)) { + $col = $real['name']; + + if (isset($real['limit'])) { + $col .= '('.$real['limit'].')'; + } + return $col; + } else { + $real = strtolower($real); + } + $col = str_replace(')', '', $real); + $limit = null; + if (strpos($col, '(') !== false) { + list($col, $limit) = explode('(', $col); + } + + if (in_array($col, array('date', 'timestamp'))) { + return $col; + } + if (strpos($col, 'number') !== false) { + return 'integer'; + } + if (strpos($col, 'integer') !== false) { + return 'integer'; + } + if (strpos($col, 'char') !== false) { + return 'string'; + } + if (strpos($col, 'text') !== false) { + return 'text'; + } + if (strpos($col, 'blob') !== false) { + return 'binary'; + } + if (in_array($col, array('float', 'double', 'decimal'))) { + return 'float'; + } + if ($col == 'boolean') { + return $col; + } + return 'text'; + } +/** + * 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 + * @access public + */ + 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 === '') { + return "''"; + } + + switch($column) { + case 'date': + $data = date('Y-m-d H:i:s', strtotime($data)); + $data = "TO_DATE('$data', 'YYYY-MM-DD HH24:MI:SS')"; + break; + case 'integer' : + case 'float' : + case null : + if (is_numeric($data)) { + break; + } + default: + $data = str_replace("'", "''", $data); + $data = "'$data'"; + break; + } + return $data; + } +/** + * Returns the ID generated from the previous INSERT operation. + * + * @param string + * @return integer + * @access public + */ + function lastInsertId($source) { + $sequence = $this->_sequenceMap[$source]; + $sql = "SELECT $sequence.currval FROM dual"; + + if (!$this->execute($sql)) { + return false; + } + + while($row = $this->fetchRow()) { + return $row[$sequence]['currval']; + } + return false; + } +/** + * Returns a formatted error message from previous database operation. + * + * @return string Error message with error number + * @access public + */ + function lastError() { + return $this->_error; + } +/** + * Returns number of affected rows in previous database operation. If no previous operation exists, this returns false. + * + * @return int Number of affected rows + * @access public + */ + function lastAffected() { + return $this->_statementId ? ocirowcount($this->_statementId): false; + } +/** + * Renders a final SQL statement by putting together the component parts in the correct order + * + * @param string $type + * @param array $data + * @return string + */ + function renderStatement($type, $data) { + extract($data); + $aliases = null; + + switch (strtolower($type)) { + case 'select': + return "SELECT {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$order} {$limit}"; + break; + case 'create': + return "INSERT INTO {$table} ({$fields}) VALUES ({$values})"; + break; + case 'update': + if (!empty($alias)) { + $aliases = "{$this->alias}{$alias} "; + } + return "UPDATE {$table} {$aliases}SET {$fields} {$conditions}"; + break; + case 'delete': + if (!empty($alias)) { + $aliases = "{$this->alias}{$alias} "; + } + return "DELETE FROM {$table} {$aliases}{$conditions}"; + break; + case 'schema': + foreach (array('columns', 'indexes') as $var) { + if (is_array(${$var})) { + ${$var} = "\t" . join(",\n\t", array_filter(${$var})); + } + } + if (trim($indexes) != '') { + $columns .= ','; + } + return "CREATE TABLE {$table} (\n{$columns}{$indexes})"; + break; + case 'alter': + break; + } + } +/** + * Enter description here... + * + * @param Model $model + * @param unknown_type $linkModel + * @param string $type Association type + * @param unknown_type $association + * @param unknown_type $assocData + * @param unknown_type $queryData + * @param unknown_type $external + * @param unknown_type $resultSet + * @param integer $recursive Number of levels of association + * @param array $stack + */ + function queryAssociation(&$model, &$linkModel, $type, $association, $assocData, &$queryData, $external = false, &$resultSet, $recursive, $stack) { + if ($query = $this->generateAssociationQuery($model, $linkModel, $type, $association, $assocData, $queryData, $external, $resultSet)) { + if (!isset($resultSet) || !is_array($resultSet)) { + if (Configure::read() > 0) { + echo '<div style = "font: Verdana bold 12px; color: #FF0000">' . sprintf(__('SQL Error in model %s:', true), $model->alias) . ' '; + if (isset($this->error) && $this->error != null) { + echo $this->error; + } + echo '</div>'; + } + return null; + } + $count = count($resultSet); + + if ($type === 'hasMany' && (!isset($assocData['limit']) || empty($assocData['limit']))) { + $ins = $fetch = array(); + for ($i = 0; $i < $count; $i++) { + if ($in = $this->insertQueryData('{$__cakeID__$}', $resultSet[$i], $association, $assocData, $model, $linkModel, $stack)) { + $ins[] = $in; + } + } + + if (!empty($ins)) { + $fetch = array(); + $ins = array_chunk($ins, 1000); + foreach ($ins as $i) { + $q = str_replace('{$__cakeID__$}', join(', ', $i), $query); + $q = str_replace('= (', 'IN (', $q); + $res = $this->fetchAll($q, $model->cacheQueries, $model->alias); + $fetch = array_merge($fetch, $res); + } + } + + if (!empty($fetch) && is_array($fetch)) { + if ($recursive > 0) { + + foreach ($linkModel->__associations as $type1) { + foreach ($linkModel->{$type1} as $assoc1 => $assocData1) { + $deepModel =& $linkModel->{$assoc1}; + $tmpStack = $stack; + $tmpStack[] = $assoc1; + + if ($linkModel->useDbConfig === $deepModel->useDbConfig) { + $db =& $this; + } else { + $db =& ConnectionManager::getDataSource($deepModel->useDbConfig); + } + $db->queryAssociation($linkModel, $deepModel, $type1, $assoc1, $assocData1, $queryData, true, $fetch, $recursive - 1, $tmpStack); + } + } + } + } + return $this->__mergeHasMany($resultSet, $fetch, $association, $model, $linkModel, $recursive); + } elseif ($type === 'hasAndBelongsToMany') { + $ins = $fetch = array(); + for ($i = 0; $i < $count; $i++) { + if ($in = $this->insertQueryData('{$__cakeID__$}', $resultSet[$i], $association, $assocData, $model, $linkModel, $stack)) { + $ins[] = $in; + } + } + + $foreignKey = $model->hasAndBelongsToMany[$association]['foreignKey']; + $joinKeys = array($foreignKey, $model->hasAndBelongsToMany[$association]['associationForeignKey']); + list($with, $habtmFields) = $model->joinModel($model->hasAndBelongsToMany[$association]['with'], $joinKeys); + $habtmFieldsCount = count($habtmFields); + + if (!empty($ins)) { + $fetch = array(); + $ins = array_chunk($ins, 1000); + foreach ($ins as $i) { + $q = str_replace('{$__cakeID__$}', '(' .join(', ', $i) .')', $query); + $q = str_replace('= (', 'IN (', $q); + $q = str_replace(' WHERE 1 = 1', '', $q); + + + $q = $this->insertQueryData($q, null, $association, $assocData, $model, $linkModel, $stack); + if ($q != false) { + $res = $this->fetchAll($q, $model->cacheQueries, $model->alias); + $fetch = array_merge($fetch, $res); + } + } + } + } + + for ($i = 0; $i < $count; $i++) { + $row =& $resultSet[$i]; + + if ($type !== 'hasAndBelongsToMany') { + $q = $this->insertQueryData($query, $resultSet[$i], $association, $assocData, $model, $linkModel, $stack); + if ($q != false) { + $fetch = $this->fetchAll($q, $model->cacheQueries, $model->alias); + } else { + $fetch = null; + } + } + + if (!empty($fetch) && is_array($fetch)) { + if ($recursive > 0) { + + foreach ($linkModel->__associations as $type1) { + foreach ($linkModel->{$type1} as $assoc1 => $assocData1) { + + $deepModel =& $linkModel->{$assoc1}; + if (($type1 === 'belongsTo') || ($deepModel->alias === $model->alias && $type === 'belongsTo') || ($deepModel->alias != $model->alias)) { + $tmpStack = $stack; + $tmpStack[] = $assoc1; + if ($linkModel->useDbConfig == $deepModel->useDbConfig) { + $db =& $this; + } else { + $db =& ConnectionManager::getDataSource($deepModel->useDbConfig); + } + $db->queryAssociation($linkModel, $deepModel, $type1, $assoc1, $assocData1, $queryData, true, $fetch, $recursive - 1, $tmpStack); + } + } + } + } + if ($type == 'hasAndBelongsToMany') { + $merge = array(); + foreach($fetch as $j => $data) { + if (isset($data[$with]) && $data[$with][$foreignKey] === $row[$model->alias][$model->primaryKey]) { + if ($habtmFieldsCount > 2) { + $merge[] = $data; + } else { + $merge[] = Set::diff($data, array($with => $data[$with])); + } + } + } + if (empty($merge) && !isset($row[$association])) { + $row[$association] = $merge; + } else { + $this->__mergeAssociation($resultSet[$i], $merge, $association, $type); + } + } else { + $this->__mergeAssociation($resultSet[$i], $fetch, $association, $type); + } + $resultSet[$i][$association] = $linkModel->afterfind($resultSet[$i][$association]); + + } else { + $tempArray[0][$association] = false; + $this->__mergeAssociation($resultSet[$i], $tempArray, $association, $type); + } + } + } + } + /** + * Generate a "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 ' . $this->fullTableName($curTable) . "\n"; + } + } + return $out; + } +} +?> \ No newline at end of file diff --git a/cake/libs/model/datasources/dbo/dbo_postgres.php b/cake/libs/model/datasources/dbo/dbo_postgres.php new file mode 100755 index 0000000..2298fc0 --- /dev/null +++ b/cake/libs/model/datasources/dbo/dbo_postgres.php @@ -0,0 +1,867 @@ +<?php +/* SVN FILE: $Id$ */ + +/** + * PostgreSQL layer for DBO. + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.model.datasources.dbo + * @since CakePHP(tm) v 0.9.1.114 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * 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( + 'connect' => 'pg_pconnect', + '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') + ); + + var $startQuote = '"'; + + 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; + } +/** + * 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'])) { + $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) { + return 'NULL'; + } + if (empty($column)) { + $column = $this->introspectType($data); + } + + switch($column) { + case 'inet': + case 'float': + case 'integer': + case 'date': + case 'datetime': + case 'timestamp': + if ($data === '') { + return $read ? 'NULL' : 'DEFAULT'; + } + 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; + 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 && $fields[0] != '*' && strpos($fields[0], 'COUNT(*)') === false) { + for ($i = 0; $i < $count; $i++) { + if (!preg_match('/^.+\\(.*\\)/', $fields[$i]) && !preg_match('/\s+AS\s+/', $fields[$i])) { + $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]); + } + } + } + } + return $fields; + } +/** + * 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 = 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); + $colList[] = 'ALTER COLUMN '. $fieldName .' TYPE ' . str_replace($fieldName, '', $this->buildColumn($col)); + } + 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" . join(",\n\t", $colList) . ";\n\n"; + } else { + $out = ''; + } + $out .= join(";\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 . ' (' . join(', ', 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'] = join(', ', 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} = join($join[$var], array_filter(${$var})); + } + } + return "CREATE TABLE {$table} (\n\t{$columns}\n);\n{$indexes}"; + break; + default: + return parent::renderStatement($type, $data); + break; + } + } +} +?> \ No newline at end of file diff --git a/cake/libs/model/datasources/dbo/dbo_sqlite.php b/cake/libs/model/datasources/dbo/dbo_sqlite.php new file mode 100755 index 0000000..c365b6f --- /dev/null +++ b/cake/libs/model/datasources/dbo/dbo_sqlite.php @@ -0,0 +1,595 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * SQLite layer for DBO + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.model.datasources.dbo + * @since CakePHP(tm) v 0.9.0 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * DBO implementation for the SQLite DBMS. + * + * Long description for class + * + * @package cake + * @subpackage cake.cake.libs.model.datasources.dbo + */ +class DboSqlite extends DboSource { +/** + * Enter description here... + * + * @var unknown_type + */ + 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, + 'connect' => 'sqlite_popen' + ); +/** + * 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') + ); +/** + * Connects to the database using config['database'] as a filename. + * + * @param array $config Configuration array for connecting + * @return mixed + */ + function connect() { + $config = $this->config; + $this->connection = $config['connect']($config['database']); + $this->connected = is_resource($this->connection); + + if ($this->connected) { + $this->_execute('PRAGMA count_changes = 1;'); + } + return $this->connected; + } +/** + * 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', E_USER_WARNING); + return null; + } + + if (!isset($this->columns[$type])) { + trigger_error("Column type {$type} does not exist", 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']; + } + if (isset($real['limit']) || isset($real['length']) || isset($column['limit']) || isset($column['length'])) { + if (isset($column['length'])) { + $length = $column['length']; + } elseif (isset($column['limit'])) { + $length = $column['limit']; + } elseif (isset($real['length'])) { + $length = $real['length']; + } else { + $length = $real['limit']; + } + $out .= '(' . $length . ')'; + } + if (isset($column['key']) && $column['key'] == 'primary' && $type == 'integer') { + $out .= ' ' . $this->columns['primary_key']['name']; + } elseif (isset($column['key']) && $column['key'] == 'primary') { + $out .= ' NOT NULL'; + } elseif (isset($column['default']) && isset($column['null']) && $column['null'] == false) { + $out .= ' DEFAULT ' . $this->value($column['default'], $type) . ' NOT NULL'; + } elseif (isset($column['default'])) { + $out .= ' DEFAULT ' . $this->value($column['default'], $type); + } elseif (isset($column['null']) && $column['null'] == true) { + $out .= ' DEFAULT NULL'; + } elseif (isset($column['null']) && $column['null'] == false) { + $out .= ' NOT NULL'; + } + return $out; + } +/** + * 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'] = join(', ', 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" . join(",\n\t", array_filter(${$var})); + } + } + return "CREATE TABLE {$table} (\n{$columns});\n{$indexes}"; + break; + default: + return parent::renderStatement($type, $data); + break; + } + } +} +?> \ No newline at end of file diff --git a/cake/libs/model/datasources/dbo/dbo_sybase.php b/cake/libs/model/datasources/dbo/dbo_sybase.php new file mode 100755 index 0000000..9e2a118 --- /dev/null +++ b/cake/libs/model/datasources/dbo/dbo_sybase.php @@ -0,0 +1,384 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Sybase layer for DBO + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.model.datasources.dbo + * @since CakePHP(tm) v 1.2.0.3097 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Short description for class. + * + * Long description for class + * + * @package cake + * @subpackage cake.cake.libs.model.datasources.dbo + */ +class DboSybase extends DboSource { +/** + * Driver description + * + * @var string + */ + var $description = "Sybase DBO Driver"; +/** + * Start quote for quoted identifiers + * + * @var string + */ + var $startQuote = ""; +/** + * End quote for quoted identifiers + * + * @var string + */ + var $endQuote = ""; +/** + * Base configuration settings for Sybase driver + * + * @var array + */ + var $_baseConfig = array( + 'persistent' => true, + 'host' => 'localhost', + 'login' => 'sa', + 'password' => '', + 'database' => 'cake', + 'port' => '4100' + ); +/** + * Sybase column definition + * + * @var array + */ + var $columns = array( + 'primary_key' => array('name' => 'numeric(9,0) IDENTITY PRIMARY KEY'), + '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' => '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') + ); +/** + * 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 = sybase_connect($config['host'], $config['login'], $config['password'], true); + } else { + $this->connection = sybase_pconnect($config['host'], $config['login'], $config['password']); + } + + if (sybase_select_db($config['database'], $this->connection)) { + $this->connected = true; + } + return $this->connected; + } +/** + * Disconnects from database. + * + * @return boolean True if the database could be disconnected, else false + */ + function disconnect() { + $this->connected = !@sybase_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 sybase_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("select name from sysobjects where type='U'"); + if (!$result) { + return array(); + } else { + + $tables = array(); + while ($line = sybase_fetch_array($result)) { + $tables[] = $line[0]; + } + + 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) { + + $cache = parent::describe($model); + if ($cache != null) { + return $cache; + } + + $fields = false; + $cols = $this->query('DESC ' . $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'], + 'length' => $this->length($column[0]['Type']), + ); + } + } + + $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 + * @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 ($data === '') { + return "''"; + } + + switch ($column) { + case 'boolean': + $data = $this->boolean((bool)$data); + break; + default: + $data = str_replace("'", "''", $data); + break; + } + + return "'" . $data . "'"; + } +/** + * Begin a transaction + * + * @param unknown_type $model + * @return boolean True on success, false on fail + * (i.e. if the database/model does not support transactions). + */ + function begin(&$model) { + if (parent::begin($model)) { + if ($this->execute('BEGIN TRAN')) { + $this->_transactionStarted = true; + return true; + } + } + return false; + } +/** + * Commit a transaction + * + * @param unknown_type $model + * @return boolean True on success, false on fail + * (i.e. if the database/model does not support transactions, + * or a transaction has not started). + */ + function commit(&$model) { + if (parent::commit($model)) { + $this->_transactionStarted = false; + return $this->execute('COMMIT TRAN'); + } + return false; + } +/** + * Rollback a transaction + * + * @param unknown_type $model + * @return boolean True on success, false on fail + * (i.e. if the database/model does not support transactions, + * or a transaction has not started). + */ + function rollback(&$model) { + if (parent::rollback($model)) { + return $this->execute('ROLLBACK TRAN'); + } + return false; + } +/** + * Returns a formatted error message from previous database operation. + * + * @todo not implemented + * @return string Error message with error number + */ + function lastError() { + 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 sybase_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 @sybase_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) { + $result=$this->fetchRow('SELECT @@IDENTITY'); + return $result[0]; + } +/** + * 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('datetime', 'smalldatetime'))) { + return 'datetime'; + } elseif (in_array($col, array('int', 'bigint', 'smallint', 'tinyint'))) { + return 'integer'; + } elseif (in_array($col, array('float', 'double', 'real', 'decimal', 'money', 'numeric', 'smallmoney'))) { + return 'float'; + } elseif (strpos($col, 'text') !== false) { + return 'text'; + } elseif (in_array($col, array('char', 'nchar', 'nvarchar', 'string', 'varchar'))) { + return 'binary'; + } elseif (in_array($col, array('binary', 'image', 'varbinary'))) { + return 'binary'; + } + + return 'text'; + } +/** + * Enter description here... + * + * @param unknown_type $results + */ + function resultSet(&$results) { + $this->results =& $results; + $this->map = array(); + $num_fields = sybase_num_fields($results); + $index = 0; + $j = 0; + + while ($j < $num_fields) { + + $column = sybase_fetch_field($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 = sybase_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; + } + } +} +?> \ No newline at end of file diff --git a/cake/libs/model/datasources/dbo_source.php b/cake/libs/model/datasources/dbo_source.php new file mode 100755 index 0000000..63d2577 --- /dev/null +++ b/cake/libs/model/datasources/dbo_source.php @@ -0,0 +1,2461 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Short description for file. + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.model.datasources + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +App::import('Core', array('Set', 'String')); + +/** + * DboSource + * + * Creates DBO-descendant objects from a given db connection configuration + * + * @package cake + * @subpackage cake.cake.libs.model.datasources + */ +class DboSource extends DataSource { +/** + * Description string for this Database Data Source. + * + * @var unknown_type + */ + var $description = "Database Data Source"; +/** + * index definition, standard cake, primary, index, unique + * + * @var array + */ + var $index = array('PRI' => 'primary', 'MUL' => 'index', 'UNI' => 'unique'); +/** + * Database keyword used to assign aliases to identifiers. + * + * @var string + */ + var $alias = 'AS '; +/** + * Caches fields quoted in DboSource::name() + * + * @var array + */ + var $fieldCache = array(); +/** + * Bypass automatic adding of joined fields/associations. + * + * @var boolean + */ + var $__bypass = false; +/** + * The set of valid SQL operations usable in a WHERE statement + * + * @var array + */ + var $__sqlOps = array('like', 'ilike', 'or', 'not', 'in', 'between', 'regexp', 'similar to'); +/** + * Index of basic SQL commands + * + * @var array + * @access protected + */ + var $_commands = array( + 'begin' => 'BEGIN', + 'commit' => 'COMMIT', + 'rollback' => 'ROLLBACK' + ); +/** + * Constructor + */ + function __construct($config = null, $autoConnect = true) { + if (!isset($config['prefix'])) { + $config['prefix'] = ''; + } + parent::__construct($config); + $this->fullDebug = Configure::read() > 1; + + if ($autoConnect) { + return $this->connect(); + } else { + return true; + } + } +/** + * Reconnects to database server with optional new settings + * + * @param array $config An array defining the new configuration settings + * @return boolean True on success, false on failure + */ + function reconnect($config = null) { + $this->disconnect(); + $this->setConfig($config); + $this->_sources = null; + + return $this->connect(); + } +/** + * Prepares a value, or an array of values for database queries by quoting and escaping them. + * + * @param mixed $data A value or an array of values to prepare. + * @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 mixed Prepared value or array of values. + */ + function value($data, $column = null, $read = true) { + if (is_array($data) && !empty($data)) { + return array_map( + array(&$this, 'value'), + $data, array_fill(0, count($data), $column), array_fill(0, count($data), $read) + ); + } elseif (is_object($data) && isset($data->type)) { + if ($data->type == 'identifier') { + return $this->name($data->value); + } elseif ($data->type == 'expression') { + return $data->value; + } + } elseif (in_array($data, array('{$__cakeID__$}', '{$__cakeForeignKey__$}'), true)) { + return $data; + } else { + return null; + } + } +/** + * Returns an object to represent a database identifier in a query + * + * @param string $identifier + * @return object An object representing a database identifier to be used in a query + */ + function identifier($identifier) { + $obj = new stdClass(); + $obj->type = 'identifier'; + $obj->value = $identifier; + return $obj; + } +/** + * Returns an object to represent a database expression in a query + * + * @param string $expression + * @return object An object representing a database expression to be used in a query + */ + function expression($expression) { + $obj = new stdClass(); + $obj->type = 'expression'; + $obj->value = $expression; + return $obj; + } +/** + * Executes given SQL statement. + * + * @param string $sql SQL statement + * @return unknown + */ + function rawQuery($sql) { + $this->took = $this->error = $this->numRows = false; + return $this->execute($sql); + } +/** + * Queries the database with given SQL statement, and obtains some metadata about the result + * (rows affected, timing, any errors, number of rows in resultset). The query is also logged. + * If DEBUG is set, the log is shown all the time, else it is only shown on errors. + * + * @param string $sql + * @param array $options + * @return mixed Resource or object representing the result set, or false on failure + */ + function execute($sql, $options = array()) { + $defaults = array('stats' => true, 'log' => $this->fullDebug); + $options = array_merge($defaults, $options); + + if ($options['stats']) { + $t = getMicrotime(); + $this->_result = $this->_execute($sql); + $this->took = round((getMicrotime() - $t) * 1000, 0); + $this->affected = $this->lastAffected(); + $this->error = $this->lastError(); + $this->numRows = $this->lastNumRows(); + } + + if ($options['log']) { + $this->logQuery($sql); + } + + if ($this->error) { + $this->showQuery($sql); + return false; + } + return $this->_result; + } +/** + * DataSource Query abstraction + * + * @return resource Result resource identifier + */ + function query() { + $args = func_get_args(); + $fields = null; + $order = null; + $limit = null; + $page = null; + $recursive = null; + + if (count($args) == 1) { + return $this->fetchAll($args[0]); + + } elseif (count($args) > 1 && (strpos(strtolower($args[0]), 'findby') === 0 || strpos(strtolower($args[0]), 'findallby') === 0)) { + $params = $args[1]; + + if (strpos(strtolower($args[0]), 'findby') === 0) { + $all = false; + $field = Inflector::underscore(preg_replace('/^findBy/i', '', $args[0])); + } else { + $all = true; + $field = Inflector::underscore(preg_replace('/^findAllBy/i', '', $args[0])); + } + + $or = (strpos($field, '_or_') !== false); + if ($or) { + $field = explode('_or_', $field); + } else { + $field = explode('_and_', $field); + } + $off = count($field) - 1; + + if (isset($params[1 + $off])) { + $fields = $params[1 + $off]; + } + + if (isset($params[2 + $off])) { + $order = $params[2 + $off]; + } + + if (!array_key_exists(0, $params)) { + return false; + } + + $c = 0; + $conditions = array(); + + foreach ($field as $f) { + $conditions[$args[2]->alias . '.' . $f] = $params[$c]; + $c++; + } + + if ($or) { + $conditions = array('OR' => $conditions); + } + + if ($all) { + if (isset($params[3 + $off])) { + $limit = $params[3 + $off]; + } + + if (isset($params[4 + $off])) { + $page = $params[4 + $off]; + } + + if (isset($params[5 + $off])) { + $recursive = $params[5 + $off]; + } + return $args[2]->find('all', compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive')); + } else { + if (isset($params[3 + $off])) { + $recursive = $params[3 + $off]; + } + return $args[2]->find('first', compact('conditions', 'fields', 'order', 'recursive')); + } + } else { + if (isset($args[1]) && $args[1] === true) { + return $this->fetchAll($args[0], true); + } else if (isset($args[1]) && !is_array($args[1]) ) { + return $this->fetchAll($args[0], false); + } else if (isset($args[1]) && is_array($args[1])) { + $offset = 0; + if (isset($args[2])) { + $cache = $args[2]; + } else { + $cache = true; + } + $args[1] = array_map(array(&$this, 'value'), $args[1]); + return $this->fetchAll(String::insert($args[0], $args[1]), $cache); + } + } + } +/** + * Returns a row from current resultset as an array + * + * @return array The fetched row as an array + */ + function fetchRow($sql = null) { + if (!empty($sql) && is_string($sql) && strlen($sql) > 5) { + if (!$this->execute($sql)) { + return null; + } + } + + if ($this->hasResult()) { + $this->resultSet($this->_result); + $resultRow = $this->fetchResult(); + return $resultRow; + } else { + return null; + } + } +/** + * 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 fetchAll($sql, $cache = true, $modelName = null) { + if ($cache && isset($this->_queryCache[$sql])) { + if (preg_match('/^\s*select/i', $sql)) { + return $this->_queryCache[$sql]; + } + } + + if ($this->execute($sql)) { + $out = array(); + + $first = $this->fetchRow(); + if ($first != null) { + $out[] = $first; + } + while ($this->hasResult() && $item = $this->fetchResult()) { + $out[] = $item; + } + + if ($cache) { + if (strpos(trim(strtolower($sql)), 'select') !== false) { + $this->_queryCache[$sql] = $out; + } + } + return $out; + + } else { + return false; + } + } +/** + * Returns a single field of the first of query results for a given SQL query, or false if empty. + * + * @param string $name Name of the field + * @param string $sql SQL query + * @return unknown + */ + function field($name, $sql) { + $data = $this->fetchRow($sql); + + if (!isset($data[$name]) || empty($data[$name])) { + return false; + } else { + return $data[$name]; + } + } +/** + * Returns a quoted name of $data for use in an SQL statement. + * Strips fields out of SQL functions before quoting. + * + * @param string $data + * @return string SQL field + */ + function name($data) { + if ($data == '*') { + return '*'; + } + if (is_object($data) && isset($data->type)) { + return $data->value; + } + $array = is_array($data); + $data = (array)$data; + $count = count($data); + + for ($i = 0; $i < $count; $i++) { + if ($data[$i] == '*') { + continue; + } + if (strpos($data[$i], '(') !== false && preg_match_all('/([^(]*)\((.*)\)(.*)/', $data[$i], $fields)) { + $fields = Set::extract($fields, '{n}.0'); + + if (!empty($fields[1])) { + if (!empty($fields[2])) { + $data[$i] = $fields[1] . '(' . $this->name($fields[2]) . ')' . $fields[3]; + } else { + $data[$i] = $fields[1] . '()' . $fields[3]; + } + } + } + $data[$i] = str_replace('.', $this->endQuote . '.' . $this->startQuote, $data[$i]); + $data[$i] = $this->startQuote . $data[$i] . $this->endQuote; + $data[$i] = str_replace($this->startQuote . $this->startQuote, $this->startQuote, $data[$i]); + $data[$i] = str_replace($this->startQuote . '(', '(', $data[$i]); + $data[$i] = str_replace(')' . $this->startQuote, ')', $data[$i]); + $alias = !empty($this->alias) ? $this->alias : 'AS '; + + if (preg_match('/\s+' . $alias . '\s*/', $data[$i])) { + if (preg_match('/\w+\s+' . $alias . '\s*/', $data[$i])) { + $quoted = $this->endQuote . ' ' . $alias . $this->startQuote; + $data[$i] = str_replace(' ' . $alias, $quoted, $data[$i]); + } else { + $quoted = $alias . $this->startQuote; + $data[$i] = str_replace($alias, $quoted, $data[$i]) . $this->endQuote; + } + } + + if (!empty($this->endQuote) && $this->endQuote == $this->startQuote) { + if (substr_count($data[$i], $this->endQuote) % 2 == 1) { + if (substr($data[$i], -2) == $this->endQuote . $this->endQuote) { + $data[$i] = substr($data[$i], 0, -1); + } else { + $data[$i] = trim($data[$i], $this->endQuote); + } + } + } + if (strpos($data[$i], '*')) { + $data[$i] = str_replace($this->endQuote . '*' . $this->endQuote, '*', $data[$i]); + } + $data[$i] = str_replace($this->endQuote . $this->endQuote, $this->endQuote, $data[$i]); + } + return (!$array) ? $data[0] : $data; + } +/** + * Checks if it's connected to the database + * + * @return boolean True if the database is connected, else false + */ + function isConnected() { + return $this->connected; + } +/** + * Checks if the result is valid + * + * @return boolean True if the result is valid else false + */ + function hasResult() { + return is_resource($this->_result); + } +/** + * Outputs the contents of the queries log. + * + * @param boolean $sorted + */ + function showLog($sorted = false) { + if ($sorted) { + $log = sortByKey($this->_queriesLog, 'took', 'desc', SORT_NUMERIC); + } else { + $log = $this->_queriesLog; + } + + if ($this->_queriesCnt > 1) { + $text = 'queries'; + } else { + $text = 'query'; + } + + if (PHP_SAPI != 'cli') { + print ("<table class=\"cake-sql-log\" id=\"cakeSqlLog_" . preg_replace('/[^A-Za-z0-9_]/', '_', uniqid(time(), true)) . "\" summary=\"Cake SQL Log\" cellspacing=\"0\" border = \"0\">\n<caption>({$this->configKeyName}) {$this->_queriesCnt} {$text} took {$this->_queriesTime} ms</caption>\n"); + print ("<thead>\n<tr><th>Nr</th><th>Query</th><th>Error</th><th>Affected</th><th>Num. rows</th><th>Took (ms)</th></tr>\n</thead>\n<tbody>\n"); + + foreach ($log as $k => $i) { + print ("<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"); + } + print ("</tbody></table>\n"); + } else { + foreach ($log as $k => $i) { + print (($k + 1) . ". {$i['query']} {$i['error']}\n"); + } + } + } +/** + * Log given SQL query. + * + * @param string $sql SQL statement + * @todo: Add hook to log errors instead of returning false + */ + function logQuery($sql) { + $this->_queriesCnt++; + $this->_queriesTime += $this->took; + $this->_queriesLog[] = array( + 'query' => $sql, + 'error' => $this->error, + 'affected' => $this->affected, + 'numRows' => $this->numRows, + 'took' => $this->took + ); + if (count($this->_queriesLog) > $this->_queriesLogMax) { + array_pop($this->_queriesLog); + } + if ($this->error) { + return false; + } + } +/** + * Output information about an SQL query. The SQL statement, number of rows in resultset, + * and execution time in microseconds. If the query fails, an error is output instead. + * + * @param string $sql Query to show information on. + */ + function showQuery($sql) { + $error = $this->error; + if (strlen($sql) > 200 && !$this->fullDebug && Configure::read() > 1) { + $sql = substr($sql, 0, 200) . '[...]'; + } + if (Configure::read() > 0) { + $out = null; + if ($error) { + trigger_error("<span style = \"color:Red;text-align:left\"><b>SQL Error:</b> {$this->error}</span>", E_USER_WARNING); + } else { + $out = ("<small>[Aff:{$this->affected} Num:{$this->numRows} Took:{$this->took}ms]</small>"); + } + pr(sprintf("<p style = \"text-align:left\"><b>Query:</b> %s %s</p>", $sql, $out)); + } + } +/** + * Gets full table name including prefix + * + * @param mixed $model + * @param boolean $quote + * @return string Full quoted table name + */ + function fullTableName($model, $quote = true) { + if (is_object($model)) { + $table = $model->tablePrefix . $model->table; + } elseif (isset($this->config['prefix'])) { + $table = $this->config['prefix'] . strval($model); + } else { + $table = strval($model); + } + if ($quote) { + return $this->name($table); + } + return $table; + } +/** + * The "C" in CRUD + * + * @param Model $model + * @param array $fields + * @param array $values + * @return boolean Success + */ + function create(&$model, $fields = null, $values = null) { + $id = null; + + if ($fields == null) { + unset($fields, $values); + $fields = array_keys($model->data); + $values = array_values($model->data); + } + $count = count($fields); + + for ($i = 0; $i < $count; $i++) { + $valueInsert[] = $this->value($values[$i], $model->getColumnType($fields[$i]), false); + } + for ($i = 0; $i < $count; $i++) { + $fieldInsert[] = $this->name($fields[$i]); + if ($fields[$i] == $model->primaryKey) { + $id = $values[$i]; + } + } + $query = array( + 'table' => $this->fullTableName($model), + 'fields' => join(', ', $fieldInsert), + 'values' => join(', ', $valueInsert) + ); + + if ($this->execute($this->renderStatement('create', $query))) { + if (empty($id)) { + $id = $this->lastInsertId($this->fullTableName($model, false), $model->primaryKey); + } + $model->setInsertID($id); + $model->id = $id; + return true; + } else { + $model->onError(); + return false; + } + } +/** + * The "R" in CRUD + * + * @param Model $model + * @param array $queryData + * @param integer $recursive Number of levels of association + * @return unknown + */ + function read(&$model, $queryData = array(), $recursive = null) { + $queryData = $this->__scrubQueryData($queryData); + + $null = null; + $array = array(); + $linkedModels = array(); + $this->__bypass = false; + $this->__booleans = array(); + + if ($recursive === null && isset($queryData['recursive'])) { + $recursive = $queryData['recursive']; + } + + if (!is_null($recursive)) { + $_recursive = $model->recursive; + $model->recursive = $recursive; + } + + if (!empty($queryData['fields'])) { + $this->__bypass = true; + $queryData['fields'] = $this->fields($model, null, $queryData['fields']); + } else { + $queryData['fields'] = $this->fields($model); + } + + $_associations = $model->__associations; + + if ($model->recursive == -1) { + $_associations = array(); + } else if ($model->recursive == 0) { + unset($_associations[2], $_associations[3]); + } + + foreach ($_associations as $type) { + foreach ($model->{$type} as $assoc => $assocData) { + $linkModel =& $model->{$assoc}; + $external = isset($assocData['external']); + + if ($model->useDbConfig == $linkModel->useDbConfig) { + if (true === $this->generateAssociationQuery($model, $linkModel, $type, $assoc, $assocData, $queryData, $external, $null)) { + $linkedModels[$type . '/' . $assoc] = true; + } + } + } + } + + $query = $this->generateAssociationQuery($model, $null, null, null, null, $queryData, false, $null); + + $resultSet = $this->fetchAll($query, $model->cacheQueries, $model->alias); + + if ($resultSet === false) { + $model->onError(); + return false; + } + + $filtered = $this->__filterResults($resultSet, $model); + + if ($model->recursive > -1) { + foreach ($_associations as $type) { + foreach ($model->{$type} as $assoc => $assocData) { + $linkModel =& $model->{$assoc}; + + if (empty($linkedModels[$type . '/' . $assoc])) { + if ($model->useDbConfig == $linkModel->useDbConfig) { + $db =& $this; + } else { + $db =& ConnectionManager::getDataSource($linkModel->useDbConfig); + } + } elseif ($model->recursive > 1 && ($type == 'belongsTo' || $type == 'hasOne')) { + $db =& $this; + } + + if (isset($db)) { + $stack = array($assoc); + $db->queryAssociation($model, $linkModel, $type, $assoc, $assocData, $array, true, $resultSet, $model->recursive - 1, $stack); + unset($db); + } + } + } + $this->__filterResults($resultSet, $model, $filtered); + } + + if (!is_null($recursive)) { + $model->recursive = $_recursive; + } + return $resultSet; + } +/** + * Private method. Passes association results thru afterFind filters of corresponding model + * + * @param array $results Reference of resultset to be filtered + * @param object $model Instance of model to operate against + * @param array $filtered List of classes already filtered, to be skipped + * @return return + */ + function __filterResults(&$results, &$model, $filtered = array()) { + $filtering = array(); + $count = count($results); + + for ($i = 0; $i < $count; $i++) { + if (is_array($results[$i])) { + $classNames = array_keys($results[$i]); + $count2 = count($classNames); + + for ($j = 0; $j < $count2; $j++) { + $className = $classNames[$j]; + if ($model->alias != $className && !in_array($className, $filtered)) { + if (!in_array($className, $filtering)) { + $filtering[] = $className; + } + + if (isset($model->{$className}) && is_object($model->{$className})) { + $data = $model->{$className}->afterFind(array(array($className => $results[$i][$className])), false); + } + if (isset($data[0][$className])) { + $results[$i][$className] = $data[0][$className]; + } + } + } + } + } + return $filtering; + } +/** + * Enter description here... + * + * @param Model $model + * @param unknown_type $linkModel + * @param string $type Association type + * @param unknown_type $association + * @param unknown_type $assocData + * @param unknown_type $queryData + * @param unknown_type $external + * @param unknown_type $resultSet + * @param integer $recursive Number of levels of association + * @param array $stack + */ + function queryAssociation(&$model, &$linkModel, $type, $association, $assocData, &$queryData, $external = false, &$resultSet, $recursive, $stack) { + if ($query = $this->generateAssociationQuery($model, $linkModel, $type, $association, $assocData, $queryData, $external, $resultSet)) { + if (!isset($resultSet) || !is_array($resultSet)) { + if (Configure::read() > 0) { + echo '<div style = "font: Verdana bold 12px; color: #FF0000">' . sprintf(__('SQL Error in model %s:', true), $model->alias) . ' '; + if (isset($this->error) && $this->error != null) { + echo $this->error; + } + echo '</div>'; + } + return null; + } + $count = count($resultSet); + + if ($type === 'hasMany' && empty($assocData['limit']) && !empty($assocData['foreignKey'])) { + $ins = $fetch = array(); + for ($i = 0; $i < $count; $i++) { + if ($in = $this->insertQueryData('{$__cakeID__$}', $resultSet[$i], $association, $assocData, $model, $linkModel, $stack)) { + $ins[] = $in; + } + } + + if (!empty($ins)) { + $fetch = $this->fetchAssociated($model, $query, $ins); + } + + if (!empty($fetch) && is_array($fetch)) { + if ($recursive > 0) { + foreach ($linkModel->__associations as $type1) { + foreach ($linkModel->{$type1} as $assoc1 => $assocData1) { + $deepModel =& $linkModel->{$assoc1}; + $tmpStack = $stack; + $tmpStack[] = $assoc1; + + if ($linkModel->useDbConfig === $deepModel->useDbConfig) { + $db =& $this; + } else { + $db =& ConnectionManager::getDataSource($deepModel->useDbConfig); + } + $db->queryAssociation($linkModel, $deepModel, $type1, $assoc1, $assocData1, $queryData, true, $fetch, $recursive - 1, $tmpStack); + } + } + } + } + $this->__filterResults($fetch, $model); + return $this->__mergeHasMany($resultSet, $fetch, $association, $model, $linkModel, $recursive); + } elseif ($type === 'hasAndBelongsToMany') { + $ins = $fetch = array(); + for ($i = 0; $i < $count; $i++) { + if ($in = $this->insertQueryData('{$__cakeID__$}', $resultSet[$i], $association, $assocData, $model, $linkModel, $stack)) { + $ins[] = $in; + } + } + if (!empty($ins)) { + if (count($ins) > 1) { + $query = str_replace('{$__cakeID__$}', '(' .join(', ', $ins) .')', $query); + $query = str_replace('= (', 'IN (', $query); + $query = str_replace('= (', 'IN (', $query); + } else { + $query = str_replace('{$__cakeID__$}',$ins[0], $query); + } + + $query = str_replace(' WHERE 1 = 1', '', $query); + } + + $foreignKey = $model->hasAndBelongsToMany[$association]['foreignKey']; + $joinKeys = array($foreignKey, $model->hasAndBelongsToMany[$association]['associationForeignKey']); + list($with, $habtmFields) = $model->joinModel($model->hasAndBelongsToMany[$association]['with'], $joinKeys); + $habtmFieldsCount = count($habtmFields); + $q = $this->insertQueryData($query, null, $association, $assocData, $model, $linkModel, $stack); + + if ($q != false) { + $fetch = $this->fetchAll($q, $model->cacheQueries, $model->alias); + } else { + $fetch = null; + } + } + + for ($i = 0; $i < $count; $i++) { + $row =& $resultSet[$i]; + + if ($type !== 'hasAndBelongsToMany') { + $q = $this->insertQueryData($query, $resultSet[$i], $association, $assocData, $model, $linkModel, $stack); + if ($q != false) { + $fetch = $this->fetchAll($q, $model->cacheQueries, $model->alias); + } else { + $fetch = null; + } + } + $selfJoin = false; + + if ($linkModel->name === $model->name) { + $selfJoin = true; + } + + if (!empty($fetch) && is_array($fetch)) { + if ($recursive > 0) { + foreach ($linkModel->__associations as $type1) { + foreach ($linkModel->{$type1} as $assoc1 => $assocData1) { + $deepModel =& $linkModel->{$assoc1}; + + if (($type1 === 'belongsTo') || ($deepModel->alias === $model->alias && $type === 'belongsTo') || ($deepModel->alias != $model->alias)) { + $tmpStack = $stack; + $tmpStack[] = $assoc1; + if ($linkModel->useDbConfig == $deepModel->useDbConfig) { + $db =& $this; + } else { + $db =& ConnectionManager::getDataSource($deepModel->useDbConfig); + } + $db->queryAssociation($linkModel, $deepModel, $type1, $assoc1, $assocData1, $queryData, true, $fetch, $recursive - 1, $tmpStack); + } + } + } + } + if ($type == 'hasAndBelongsToMany') { + $uniqueIds = $merge = array(); + + foreach ($fetch as $j => $data) { + if ( + (isset($data[$with]) && $data[$with][$foreignKey] === $row[$model->alias][$model->primaryKey]) + ) { + if ($habtmFieldsCount <= 2) { + unset($data[$with]); + } + $merge[] = $data; + } + } + if (empty($merge) && !isset($row[$association])) { + $row[$association] = $merge; + } else { + $this->__mergeAssociation($resultSet[$i], $merge, $association, $type); + } + } else { + $this->__mergeAssociation($resultSet[$i], $fetch, $association, $type, $selfJoin); + } + if (isset($resultSet[$i][$association])) { + $resultSet[$i][$association] = $linkModel->afterFind($resultSet[$i][$association]); + } + } else { + $tempArray[0][$association] = false; + $this->__mergeAssociation($resultSet[$i], $tempArray, $association, $type, $selfJoin); + } + } + } + } +/** + * A more efficient way to fetch associations. Woohoo! + * + * @param model $model Primary model object + * @param string $query Association query + * @param array $ids Array of IDs of associated records + * @return array Association results + */ + function fetchAssociated($model, $query, $ids) { + $query = str_replace('{$__cakeID__$}', join(', ', $ids), $query); + if (count($ids) > 1) { + $query = str_replace('= (', 'IN (', $query); + $query = str_replace('= (', 'IN (', $query); + } + return $this->fetchAll($query, $model->cacheQueries, $model->alias); + } +/** + * mergeHasMany - Merge the results of hasMany relations. + * + * + * @param array $resultSet Data to merge into + * @param array $merge Data to merge + * @param string $association Name of Model being Merged + * @param object $model Model being merged onto + * @param object $linkModel Model being merged + * @return void + **/ + function __mergeHasMany(&$resultSet, $merge, $association, &$model, &$linkModel) { + foreach ($resultSet as $i => $value) { + $count = 0; + $merged[$association] = array(); + foreach ($merge as $j => $data) { + if (isset($value[$model->alias]) && $value[$model->alias][$model->primaryKey] === $data[$association][$model->hasMany[$association]['foreignKey']]) { + if (count($data) > 1) { + $data = array_merge($data[$association], $data); + unset($data[$association]); + foreach ($data as $key => $name) { + if (is_numeric($key)) { + $data[$association][] = $name; + unset($data[$key]); + } + } + $merged[$association][] = $data; + } else { + $merged[$association][] = $data[$association]; + } + } + $count++; + } + if (isset($value[$model->alias])) { + $resultSet[$i] = Set::pushDiff($resultSet[$i], $merged); + unset($merged); + } + } + } +/** + * Enter description here... + * + * @param unknown_type $data + * @param unknown_type $merge + * @param unknown_type $association + * @param unknown_type $type + * @param boolean $selfJoin + */ + function __mergeAssociation(&$data, $merge, $association, $type, $selfJoin = false) { + if (isset($merge[0]) && !isset($merge[0][$association])) { + $association = Inflector::pluralize($association); + } + + if ($type == 'belongsTo' || $type == 'hasOne') { + if (isset($merge[$association])) { + $data[$association] = $merge[$association][0]; + } else { + if (count($merge[0][$association]) > 1) { + foreach ($merge[0] as $assoc => $data2) { + if ($assoc != $association) { + $merge[0][$association][$assoc] = $data2; + } + } + } + if (!isset($data[$association])) { + if ($merge[0][$association] != null) { + $data[$association] = $merge[0][$association]; + } else { + $data[$association] = array(); + } + } else { + if (is_array($merge[0][$association])) { + foreach ($data[$association] as $k => $v) { + if (!is_array($v)) { + $dataAssocTmp[$k] = $v; + } + } + + foreach ($merge[0][$association] as $k => $v) { + if (!is_array($v)) { + $mergeAssocTmp[$k] = $v; + } + } + $dataKeys = array_keys($data); + $mergeKeys = array_keys($merge[0]); + + if ($mergeKeys[0] === $dataKeys[0] || $mergeKeys === $dataKeys) { + $data[$association][$association] = $merge[0][$association]; + } else { + $diff = Set::diff($dataAssocTmp, $mergeAssocTmp); + $data[$association] = array_merge($merge[0][$association], $diff); + } + } elseif ($selfJoin && array_key_exists($association, $merge[0])) { + $data[$association] = array_merge($data[$association], array($association => array())); + } + } + } + } else { + if (isset($merge[0][$association]) && $merge[0][$association] === false) { + if (!isset($data[$association])) { + $data[$association] = array(); + } + } else { + foreach ($merge as $i => $row) { + if (count($row) == 1) { + if (empty($data[$association]) || (isset($data[$association]) && !in_array($row[$association], $data[$association]))) { + $data[$association][] = $row[$association]; + } + } else if (!empty($row)) { + $tmp = array_merge($row[$association], $row); + unset($tmp[$association]); + $data[$association][] = $tmp; + } + } + } + } + } +/** + * Generates an array representing a query or part of a query from a single model or two associated models + * + * @param Model $model + * @param Model $linkModel + * @param string $type + * @param string $association + * @param array $assocData + * @param array $queryData + * @param boolean $external + * @param array $resultSet + * @return mixed + */ + function generateAssociationQuery(&$model, &$linkModel, $type, $association = null, $assocData = array(), &$queryData, $external = false, &$resultSet) { + $queryData = $this->__scrubQueryData($queryData); + $assocData = $this->__scrubQueryData($assocData); + + if (empty($queryData['fields'])) { + $queryData['fields'] = $this->fields($model, $model->alias); + } elseif (!empty($model->hasMany) && $model->recursive > -1) { + $assocFields = $this->fields($model, $model->alias, array("{$model->alias}.{$model->primaryKey}")); + $passedFields = $this->fields($model, $model->alias, $queryData['fields']); + + if (count($passedFields) === 1) { + $match = strpos($passedFields[0], $assocFields[0]); + $match1 = strpos($passedFields[0], 'COUNT('); + if ($match === false && $match1 === false) { + $queryData['fields'] = array_merge($passedFields, $assocFields); + } else { + $queryData['fields'] = $passedFields; + } + } else { + $queryData['fields'] = array_merge($passedFields, $assocFields); + } + unset($assocFields, $passedFields); + } + + if ($linkModel == null) { + return $this->buildStatement( + array( + 'fields' => array_unique($queryData['fields']), + 'table' => $this->fullTableName($model), + 'alias' => $model->alias, + 'limit' => $queryData['limit'], + 'offset' => $queryData['offset'], + 'joins' => $queryData['joins'], + 'conditions' => $queryData['conditions'], + 'order' => $queryData['order'], + 'group' => $queryData['group'] + ), + $model + ); + } + if ($external && !empty($assocData['finderQuery'])) { + return $assocData['finderQuery']; + } + + $alias = $association; + $self = ($model->name == $linkModel->name); + $fields = array(); + + if ((!$external && in_array($type, array('hasOne', 'belongsTo')) && $this->__bypass === false) || $external) { + $fields = $this->fields($linkModel, $alias, $assocData['fields']); + } + if (empty($assocData['offset']) && !empty($assocData['page'])) { + $assocData['offset'] = ($assocData['page'] - 1) * $assocData['limit']; + } + $assocData['limit'] = $this->limit($assocData['limit'], $assocData['offset']); + + switch ($type) { + case 'hasOne': + case 'belongsTo': + $conditions = $this->__mergeConditions( + $assocData['conditions'], + $this->getConstraint($type, $model, $linkModel, $alias, array_merge($assocData, compact('external', 'self'))) + ); + + if (!$self && $external) { + foreach ($conditions as $key => $condition) { + if (is_numeric($key) && strpos($condition, $model->alias . '.') !== false) { + unset($conditions[$key]); + } + } + } + + if ($external) { + $query = array_merge($assocData, array( + 'conditions' => $conditions, + 'table' => $this->fullTableName($linkModel), + 'fields' => $fields, + 'alias' => $alias, + 'group' => null + )); + $query = array_merge(array('order' => $assocData['order'], 'limit' => $assocData['limit']), $query); + } else { + $join = array( + 'table' => $this->fullTableName($linkModel), + 'alias' => $alias, + 'type' => isset($assocData['type']) ? $assocData['type'] : 'LEFT', + 'conditions' => trim($this->conditions($conditions, true, false, $model)) + ); + $queryData['fields'] = array_merge($queryData['fields'], $fields); + + if (!empty($assocData['order'])) { + $queryData['order'][] = $assocData['order']; + } + if (!in_array($join, $queryData['joins'])) { + $queryData['joins'][] = $join; + } + return true; + } + break; + case 'hasMany': + $assocData['fields'] = $this->fields($linkModel, $alias, $assocData['fields']); + if (!empty($assocData['foreignKey'])) { + $assocData['fields'] = array_merge($assocData['fields'], $this->fields($linkModel, $alias, array("{$alias}.{$assocData['foreignKey']}"))); + } + $query = array( + 'conditions' => $this->__mergeConditions($this->getConstraint('hasMany', $model, $linkModel, $alias, $assocData), $assocData['conditions']), + 'fields' => array_unique($assocData['fields']), + 'table' => $this->fullTableName($linkModel), + 'alias' => $alias, + 'order' => $assocData['order'], + 'limit' => $assocData['limit'], + 'group' => null + ); + break; + case 'hasAndBelongsToMany': + $joinFields = array(); + $joinAssoc = null; + + if (isset($assocData['with']) && !empty($assocData['with'])) { + $joinKeys = array($assocData['foreignKey'], $assocData['associationForeignKey']); + list($with, $joinFields) = $model->joinModel($assocData['with'], $joinKeys); + + $joinTbl = $this->fullTableName($model->{$with}); + $joinAlias = $joinTbl; + + if (is_array($joinFields) && !empty($joinFields)) { + $joinFields = $this->fields($model->{$with}, $model->{$with}->alias, $joinFields); + $joinAssoc = $joinAlias = $model->{$with}->alias; + } else { + $joinFields = array(); + } + } else { + $joinTbl = $this->fullTableName($assocData['joinTable']); + $joinAlias = $joinTbl; + } + $query = array( + 'conditions' => $assocData['conditions'], + 'limit' => $assocData['limit'], + 'table' => $this->fullTableName($linkModel), + 'alias' => $alias, + 'fields' => array_merge($this->fields($linkModel, $alias, $assocData['fields']), $joinFields), + 'order' => $assocData['order'], + 'group' => null, + 'joins' => array(array( + 'table' => $joinTbl, + 'alias' => $joinAssoc, + 'conditions' => $this->getConstraint('hasAndBelongsToMany', $model, $linkModel, $joinAlias, $assocData, $alias) + )) + ); + break; + } + if (isset($query)) { + return $this->buildStatement($query, $model); + } + return null; + } +/** + * Returns a conditions array for the constraint between two models + * + * @param string $type Association type + * @param object $model Model object + * @param array $association Association array + * @return array Conditions array defining the constraint between $model and $association + */ + function getConstraint($type, $model, $linkModel, $alias, $assoc, $alias2 = null) { + $assoc = array_merge(array('external' => false, 'self' => false), $assoc); + + if (array_key_exists('foreignKey', $assoc) && empty($assoc['foreignKey'])) { + return array(); + } + + switch (true) { + case ($assoc['external'] && $type == 'hasOne'): + return array("{$alias}.{$assoc['foreignKey']}" => '{$__cakeID__$}'); + break; + case ($assoc['external'] && $type == 'belongsTo'): + return array("{$alias}.{$linkModel->primaryKey}" => '{$__cakeForeignKey__$}'); + break; + case (!$assoc['external'] && $type == 'hasOne'): + return array("{$alias}.{$assoc['foreignKey']}" => $this->identifier("{$model->alias}.{$model->primaryKey}")); + break; + case (!$assoc['external'] && $type == 'belongsTo'): + return array("{$model->alias}.{$assoc['foreignKey']}" => $this->identifier("{$alias}.{$linkModel->primaryKey}")); + break; + case ($type == 'hasMany'): + return array("{$alias}.{$assoc['foreignKey']}" => array('{$__cakeID__$}')); + break; + case ($type == 'hasAndBelongsToMany'): + return array( + array("{$alias}.{$assoc['foreignKey']}" => '{$__cakeID__$}'), + array("{$alias}.{$assoc['associationForeignKey']}" => $this->identifier("{$alias2}.{$linkModel->primaryKey}")) + ); + break; + } + return array(); + } +/** + * Builds and generates a JOIN statement from an array. Handles final clean-up before conversion. + * + * @param array $join An array defining a JOIN statement in a query + * @return string An SQL JOIN statement to be used in a query + * @see DboSource::renderJoinStatement() + * @see DboSource::buildStatement() + */ + function buildJoinStatement($join) { + $data = array_merge(array( + 'type' => null, + 'alias' => null, + 'table' => 'join_table', + 'conditions' => array() + ), $join); + + if (!empty($data['alias'])) { + $data['alias'] = $this->alias . $this->name($data['alias']); + } + if (!empty($data['conditions'])) { + $data['conditions'] = trim($this->conditions($data['conditions'], true, false)); + } + return $this->renderJoinStatement($data); + } +/** + * Builds and generates an SQL statement from an array. Handles final clean-up before conversion. + * + * @param array $query An array defining an SQL query + * @param object $model The model object which initiated the query + * @return string An executable SQL statement + * @see DboSource::renderStatement() + */ + function buildStatement($query, $model) { + $query = array_merge(array('offset' => null, 'joins' => array()), $query); + if (!empty($query['joins'])) { + $count = count($query['joins']); + for ($i = 0; $i < $count; $i++) { + if (is_array($query['joins'][$i])) { + $query['joins'][$i] = $this->buildJoinStatement($query['joins'][$i]); + } + } + } + return $this->renderStatement('select', array( + 'conditions' => $this->conditions($query['conditions'], true, true, $model), + 'fields' => join(', ', $query['fields']), + 'table' => $query['table'], + 'alias' => $this->alias . $this->name($query['alias']), + 'order' => $this->order($query['order']), + 'limit' => $this->limit($query['limit'], $query['offset']), + 'joins' => join(' ', $query['joins']), + 'group' => $this->group($query['group']) + )); + } +/** + * Renders a final SQL JOIN statement + * + * @param array $data + * @return string + */ + function renderJoinStatement($data) { + extract($data); + return trim("{$type} JOIN {$table} {$alias} ON ({$conditions})"); + } +/** + * Renders a final SQL statement by putting together the component parts in the correct order + * + * @param string $type + * @param array $data + * @return string + */ + function renderStatement($type, $data) { + extract($data); + $aliases = null; + + switch (strtolower($type)) { + case 'select': + return "SELECT {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$group} {$order} {$limit}"; + break; + case 'create': + return "INSERT INTO {$table} ({$fields}) VALUES ({$values})"; + break; + case 'update': + if (!empty($alias)) { + $aliases = "{$this->alias}{$alias} {$joins} "; + } + return "UPDATE {$table} {$aliases}SET {$fields} {$conditions}"; + break; + case 'delete': + if (!empty($alias)) { + $aliases = "{$this->alias}{$alias} {$joins} "; + } + return "DELETE {$alias} FROM {$table} {$aliases}{$conditions}"; + break; + case 'schema': + foreach (array('columns', 'indexes') as $var) { + if (is_array(${$var})) { + ${$var} = "\t" . join(",\n\t", array_filter(${$var})); + } + } + if (trim($indexes) != '') { + $columns .= ','; + } + return "CREATE TABLE {$table} (\n{$columns}{$indexes});"; + break; + case 'alter': + break; + } + } +/** + * Merges a mixed set of string/array conditions + * + * @return array + */ + function __mergeConditions($query, $assoc) { + if (empty($assoc)) { + return $query; + } + + if (is_array($query)) { + return array_merge((array)$assoc, $query); + } + + if (!empty($query)) { + $query = array($query); + if (is_array($assoc)) { + $query = array_merge($query, $assoc); + } else { + $query[] = $assoc; + } + return $query; + } + + return $assoc; + } +/** + * Generates and executes an SQL UPDATE statement for given model, fields, and values. + * For databases that do not support aliases in UPDATE queries. + * + * @param Model $model + * @param array $fields + * @param array $values + * @param mixed $conditions + * @return boolean Success + */ + function update(&$model, $fields = array(), $values = null, $conditions = null) { + if ($values == null) { + $combined = $fields; + } else { + $combined = array_combine($fields, $values); + } + $fields = join(', ', $this->_prepareUpdateFields($model, $combined, empty($conditions))); + + $alias = $joins = null; + $table = $this->fullTableName($model); + $conditions = $this->_matchRecords($model, $conditions); + + if ($conditions === false) { + return false; + } + $query = compact('table', 'alias', 'joins', 'fields', 'conditions'); + + if (!$this->execute($this->renderStatement('update', $query))) { + $model->onError(); + return false; + } + return true; + } +/** + * Quotes and prepares fields and values for an SQL UPDATE statement + * + * @param Model $model + * @param array $fields + * @param boolean $quoteValues If values should be quoted, or treated as SQL snippets + * @param boolean $alias Include the model alias in the field name + * @return array Fields and values, quoted and preparted + * @access protected + */ + function _prepareUpdateFields(&$model, $fields, $quoteValues = true, $alias = false) { + $quotedAlias = $this->startQuote . $model->alias . $this->endQuote; + + $updates = array(); + foreach ($fields as $field => $value) { + if ($alias && strpos($field, '.') === false) { + $quoted = $model->escapeField($field); + } elseif (!$alias && strpos($field, '.') !== false) { + $quoted = $this->name(str_replace($quotedAlias . '.', '', str_replace( + $model->alias . '.', '', $field + ))); + } else { + $quoted = $this->name($field); + } + + if ($value === null) { + $updates[] = $quoted . ' = NULL'; + continue; + } + $update = $quoted . ' = '; + + if ($quoteValues) { + $update .= $this->value($value, $model->getColumnType($field), false); + } elseif (!$alias) { + $update .= str_replace($quotedAlias . '.', '', str_replace( + $model->alias . '.', '', $value + )); + } else { + $update .= $value; + } + $updates[] = $update; + } + return $updates; + } +/** + * Generates and executes an SQL DELETE statement. + * For databases that do not support aliases in UPDATE queries. + * + * @param Model $model + * @param mixed $conditions + * @return boolean Success + */ + function delete(&$model, $conditions = null) { + $alias = $joins = null; + $table = $this->fullTableName($model); + $conditions = $this->_matchRecords($model, $conditions); + + if ($conditions === false) { + return false; + } + + if ($this->execute($this->renderStatement('delete', compact('alias', 'table', 'joins', 'conditions'))) === false) { + $model->onError(); + return false; + } + return true; + } +/** + * Gets a list of record IDs for the given conditions. Used for multi-record updates and deletes + * in databases that do not support aliases in UPDATE/DELETE queries. + * + * @param Model $model + * @param mixed $conditions + * @return array List of record IDs + * @access protected + */ + function _matchRecords(&$model, $conditions = null) { + if ($conditions === true) { + $conditions = $this->conditions(true); + } elseif ($conditions === null) { + $conditions = $this->conditions($this->defaultConditions($model, $conditions, false), true, true, $model); + } else { + $idList = $model->find('all', array( + 'fields' => "{$model->alias}.{$model->primaryKey}", + 'conditions' => $conditions + )); + + if (empty($idList)) { + return false; + } + $conditions = $this->conditions(array( + $model->primaryKey => Set::extract($idList, "{n}.{$model->alias}.{$model->primaryKey}") + )); + } + return $conditions; + } +/** + * Returns an array of SQL JOIN fragments from a model's associations + * + * @param object $model + * @return array + */ + function _getJoins($model) { + $join = array(); + $joins = array_merge($model->getAssociated('hasOne'), $model->getAssociated('belongsTo')); + + foreach ($joins as $assoc) { + if (isset($model->{$assoc}) && $model->useDbConfig == $model->{$assoc}->useDbConfig) { + $assocData = $model->getAssociated($assoc); + $join[] = $this->buildJoinStatement(array( + 'table' => $this->fullTableName($model->{$assoc}), + 'alias' => $assoc, + 'type' => isset($assocData['type']) ? $assocData['type'] : 'LEFT', + 'conditions' => trim($this->conditions( + $this->__mergeConditions($assocData['conditions'], $this->getConstraint($assocData['association'], $model, $model->{$assoc}, $assoc, $assocData)), + true, false, $model + )) + )); + } + } + return $join; + } +/** + * Returns an SQL calculation, i.e. COUNT() or MAX() + * + * @param model $model + * @param string $func Lowercase name of SQL function, i.e. 'count' or 'max' + * @param array $params Function parameters (any values must be quoted manually) + * @return string An SQL calculation function + * @access public + */ + function calculate(&$model, $func, $params = array()) { + $params = (array)$params; + + switch (strtolower($func)) { + case 'count': + if (!isset($params[0])) { + $params[0] = '*'; + } + if (!isset($params[1])) { + $params[1] = 'count'; + } + return 'COUNT(' . $this->name($params[0]) . ') AS ' . $this->name($params[1]); + case 'max': + case 'min': + if (!isset($params[1])) { + $params[1] = $params[0]; + } + return strtoupper($func) . '(' . $this->name($params[0]) . ') AS ' . $this->name($params[1]); + break; + } + } +/** + * 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('TRUNCATE TABLE ' . $this->fullTableName($table)); + } +/** + * Begin a transaction + * + * @param model $model + * @return boolean True on success, false on fail + * (i.e. if the database/model does not support transactions, + * or a transaction has not started). + */ + function begin(&$model) { + if (parent::begin($model) && $this->execute($this->_commands['begin'])) { + $this->_transactionStarted = true; + return true; + } + return false; + } +/** + * Commit a transaction + * + * @param model $model + * @return boolean True on success, false on fail + * (i.e. if the database/model does not support transactions, + * or a transaction has not started). + */ + function commit(&$model) { + if (parent::commit($model) && $this->execute($this->_commands['commit'])) { + $this->_transactionStarted = false; + return true; + } + return false; + } +/** + * Rollback a transaction + * + * @param model $model + * @return boolean True on success, false on fail + * (i.e. if the database/model does not support transactions, + * or a transaction has not started). + */ + function rollback(&$model) { + if (parent::rollback($model) && $this->execute($this->_commands['rollback'])) { + $this->_transactionStarted = false; + return true; + } + return false; + } +/** + * Creates a default set of conditions from the model if $conditions is null/empty. + * + * @param object $model + * @param mixed $conditions + * @param boolean $useAlias Use model aliases rather than table names when generating conditions + * @return mixed + */ + function defaultConditions(&$model, $conditions, $useAlias = true) { + if (!empty($conditions)) { + return $conditions; + } + if (!$model->exists()) { + return false; + } + $alias = $model->alias; + + if (!$useAlias) { + $alias = $this->fullTableName($model, false); + } + return array("{$alias}.{$model->primaryKey}" => $model->getID()); + } +/** + * Returns a key formatted like a string Model.fieldname(i.e. Post.title, or Country.name) + * + * @param unknown_type $model + * @param unknown_type $key + * @param unknown_type $assoc + * @return string + */ + function resolveKey($model, $key, $assoc = null) { + if (empty($assoc)) { + $assoc = $model->alias; + } + if (!strpos('.', $key)) { + return $this->name($model->alias) . '.' . $this->name($key); + } + return $key; + } +/** + * Private helper method to remove query metadata in given data array. + * + * @param array $data + * @return array + */ + function __scrubQueryData($data) { + foreach (array('conditions', 'fields', 'joins', 'order', 'limit', 'offset', 'group') as $key) { + if (!isset($data[$key]) || empty($data[$key])) { + $data[$key] = array(); + } + } + return $data; + } +/** + * Generates the fields list of an SQL query. + * + * @param Model $model + * @param string $alias Alias tablename + * @param mixed $fields + * @param boolean $quote If false, returns fields array unquoted + * @return array + */ + function fields(&$model, $alias = null, $fields = array(), $quote = true) { + if (empty($alias)) { + $alias = $model->alias; + } + if (empty($fields)) { + $fields = array_keys($model->schema()); + } elseif (!is_array($fields)) { + $fields = String::tokenize($fields); + } + $fields = array_values(array_filter($fields)); + + if (!$quote) { + return $fields; + } + $count = count($fields); + + if ($count >= 1 && !in_array($fields[0], array('*', 'COUNT(*)'))) { + for ($i = 0; $i < $count; $i++) { + if (preg_match('/^\(.*\)\s' . $this->alias . '.*/i', $fields[$i])){ + continue; + } elseif (!preg_match('/^.+\\(.*\\)/', $fields[$i])) { + $prepend = ''; + + if (strpos($fields[$i], 'DISTINCT') !== false) { + $prepend = 'DISTINCT '; + $fields[$i] = trim(str_replace('DISTINCT', '', $fields[$i])); + } + $dot = strpos($fields[$i], '.'); + + if ($dot === false) { + $prefix = !( + strpos($fields[$i], ' ') !== false || + strpos($fields[$i], '(') !== false + ); + $fields[$i] = $this->name(($prefix ? $alias . '.' : '') . $fields[$i]); + } else { + $value = array(); + $comma = strpos($fields[$i], ','); + if ($comma === false) { + $build = explode('.', $fields[$i]); + if (!Set::numeric($build)) { + $fields[$i] = $this->name($build[0] . '.' . $build[1]); + } + $comma = String::tokenize($fields[$i]); + foreach ($comma as $string) { + if (preg_match('/^[0-9]+\.[0-9]+$/', $string)) { + $value[] = $string; + } else { + $build = explode('.', $string); + $value[] = $this->name(trim($build[0]) . '.' . trim($build[1])); + } + } + $fields[$i] = implode(', ', $value); + } + } + $fields[$i] = $prepend . $fields[$i]; + } elseif (preg_match('/\(([\.\w]+)\)/', $fields[$i], $field)) { + if (isset($field[1])) { + if (strpos($field[1], '.') === false) { + $field[1] = $this->name($alias . '.' . $field[1]); + } else { + $field[0] = explode('.', $field[1]); + if (!Set::numeric($field[0])) { + $field[0] = join('.', array_map(array($this, 'name'), $field[0])); + $fields[$i] = preg_replace('/\(' . $field[1] . '\)/', '(' . $field[0] . ')', $fields[$i], 1); + } + } + } + } + } + } + return array_unique($fields); + } +/** + * Creates a WHERE clause by parsing given conditions data. + * + * @param mixed $conditions Array or string of conditions + * @param boolean $quoteValues If true, values should be quoted + * @param boolean $where If true, "WHERE " will be prepended to the return value + * @param Model $model A reference to the Model instance making the query + * @return string SQL fragment + */ + function conditions($conditions, $quoteValues = true, $where = true, $model = null) { + $clause = $out = ''; + + if ($where) { + $clause = ' WHERE '; + } + + if (is_array($conditions) && !empty($conditions)) { + $out = $this->conditionKeysToString($conditions, $quoteValues, $model); + + if (empty($out)) { + return $clause . ' 1 = 1'; + } + return $clause . join(' AND ', $out); + } + + if (empty($conditions) || trim($conditions) == '' || $conditions === true) { + return $clause . '1 = 1'; + } + $clauses = '/^WHERE\\x20|^GROUP\\x20BY\\x20|^HAVING\\x20|^ORDER\\x20BY\\x20/i'; + + if (preg_match($clauses, $conditions, $match)) { + $clause = ''; + } + if (trim($conditions) == '') { + $conditions = ' 1 = 1'; + } else { + $conditions = $this->__quoteFields($conditions); + } + return $clause . $conditions; + } +/** + * Creates a WHERE clause by parsing given conditions array. Used by DboSource::conditions(). + * + * @param array $conditions Array or string of conditions + * @param boolean $quoteValues If true, values should be quoted + * @param Model $model A reference to the Model instance making the query + * @return string SQL fragment + */ + function conditionKeysToString($conditions, $quoteValues = true, $model = null) { + $c = 0; + $out = array(); + $data = $columnType = null; + $bool = array('and', 'or', 'not', 'and not', 'or not', 'xor', '||', '&&'); + + foreach ($conditions as $key => $value) { + $join = ' AND '; + $not = null; + + if (is_array($value)) { + $valueInsert = ( + !empty($value) && + (substr_count($key, '?') == count($value) || substr_count($key, ':') == count($value)) + ); + } + + if (is_numeric($key) && empty($value)) { + continue; + } elseif (is_numeric($key) && is_string($value)) { + $out[] = $not . $this->__quoteFields($value); + } elseif ((is_numeric($key) && is_array($value)) || in_array(strtolower(trim($key)), $bool)) { + if (in_array(strtolower(trim($key)), $bool)) { + $join = ' ' . strtoupper($key) . ' '; + } else { + $key = $join; + } + $value = $this->conditionKeysToString($value, $quoteValues, $model); + + if (strpos($join, 'NOT') !== false) { + if (strtoupper(trim($key)) == 'NOT') { + $key = 'AND ' . trim($key); + } + $not = 'NOT '; + } + + if (empty($value[1])) { + if ($not) { + $out[] = $not . '(' . $value[0] . ')'; + } else { + $out[] = $value[0] ; + } + } else { + $out[] = '(' . $not . '(' . join(') ' . strtoupper($key) . ' (', $value) . '))'; + } + + } else { + if (is_object($value) && isset($value->type)) { + if ($value->type == 'identifier') { + $data .= $this->name($key) . ' = ' . $this->name($value->value); + } elseif ($value->type == 'expression') { + if (is_numeric($key)) { + $data .= $value->value; + } else { + $data .= $this->name($key) . ' = ' . $value->value; + } + } + } elseif (is_array($value) && !empty($value) && !$valueInsert) { + $keys = array_keys($value); + if (array_keys($value) === array_values(array_keys($value))) { + $count = count($value); + if ($count === 1) { + $data = $this->__quoteFields($key) . ' = ('; + } else { + $data = $this->__quoteFields($key) . ' IN ('; + } + if ($quoteValues || strpos($value[0], '-!') !== 0) { + if (is_object($model)) { + $columnType = $model->getColumnType($key); + } + $data .= join(', ', $this->value($value, $columnType)); + } + $data .= ')'; + } else { + $ret = $this->conditionKeysToString($value, $quoteValues, $model); + if (count($ret) > 1) { + $data = '(' . join(') AND (', $ret) . ')'; + } elseif (isset($ret[0])) { + $data = $ret[0]; + } + } + } elseif (is_numeric($key) && !empty($value)) { + $data = $this->__quoteFields($value); + } else { + $data = $this->__parseKey($model, trim($key), $value); + } + + if ($data != null) { + if (preg_match('/^\(\(\((.+)\)\)\)$/', $data)) { + $data = substr($data, 1, strlen($data) - 2); + } + $out[] = $data; + $data = null; + } + } + $c++; + } + return $out; + } +/** + * Extracts a Model.field identifier and an SQL condition operator from a string, formats + * and inserts values, and composes them into an SQL snippet. + * + * @param Model $model Model object initiating the query + * @param string $key An SQL key snippet containing a field and optional SQL operator + * @param mixed $value The value(s) to be inserted in the string + * @return string + * @access private + */ + function __parseKey($model, $key, $value) { + $operatorMatch = '/^((' . join(')|(', $this->__sqlOps); + $operatorMatch .= '\\x20)|<[>=]?(?![^>]+>)\\x20?|[>=!]{1,3}(?!<)\\x20?)/is'; + $bound = (strpos($key, '?') !== false || (is_array($value) && strpos($key, ':') !== false)); + + if (!strpos($key, ' ')) { + $operator = '='; + } else { + list($key, $operator) = explode(' ', trim($key), 2); + + if (!preg_match($operatorMatch, trim($operator)) && strpos($operator, ' ') !== false) { + $key = $key . ' ' . $operator; + $split = strrpos($key, ' '); + $operator = substr($key, $split); + $key = substr($key, 0, $split); + } + } + + + $type = (is_object($model) ? $model->getColumnType($key) : null); + + $null = ($value === null || (is_array($value) && empty($value))); + + if (strtolower($operator) === 'not') { + $data = $this->conditionKeysToString( + array($operator => array($key => $value)), true, $model + ); + return $data[0]; + } + + $value = $this->value($value, $type); + + if ($key !== '?') { + $isKey = (strpos($key, '(') !== false || strpos($key, ')') !== false); + $key = $isKey ? $this->__quoteFields($key) : $this->name($key); + } + + if ($bound) { + return String::insert($key . ' ' . trim($operator), $value); + } + + if (!preg_match($operatorMatch, trim($operator))) { + $operator .= ' ='; + } + $operator = trim($operator); + + if (is_array($value)) { + $value = join(', ', $value); + + switch ($operator) { + case '=': + $operator = 'IN'; + break; + case '!=': + case '<>': + $operator = 'NOT IN'; + break; + } + $value = "({$value})"; + } elseif ($null) { + switch ($operator) { + case '=': + $operator = 'IS'; + break; + case '!=': + case '<>': + $operator = 'IS NOT'; + break; + } + } + + return "{$key} {$operator} {$value}"; + } +/** + * Quotes Model.fields + * + * @param string $conditions + * @return string or false if no match + * @access private + */ + function __quoteFields($conditions) { + $start = $end = null; + $original = $conditions; + + if (!empty($this->startQuote)) { + $start = preg_quote($this->startQuote); + } + if (!empty($this->endQuote)) { + $end = preg_quote($this->endQuote); + } + $conditions = str_replace(array($start, $end), '', $conditions); + preg_match_all('/(?:[\'\"][^\'\"\\\]*(?:\\\.[^\'\"\\\]*)*[\'\"])|([a-z0-9_' . $start . $end . ']*\\.[a-z0-9_' . $start . $end . ']*)/i', $conditions, $replace, PREG_PATTERN_ORDER); + + if (isset($replace['1']['0'])) { + $pregCount = count($replace['1']); + + for ($i = 0; $i < $pregCount; $i++) { + if (!empty($replace['1'][$i]) && !is_numeric($replace['1'][$i])) { + $conditions = preg_replace('/\b' . preg_quote($replace['1'][$i]) . '\b/', $this->name($replace['1'][$i]), $conditions); + } + } + return $conditions; + } + return $original; + } +/** + * 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'; + } + + if ($offset) { + $rt .= ' ' . $offset . ','; + } + + $rt .= ' ' . $limit; + return $rt; + } + return null; + } +/** + * Returns an ORDER BY clause as a string. + * + * @param string $key Field reference, as a key (i.e. Post.title) + * @param string $direction Direction (ASC or DESC) + * @return string ORDER BY clause + */ + function order($keys, $direction = 'ASC') { + if (is_string($keys) && strpos($keys, ',') && !preg_match('/\(.+\,.+\)/', $keys)) { + $keys = array_map('trim', explode(',', $keys)); + } + + if (is_array($keys)) { + $keys = array_filter($keys); + } + + if (empty($keys) || (is_array($keys) && count($keys) && isset($keys[0]) && empty($keys[0]))) { + return ''; + } + + if (is_array($keys)) { + $keys = (Set::countDim($keys) > 1) ? array_map(array(&$this, 'order'), $keys) : $keys; + + foreach ($keys as $key => $value) { + if (is_numeric($key)) { + $key = $value = ltrim(str_replace('ORDER BY ', '', $this->order($value))); + $value = (!preg_match('/\\x20ASC|\\x20DESC/i', $key) ? ' ' . $direction : ''); + } else { + $value = ' ' . $value; + } + + if (!preg_match('/^.+\\(.*\\)/', $key) && !strpos($key, ',')) { + if (preg_match('/\\x20ASC|\\x20DESC/i', $key, $dir)) { + $dir = $dir[0]; + $key = preg_replace('/\\x20ASC|\\x20DESC/i', '', $key); + } else { + $dir = ''; + } + $key = trim($key); + if (!preg_match('/\s/', $key)) { + $key = $this->name($key); + } + $key .= ' ' . trim($dir); + } + $order[] = $this->order($key . $value); + } + return ' ORDER BY ' . trim(str_replace('ORDER BY', '', join(',', $order))); + } + $keys = preg_replace('/ORDER\\x20BY/i', '', $keys); + + if (strpos($keys, '.')) { + preg_match_all('/([a-zA-Z0-9_]{1,})\\.([a-zA-Z0-9_]{1,})/', $keys, $result, PREG_PATTERN_ORDER); + $pregCount = count($result[0]); + + for ($i = 0; $i < $pregCount; $i++) { + if (!is_numeric($result[0][$i])) { + $keys = preg_replace('/' . $result[0][$i] . '/', $this->name($result[0][$i]), $keys); + } + } + $result = ' ORDER BY ' . $keys; + return $result . (!preg_match('/\\x20ASC|\\x20DESC/i', $keys) ? ' ' . $direction : ''); + + } elseif (preg_match('/(\\x20ASC|\\x20DESC)/i', $keys, $match)) { + $direction = $match[1]; + return ' ORDER BY ' . preg_replace('/' . $match[1] . '/', '', $keys) . $direction; + } + return ' ORDER BY ' . $keys . ' ' . $direction; + } +/** + * Create a GROUP BY SQL clause + * + * @param string $group Group By Condition + * @return mixed string condition or null + */ + function group($group) { + if ($group) { + if (is_array($group)) { + $group = join(', ', $group); + } + return ' GROUP BY ' . $this->__quoteFields($group); + } + return null; + } +/** + * Disconnects database, kills the connection and says the connection is closed, + * and if DEBUG is turned on, the log for this object is shown. + * + */ + function close() { + if (Configure::read() > 1) { + $this->showLog(); + } + $this->disconnect(); + } +/** + * Checks if the specified table contains any record matching specified SQL + * + * @param Model $model Model to search + * @param string $sql SQL WHERE clause (condition only, not the "WHERE" part) + * @return boolean True if the table has a matching record, else false + */ + function hasAny(&$Model, $sql) { + $sql = $this->conditions($sql); + $table = $this->fullTableName($Model); + $alias = $this->alias . $this->name($Model->alias); + $where = $sql ? "{$sql}" : ' WHERE 1 = 1'; + $id = $Model->escapeField(); + + $out = $this->fetchRow("SELECT COUNT({$id}) {$this->alias}count FROM {$table} {$alias}{$where}"); + + if (is_array($out)) { + return $out[0]['count']; + } + return false; + } +/** + * 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 mixed An integer or string representing the length of the column + */ + function length($real) { + if (!preg_match_all('/([\w\s]+)(?:\((\d+)(?:,(\d+))?\))?(\sunsigned)?(\szerofill)?/', $real, $result)) { + trigger_error(__('FIXME: Can\'t parse field: ' . $real, true), E_USER_WARNING); + $col = str_replace(array(')', 'unsigned'), '', $real); + $limit = null; + + if (strpos($col, '(') !== false) { + list($col, $limit) = explode('(', $col); + } + if ($limit != null) { + return intval($limit); + } + return null; + } + + $types = array( + 'int' => 1, 'tinyint' => 1, 'smallint' => 1, 'mediumint' => 1, 'integer' => 1, 'bigint' => 1 + ); + + list($real, $type, $length, $offset, $sign, $zerofill) = $result; + $typeArr = $type; + $type = $type[0]; + $length = $length[0]; + $offset = $offset[0]; + + $isFloat = in_array($type, array('dec', 'decimal', 'float', 'numeric', 'double')); + if ($isFloat && $offset) { + return $length.','.$offset; + } + + if (($real[0] == $type) && (count($real) == 1)) { + return null; + } + + if (isset($types[$type])) { + $length += $types[$type]; + if (!empty($sign)) { + $length--; + } + } elseif (in_array($type, array('enum', 'set'))) { + $length = 0; + foreach ($typeArr as $key => $enumValue) { + if ($key == 0) { + continue; + } + $tmpLength = strlen($enumValue); + if ($tmpLength > $length) { + $length = $tmpLength; + } + } + } + return intval($length); + } +/** + * Translates between PHP boolean values and Database (faked) boolean values + * + * @param mixed $data Value to be translated + * @return mixed Converted boolean value + */ + function boolean($data) { + if ($data === true || $data === false) { + if ($data === true) { + return 1; + } + return 0; + } else { + return !empty($data); + } + } +/** + * Inserts multiple values into a table + * + * @param string $table + * @param string $fields + * @param array $values + * @access protected + */ + function insertMulti($table, $fields, $values) { + $table = $this->fullTableName($table); + if (is_array($fields)) { + $fields = join(', ', array_map(array(&$this, 'name'), $fields)); + } + $count = count($values); + for ($x = 0; $x < $count; $x++) { + $this->query("INSERT INTO {$table} ({$fields}) VALUES {$values[$x]}"); + } + } +/** + * 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) { + return false; + } +/** + * Generate a database-native schema for the given Schema object + * + * @param object $schema An instance of a subclass of CakeSchema + * @param string $tableName Optional. If specified only the table name given will be generated. + * Otherwise, all tables defined in the schema are generated. + * @return string + */ + function createSchema($schema, $tableName = 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 (!$tableName || $tableName == $curTable) { + $cols = $colList = $indexes = array(); + $primary = null; + $table = $this->fullTableName($curTable); + + foreach ($columns as $name => $col) { + if (is_string($col)) { + $col = array('type' => $col); + } + if (isset($col['key']) && $col['key'] == 'primary') { + $primary = $name; + } + if ($name !== 'indexes') { + $col['name'] = $name; + if (!isset($col['type'])) { + $col['type'] = 'string'; + } + $cols[] = $this->buildColumn($col); + } else { + $indexes = array_merge($indexes, $this->buildIndex($col, $table)); + } + } + if (empty($indexes) && !empty($primary)) { + $col = array('PRIMARY' => array('column' => $primary, 'unique' => 1)); + $indexes = array_merge($indexes, $this->buildIndex($col, $table)); + } + $columns = $cols; + $out .= $this->renderStatement('schema', compact('table', 'columns', 'indexes')) . "\n\n"; + } + } + return $out; + } +/** + * Generate a alter syntax from CakeSchema::compare() + * + * @param unknown_type $schema + * @return unknown + */ + function alterSchema($compare, $table = null) { + return false; + } +/** + * Generate a "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 ' . $this->fullTableName($curTable) . ";\n"; + } + } + return $out; + } +/** + * 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; + extract(array_merge(array('null' => true), $column)); + + if (empty($name) || empty($type)) { + trigger_error('Column name or type not defined in schema', E_USER_WARNING); + return null; + } + + if (!isset($this->columns[$type])) { + trigger_error("Column type {$type} does not exist", E_USER_WARNING); + return null; + } + + $real = $this->columns[$type]; + $out = $this->name($name) . ' ' . $real['name']; + + if (isset($real['limit']) || isset($real['length']) || isset($column['limit']) || isset($column['length'])) { + if (isset($column['length'])) { + $length = $column['length']; + } elseif (isset($column['limit'])) { + $length = $column['limit']; + } elseif (isset($real['length'])) { + $length = $real['length']; + } else { + $length = $real['limit']; + } + $out .= '(' . $length . ')'; + } + + if (($column['type'] == 'integer' || $column['type'] == 'float' ) && isset($column['default']) && $column['default'] === '') { + $column['default'] = null; + } + + if (isset($column['key']) && $column['key'] == 'primary' && $type == 'integer') { + $out .= ' ' . $this->columns['primary_key']['name']; + } elseif (isset($column['key']) && $column['key'] == 'primary') { + $out .= ' NOT NULL'; + } elseif (isset($column['default']) && isset($column['null']) && $column['null'] == false) { + $out .= ' DEFAULT ' . $this->value($column['default'], $type) . ' NOT NULL'; + } elseif (isset($column['default'])) { + $out .= ' DEFAULT ' . $this->value($column['default'], $type); + } elseif (isset($column['null']) && $column['null'] == true) { + $out .= ' DEFAULT NULL'; + } elseif (isset($column['null']) && $column['null'] == false) { + $out .= ' NOT NULL'; + } + return $out; + } +/** + * Format indexes for create table + * + * @param array $indexes + * @param string $table + * @return array + */ + function buildIndex($indexes, $table = null) { + $join = array(); + foreach ($indexes as $name => $value) { + $out = ''; + if ($name == 'PRIMARY') { + $out .= 'PRIMARY '; + $name = null; + } else { + if (!empty($value['unique'])) { + $out .= 'UNIQUE '; + } + $name = $this->startQuote . $name . $this->endQuote; + } + if (is_array($value['column'])) { + $out .= 'KEY ' . $name . ' (' . join(', ', array_map(array(&$this, 'name'), $value['column'])) . ')'; + } else { + $out .= 'KEY ' . $name . ' (' . $this->name($value['column']) . ')'; + } + $join[] = $out; + } + return $join; + } +/** + * Guesses the data type of an array + * + * @param string $value + * @return void + * @access public + */ + function introspectType($value) { + if (!is_array($value)) { + if ($value === true || $value === false) { + return 'boolean'; + } + if (is_float($value) && floatval($value) === $value) { + return 'float'; + } + if (is_int($value) && intval($value) === $value) { + return 'integer'; + } + if (is_string($value) && strlen($value) > 255) { + return 'text'; + } + return 'string'; + } + + $isAllFloat = $isAllInt = true; + $containsFloat = $containsInt = $containsString = false; + foreach ($value as $key => $valElement) { + $valElement = trim($valElement); + if (!is_float($valElement) && !preg_match('/^[\d]+\.[\d]+$/', $valElement)) { + $isAllFloat = false; + } else { + $containsFloat = true; + continue; + } + if (!is_int($valElement) && !preg_match('/^[\d]+$/', $valElement)) { + $isAllInt = false; + } else { + $containsInt = true; + continue; + } + $containsString = true; + } + + if ($isAllFloat) { + return 'float'; + } + if ($isAllInt) { + return 'integer'; + } + + if ($containsInt && !$containsString) { + return 'integer'; + } + return 'string'; + } +} +?> \ No newline at end of file diff --git a/cake/libs/model/db_acl.php b/cake/libs/model/db_acl.php new file mode 100755 index 0000000..a2a943c --- /dev/null +++ b/cake/libs/model/db_acl.php @@ -0,0 +1,317 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * This is core configuration file. + * + * Use it to configure core behaviour ofCake. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.model + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Load Model and AppModel + */ +App::import('Model', 'App'); +/** + * Short description for file. + * + * Long description for file + * + * + * @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') + ) + ); + + $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("Model class '$name' not found in AclNode::node() when trying to bind {$this->alias} object", 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("AclNode::node() - Couldn't find {$type} node identified by \"" . 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(); + } +} +?> \ No newline at end of file diff --git a/cake/libs/model/model.php b/cake/libs/model/model.php new file mode 100755 index 0000000..d437edf --- /dev/null +++ b/cake/libs/model/model.php @@ -0,0 +1,2894 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Object-relational mapper. + * + * DBO-backed object data model, for mapping database tables to Cake objects. + * + * PHP versions 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.model + * @since CakePHP(tm) v 0.10.0.0 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Included libs + */ +App::import('Core', array('ClassRegistry', 'Overloadable', 'Validation', 'Behavior', 'ConnectionManager', 'Set', 'String')); +/** + * Object-relational mapper. + * + * DBO-backed object data model. + * Automatically selects a database table name based on a pluralized lowercase object class name + * (i.e. class 'User' => table 'users'; class 'Man' => table 'men') + * The table is required to have at least 'id auto_increment' primary key. + * + * @package cake + * @subpackage cake.cake.libs.model + * @link http://book.cakephp.org/view/66/Models + */ +class Model extends Overloadable { +/** + * The name of the DataSource connection that this Model uses + * + * @var string + * @access public + * @link http://book.cakephp.org/view/435/useDbConfig + */ + var $useDbConfig = 'default'; +/** + * Custom database table name, or null/false if no table association is desired. + * + * @var string + * @access public + * @link http://book.cakephp.org/view/436/useTable + */ + var $useTable = null; +/** + * Custom display field name. Display fields are used by Scaffold, in SELECT boxes' OPTION elements. + * + * @var string + * @access public + * @link http://book.cakephp.org/view/438/displayField + */ + var $displayField = null; +/** + * Value of the primary key ID of the record that this model is currently pointing to. + * Automatically set after database insertions. + * + * @var mixed + * @access public + */ + var $id = false; +/** + * Container for the data that this model gets from persistent storage (usually, a database). + * + * @var array + * @access public + * @link http://book.cakephp.org/view/441/data + */ + var $data = array(); +/** + * Table name for this Model. + * + * @var string + * @access public + */ + var $table = false; +/** + * The name of the primary key field for this model. + * + * @var string + * @access public + * @link http://book.cakephp.org/view/437/primaryKey + */ + var $primaryKey = null; +/** + * Field-by-field table metadata. + * + * @var array + * @access protected + * @link http://book.cakephp.org/view/442/_schema + */ + var $_schema = null; +/** + * List of validation rules. Append entries for validation as ('field_name' => '/^perl_compat_regexp$/') + * that have to match with preg_match(). Use these rules with Model::validate() + * + * @var array + * @access public + * @link http://book.cakephp.org/view/443/validate + * @link http://book.cakephp.org/view/125/Data-Validation + */ + var $validate = array(); +/** + * List of validation errors. + * + * @var array + * @access public + * @link http://book.cakephp.org/view/410/Validating-Data-from-the-Controller + */ + var $validationErrors = array(); +/** + * Database table prefix for tables in model. + * + * @var string + * @access public + * @link http://book.cakephp.org/view/475/tablePrefix + */ + var $tablePrefix = null; +/** + * Name of the model. + * + * @var string + * @access public + * @link http://book.cakephp.org/view/444/name + */ + var $name = null; +/** + * Alias name for model. + * + * @var string + * @access public + */ + var $alias = null; +/** + * List of table names included in the model description. Used for associations. + * + * @var array + * @access public + */ + var $tableToModel = array(); +/** + * Whether or not to log transactions for this model. + * + * @var boolean + * @access public + */ + var $logTransactions = false; +/** + * Whether or not to enable transactions for this model (i.e. BEGIN/COMMIT/ROLLBACK statements) + * + * @var boolean + * @access public + */ + var $transactional = false; +/** + * Whether or not to cache queries for this model. This enables in-memory + * caching only, the results are not stored beyond the current request. + * + * @var boolean + * @access public + * @link http://book.cakephp.org/view/445/cacheQueries + */ + var $cacheQueries = false; +/** + * Detailed list of belongsTo associations. + * + * @var array + * @access public + * @link http://book.cakephp.org/view/81/belongsTo + */ + var $belongsTo = array(); +/** + * Detailed list of hasOne associations. + * + * @var array + * @access public + * @link http://book.cakephp.org/view/80/hasOne + */ + var $hasOne = array(); +/** + * Detailed list of hasMany associations. + * + * @var array + * @access public + * @link http://book.cakephp.org/view/82/hasMany + */ + var $hasMany = array(); +/** + * Detailed list of hasAndBelongsToMany associations. + * + * @var array + * @access public + * @link http://book.cakephp.org/view/83/hasAndBelongsToMany-HABTM + */ + var $hasAndBelongsToMany = array(); +/** + * List of behaviors to load when the model object is initialized. Settings can be + * passed to behaviors by using the behavior name as index. Eg: + * + * var $actsAs = array('Translate', 'MyBehavior' => array('setting1' => 'value1')) + * + * @var array + * @access public + * @link http://book.cakephp.org/view/90/Using-Behaviors + */ + var $actsAs = null; +/** + * Holds the Behavior objects currently bound to this model. + * + * @var BehaviorCollection + * @access public + */ + var $Behaviors = null; +/** + * Whitelist of fields allowed to be saved. + * + * @var array + * @access public + */ + var $whitelist = array(); +/** + * Whether or not to cache sources for this model. + * + * @var boolean + * @access public + */ + var $cacheSources = true; +/** + * Type of find query currently executing. + * + * @var string + * @access public + */ + var $findQueryType = null; +/** + * Number of associations to recurse through during find calls. Fetches only + * the first level by default. + * + * @var integer + * @access public + * @link http://book.cakephp.org/view/439/recursive + */ + var $recursive = 1; +/** + * The column name(s) and direction(s) to order find results by default. + * + * var $order = "Post.created DESC"; + * var $order = array("Post.view_count DESC", "Post.rating DESC"); + * + * @var string + * @access public + * @link http://book.cakephp.org/view/440/order + */ + var $order = null; +/** + * Whether or not the model record exists, set by Model::exists(). + * + * @var bool + * @access private + */ + var $__exists = null; +/** + * Default list of association keys. + * + * @var array + * @access private + */ + var $__associationKeys = array( + 'belongsTo' => array('className', 'foreignKey', 'conditions', 'fields', 'order', 'counterCache'), + 'hasOne' => array('className', 'foreignKey','conditions', 'fields','order', 'dependent'), + 'hasMany' => array('className', 'foreignKey', 'conditions', 'fields', 'order', 'limit', 'offset', 'dependent', 'exclusive', 'finderQuery', 'counterQuery'), + 'hasAndBelongsToMany' => array('className', 'joinTable', 'with', 'foreignKey', 'associationForeignKey', 'conditions', 'fields', 'order', 'limit', 'offset', 'unique', 'finderQuery', 'deleteQuery', 'insertQuery') + ); +/** + * Holds provided/generated association key names and other data for all associations. + * + * @var array + * @access private + */ + var $__associations = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany'); +/** + * Holds model associations temporarily to allow for dynamic (un)binding. + * + * @var array + * @access private + */ + var $__backAssociation = array(); +/** + * The ID of the model record that was last inserted. + * + * @var integer + * @access private + */ + var $__insertID = null; +/** + * The number of records returned by the last query. + * + * @var integer + * @access private + */ + var $__numRows = null; +/** + * The number of records affected by the last query. + * + * @var integer + * @access private + */ + var $__affectedRows = null; +/** + * List of valid finder method options, supplied as the first parameter to find(). + * + * @var array + * @access protected + */ + var $_findMethods = array( + 'all' => true, 'first' => true, 'count' => true, + 'neighbors' => true, 'list' => true, 'threaded' => true + ); +/** + * Constructor. Binds the model's database table to the object. + * + * @param integer $id Set this ID for this model on startup + * @param string $table Name of database table to use. + * @param object $ds DataSource connection object. + */ + function __construct($id = false, $table = null, $ds = null) { + parent::__construct(); + + if (is_array($id)) { + extract(array_merge( + array( + 'id' => $this->id, 'table' => $this->useTable, 'ds' => $this->useDbConfig, + 'name' => $this->name, 'alias' => $this->alias + ), + $id + )); + } + + if ($this->name === null) { + $this->name = (isset($name) ? $name : get_class($this)); + } + + if ($this->alias === null) { + $this->alias = (isset($alias) ? $alias : $this->name); + } + + if ($this->primaryKey === null) { + $this->primaryKey = 'id'; + } + + ClassRegistry::addObject($this->alias, $this); + + $this->id = $id; + unset($id); + + if ($table === false) { + $this->useTable = false; + } elseif ($table) { + $this->useTable = $table; + } + + if ($ds !== null) { + $this->useDbConfig = $ds; + } + + if (is_subclass_of($this, 'AppModel')) { + $appVars = get_class_vars('AppModel'); + $merge = array('_findMethods'); + + if ($this->actsAs !== null || $this->actsAs !== false) { + $merge[] = 'actsAs'; + } + $parentClass = get_parent_class($this); + if (strtolower($parentClass) !== 'appmodel') { + $parentVars = get_class_vars($parentClass); + foreach ($merge as $var) { + if (isset($parentVars[$var]) && !empty($parentVars[$var])) { + $appVars[$var] = Set::merge($appVars[$var], $parentVars[$var]); + } + } + } + + foreach ($merge as $var) { + if (isset($appVars[$var]) && !empty($appVars[$var]) && is_array($this->{$var})) { + $this->{$var} = Set::merge($appVars[$var], $this->{$var}); + } + } + } + $this->Behaviors = new BehaviorCollection(); + + if ($this->useTable !== false) { + $this->setDataSource($ds); + + if ($this->useTable === null) { + $this->useTable = Inflector::tableize($this->name); + } + if (method_exists($this, 'setTablePrefix')) { + $this->setTablePrefix(); + } + $this->setSource($this->useTable); + + if ($this->displayField == null) { + $this->displayField = $this->hasField(array('title', 'name', $this->primaryKey)); + } + } elseif ($this->table === false) { + $this->table = Inflector::tableize($this->name); + } + $this->__createLinks(); + $this->Behaviors->init($this->alias, $this->actsAs); + } +/** + * Handles custom method calls, like findBy<field> for DB models, + * and custom RPC calls for remote data sources. + * + * @param string $method Name of method to call. + * @param array $params Parameters for the method. + * @return mixed Whatever is returned by called method + * @access protected + */ + function call__($method, $params) { + $result = $this->Behaviors->dispatchMethod($this, $method, $params); + + if ($result !== array('unhandled')) { + return $result; + } + $db =& ConnectionManager::getDataSource($this->useDbConfig); + $return = $db->query($method, $params, $this); + + if (!PHP5) { + $this->resetAssociations(); + } + return $return; + } +/** + * Bind model associations on the fly. + * + * If $permanent is true, association will not be reset + * to the originals defined in the model. + * + * @param mixed $model A model or association name (string) or set of binding options (indexed by model name type) + * @param array $options If $model is a string, this is the list of association properties with which $model will + * be bound + * @param boolean $permanent Set to true to make the binding permanent + * @return void + * @access public + * @todo + */ + function bind($model, $options = array(), $permanent = true) { + if (!is_array($model)) { + $model = array($model => $options); + } + + foreach ($model as $name => $options) { + if (isset($options['type'])) { + $assoc = $options['type']; + } elseif (isset($options[0])) { + $assoc = $options[0]; + } else { + $assoc = 'belongsTo'; + } + + if (!$permanent) { + $this->__backAssociation[$assoc] = $this->{$assoc}; + } + foreach ($model as $key => $value) { + $assocName = $modelName = $key; + + if (isset($this->{$assoc}[$assocName])) { + $this->{$assoc}[$assocName] = array_merge($this->{$assoc}[$assocName], $options); + } else { + if (isset($value['className'])) { + $modelName = $value['className']; + } + + $this->__constructLinkedModel($assocName, $modelName); + $this->{$assoc}[$assocName] = $model[$assocName]; + $this->__generateAssociation($assoc); + } + unset($this->{$assoc}[$assocName]['type'], $this->{$assoc}[$assocName][0]); + } + } + } +/** + * Bind model associations on the fly. + * + * If $reset is false, association will not be reset + * to the originals defined in the model + * + * Example: Add a new hasOne binding to the Profile model not + * defined in the model source code: + * <code> + * $this->User->bindModel( array('hasOne' => array('Profile')) ); + * </code> + * + * @param array $params Set of bindings (indexed by binding type) + * @param boolean $reset Set to false to make the binding permanent + * @return boolean Success + * @access public + * @link http://book.cakephp.org/view/86/Creating-and-Destroying-Associations-on-the-Fly + */ + function bindModel($params, $reset = true) { + foreach ($params as $assoc => $model) { + if ($reset === true) { + $this->__backAssociation[$assoc] = $this->{$assoc}; + } + + foreach ($model as $key => $value) { + $assocName = $key; + + if (is_numeric($key)) { + $assocName = $value; + $value = array(); + } + $modelName = $assocName; + $this->{$assoc}[$assocName] = $value; + } + } + $this->__createLinks(); + return true; + } +/** + * Turn off associations on the fly. + * + * If $reset is false, association will not be reset + * to the originals defined in the model + * + * Example: Turn off the associated Model Support request, + * to temporarily lighten the User model: + * <code> + * $this->User->unbindModel( array('hasMany' => array('Supportrequest')) ); + * </code> + * + * @param array $params Set of bindings to unbind (indexed by binding type) + * @param boolean $reset Set to false to make the unbinding permanent + * @return boolean Success + * @access public + * @link http://book.cakephp.org/view/86/Creating-and-Destroying-Associations-on-the-Fly + */ + function unbindModel($params, $reset = true) { + foreach ($params as $assoc => $models) { + if ($reset === true) { + $this->__backAssociation[$assoc] = $this->{$assoc}; + } + + foreach ($models as $model) { + $this->__backAssociation = array_merge($this->__backAssociation, $this->{$assoc}); + unset ($this->__backAssociation[$model]); + unset ($this->{$assoc}[$model]); + } + } + return true; + } +/** + * Create a set of associations. + * + * @return void + * @access private + */ + function __createLinks() { + foreach ($this->__associations as $type) { + if (!is_array($this->{$type})) { + $this->{$type} = explode(',', $this->{$type}); + + foreach ($this->{$type} as $i => $className) { + $className = trim($className); + unset ($this->{$type}[$i]); + $this->{$type}[$className] = array(); + } + } + + if (!empty($this->{$type})) { + foreach ($this->{$type} as $assoc => $value) { + $plugin = null; + + if (is_numeric($assoc)) { + unset ($this->{$type}[$assoc]); + $assoc = $value; + $value = array(); + $this->{$type}[$assoc] = $value; + + if (strpos($assoc, '.') !== false) { + $value = $this->{$type}[$assoc]; + unset($this->{$type}[$assoc]); + list($plugin, $assoc) = explode('.', $assoc); + $this->{$type}[$assoc] = $value; + $plugin = $plugin . '.'; + } + } + $className = $assoc; + + if (isset($value['className']) && !empty($value['className'])) { + $className = $value['className']; + if (strpos($className, '.') !== false) { + list($plugin, $className) = explode('.', $className); + $plugin = $plugin . '.'; + $this->{$type}[$assoc]['className'] = $className; + } + } + $this->__constructLinkedModel($assoc, $plugin . $className); + } + $this->__generateAssociation($type); + } + } + } +/** + * Private helper method to create associated models of a given class. + * + * @param string $assoc Association name + * @param string $className Class name + * @deprecated $this->$className use $this->$assoc instead. $assoc is the 'key' in the associations array; + * examples: var $hasMany = array('Assoc' => array('className' => 'ModelName')); + * usage: $this->Assoc->modelMethods(); + * + * var $hasMany = array('ModelName'); + * usage: $this->ModelName->modelMethods(); + * @return void + * @access private + */ + function __constructLinkedModel($assoc, $className = null) { + if (empty($className)) { + $className = $assoc; + } + + if (!isset($this->{$assoc}) || $this->{$assoc}->name !== $className) { + $model = array('class' => $className, 'alias' => $assoc); + if (PHP5) { + $this->{$assoc} = ClassRegistry::init($model); + } else { + $this->{$assoc} =& ClassRegistry::init($model); + } + if ($assoc) { + $this->tableToModel[$this->{$assoc}->table] = $assoc; + } + } + } +/** + * Build an array-based association from string. + * + * @param string $type 'belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany' + * @return void + * @access private + */ + function __generateAssociation($type) { + foreach ($this->{$type} as $assocKey => $assocData) { + $class = $assocKey; + $dynamicWith = false; + + foreach ($this->__associationKeys[$type] as $key) { + + if (!isset($this->{$type}[$assocKey][$key]) || $this->{$type}[$assocKey][$key] === null) { + $data = ''; + + switch ($key) { + case 'fields': + $data = ''; + break; + + case 'foreignKey': + $data = (($type == 'belongsTo') ? Inflector::underscore($assocKey) : Inflector::singularize($this->table)) . '_id'; + break; + + case 'associationForeignKey': + $data = Inflector::singularize($this->{$class}->table) . '_id'; + break; + + case 'with': + $data = Inflector::camelize(Inflector::singularize($this->{$type}[$assocKey]['joinTable'])); + $dynamicWith = true; + break; + + case 'joinTable': + $tables = array($this->table, $this->{$class}->table); + sort ($tables); + $data = $tables[0] . '_' . $tables[1]; + break; + + case 'className': + $data = $class; + break; + + case 'unique': + $data = true; + break; + } + $this->{$type}[$assocKey][$key] = $data; + } + } + + if (!empty($this->{$type}[$assocKey]['with'])) { + $joinClass = $this->{$type}[$assocKey]['with']; + if (is_array($joinClass)) { + $joinClass = key($joinClass); + } + $plugin = null; + + if (strpos($joinClass, '.') !== false) { + list($plugin, $joinClass) = explode('.', $joinClass); + $plugin = $plugin . '.'; + $this->{$type}[$assocKey]['with'] = $joinClass; + } + + if (!ClassRegistry::isKeySet($joinClass) && $dynamicWith === true) { + $this->{$joinClass} = new AppModel(array( + 'name' => $joinClass, + 'table' => $this->{$type}[$assocKey]['joinTable'], + 'ds' => $this->useDbConfig + )); + } else { + $this->__constructLinkedModel($joinClass, $plugin . $joinClass); + $this->{$type}[$assocKey]['joinTable'] = $this->{$joinClass}->table; + } + + if (count($this->{$joinClass}->schema()) <= 2 && $this->{$joinClass}->primaryKey !== false) { + $this->{$joinClass}->primaryKey = $this->{$type}[$assocKey]['foreignKey']; + } + } + } + } +/** + * Sets a custom table for your controller class. Used by your controller to select a database table. + * + * @param string $tableName Name of the custom table + * @return void + * @access public + */ + function setSource($tableName) { + $this->setDataSource($this->useDbConfig); + $db =& ConnectionManager::getDataSource($this->useDbConfig); + $db->cacheSources = ($this->cacheSources && $db->cacheSources); + + if ($db->isInterfaceSupported('listSources')) { + $sources = $db->listSources(); + if (is_array($sources) && !in_array(strtolower($this->tablePrefix . $tableName), array_map('strtolower', $sources))) { + return $this->cakeError('missingTable', array(array( + 'className' => $this->alias, + 'table' => $this->tablePrefix . $tableName + ))); + } + $this->_schema = null; + } + $this->table = $this->useTable = $tableName; + $this->tableToModel[$this->table] = $this->alias; + $this->schema(); + } +/** + * This function does two things: 1) it scans the array $one for the primary key, + * and if that's found, it sets the current id to the value of $one[id]. + * For all other keys than 'id' the keys and values of $one are copied to the 'data' property of this object. + * 2) Returns an array with all of $one's keys and values. + * (Alternative indata: two strings, which are mangled to + * a one-item, two-dimensional array using $one for a key and $two as its value.) + * + * @param mixed $one Array or string of data + * @param string $two Value string for the alternative indata method + * @return array Data with all of $one's keys and values + * @access public + */ + function set($one, $two = null) { + if (!$one) { + return; + } + if (is_object($one)) { + $one = Set::reverse($one); + } + + if (is_array($one)) { + $data = $one; + if (empty($one[$this->alias])) { + if ($this->getAssociated(key($one)) === null) { + $data = array($this->alias => $one); + } + } + } else { + $data = array($this->alias => array($one => $two)); + } + + foreach ($data as $modelName => $fieldSet) { + if (is_array($fieldSet)) { + + foreach ($fieldSet as $fieldName => $fieldValue) { + if (isset($this->validationErrors[$fieldName])) { + unset ($this->validationErrors[$fieldName]); + } + + if ($modelName === $this->alias) { + if ($fieldName === $this->primaryKey) { + $this->id = $fieldValue; + } + } + if (is_array($fieldValue) || is_object($fieldValue)) { + $fieldValue = $this->deconstruct($fieldName, $fieldValue); + } + $this->data[$modelName][$fieldName] = $fieldValue; + } + } + } + return $data; + } +/** + * Deconstructs a complex data type (array or object) into a single field value. + * + * @param string $field The name of the field to be deconstructed + * @param mixed $data An array or object to be deconstructed into a field + * @return mixed The resulting data that should be assigned to a field + * @access public + */ + function deconstruct($field, $data) { + if (!is_array($data)) { + return $data; + } + + $copy = $data; + $type = $this->getColumnType($field); + + if (in_array($type, array('datetime', 'timestamp', 'date', 'time'))) { + $useNewDate = (isset($data['year']) || isset($data['month']) || + isset($data['day']) || isset($data['hour']) || isset($data['minute'])); + + $dateFields = array('Y' => 'year', 'm' => 'month', 'd' => 'day', 'H' => 'hour', 'i' => 'min', 's' => 'sec'); + $timeFields = array('H' => 'hour', 'i' => 'min', 's' => 'sec'); + + $db =& ConnectionManager::getDataSource($this->useDbConfig); + $format = $db->columns[$type]['format']; + $date = array(); + + if (isset($data['hour']) && isset($data['meridian']) && $data['hour'] != 12 && 'pm' == $data['meridian']) { + $data['hour'] = $data['hour'] + 12; + } + if (isset($data['hour']) && isset($data['meridian']) && $data['hour'] == 12 && 'am' == $data['meridian']) { + $data['hour'] = '00'; + } + if ($type == 'time') { + foreach ($timeFields as $key => $val) { + if (!isset($data[$val]) || $data[$val] === '0' || $data[$val] === '00') { + $data[$val] = '00'; + } elseif ($data[$val] === '') { + $data[$val] = ''; + } else { + $data[$val] = sprintf('%02d', $data[$val]); + } + if (!empty($data[$val])) { + $date[$key] = $data[$val]; + } else { + return null; + } + } + } + + if ($type == 'datetime' || $type == 'timestamp' || $type == 'date') { + foreach ($dateFields as $key => $val) { + if ($val == 'hour' || $val == 'min' || $val == 'sec') { + if (!isset($data[$val]) || $data[$val] === '0' || $data[$val] === '00') { + $data[$val] = '00'; + } else { + $data[$val] = sprintf('%02d', $data[$val]); + } + } + if (!isset($data[$val]) || isset($data[$val]) && (empty($data[$val]) || $data[$val][0] === '-')) { + return null; + } + if (isset($data[$val]) && !empty($data[$val])) { + $date[$key] = $data[$val]; + } + } + } + $date = str_replace(array_keys($date), array_values($date), $format); + if ($useNewDate && !empty($date)) { + return $date; + } + } + return $data; + } +/** + * Returns an array of table metadata (column names and types) from the database. + * $field => keys(type, null, default, key, length, extra) + * + * @param mixed $field Set to true to reload schema, or a string to return a specific field + * @return array Array of table metadata + * @access public + */ + function schema($field = false) { + if (!is_array($this->_schema) || $field === true) { + $db =& ConnectionManager::getDataSource($this->useDbConfig); + $db->cacheSources = ($this->cacheSources && $db->cacheSources); + if ($db->isInterfaceSupported('describe') && $this->useTable !== false) { + $this->_schema = $db->describe($this, $field); + } elseif ($this->useTable === false) { + $this->_schema = array(); + } + } + if (is_string($field)) { + if (isset($this->_schema[$field])) { + return $this->_schema[$field]; + } else { + return null; + } + } + return $this->_schema; + } +/** + * Returns an associative array of field names and column types. + * + * @return array Field types indexed by field name + * @access public + */ + function getColumnTypes() { + $columns = $this->schema(); + if (empty($columns)) { + trigger_error(__('(Model::getColumnTypes) Unable to build model field data. If you are using a model without a database table, try implementing schema()', true), E_USER_WARNING); + } + $cols = array(); + foreach ($columns as $field => $values) { + $cols[$field] = $values['type']; + } + return $cols; + } +/** + * Returns the column type of a column in the model. + * + * @param string $column The name of the model column + * @return string Column type + * @access public + */ + function getColumnType($column) { + $db =& ConnectionManager::getDataSource($this->useDbConfig); + $cols = $this->schema(); + $model = null; + + $column = str_replace(array($db->startQuote, $db->endQuote), '', $column); + + if (strpos($column, '.')) { + list($model, $column) = explode('.', $column); + } + if ($model != $this->alias && isset($this->{$model})) { + return $this->{$model}->getColumnType($column); + } + if (isset($cols[$column]) && isset($cols[$column]['type'])) { + return $cols[$column]['type']; + } + return null; + } +/** + * Returns true if the supplied field exists in the model's database table. + * + * @param mixed $name Name of field to look for, or an array of names + * @return mixed If $name is a string, returns a boolean indicating whether the field exists. + * If $name is an array of field names, returns the first field that exists, + * or false if none exist. + * @access public + */ + function hasField($name) { + if (is_array($name)) { + foreach ($name as $n) { + if ($this->hasField($n)) { + return $n; + } + } + return false; + } + + if (empty($this->_schema)) { + $this->schema(); + } + + if ($this->_schema != null) { + return isset($this->_schema[$name]); + } + return false; + } +/** + * Initializes the model for writing a new record, loading the default values + * for those fields that are not defined in $data. Especially helpful for + * saving data in loops. + * + * @param mixed $data Optional data array to assign to the model after it is created. If null or false, + * schema data defaults are not merged. + * @param boolean $filterKey If true, overwrites any primary key input with an empty value + * @return array The current Model::data; after merging $data and/or defaults from database + * @access public + * @link http://book.cakephp.org/view/75/Saving-Your-Data + */ + function create($data = array(), $filterKey = false) { + $defaults = array(); + $this->id = false; + $this->data = array(); + $this->__exists = null; + $this->validationErrors = array(); + + if ($data !== null && $data !== false) { + foreach ($this->schema() as $field => $properties) { + if ($this->primaryKey !== $field && isset($properties['default'])) { + $defaults[$field] = $properties['default']; + } + } + $this->set(Set::filter($defaults)); + $this->set($data); + } + if ($filterKey) { + $this->set($this->primaryKey, false); + } + return $this->data; + } +/** + * Returns a list of fields from the database, and sets the current model + * data (Model::$data) with the record found. + * + * @param mixed $fields String of single fieldname, or an array of fieldnames. + * @param mixed $id The ID of the record to read + * @return array Array of database fields, or false if not found + * @access public + */ + function read($fields = null, $id = null) { + $this->validationErrors = array(); + + if ($id != null) { + $this->id = $id; + } + + $id = $this->id; + + if (is_array($this->id)) { + $id = $this->id[0]; + } + + if ($id !== null && $id !== false) { + $this->data = $this->find('first', array( + 'conditions' => array($this->alias . '.' . $this->primaryKey => $id), + 'fields' => $fields + )); + return $this->data; + } else { + return false; + } + } +/** + * Returns the contents of a single field given the supplied conditions, in the + * supplied order. + * + * @param string $name Name of field to get + * @param array $conditions SQL conditions (defaults to NULL) + * @param string $order SQL ORDER BY fragment + * @return string field contents, or false if not found + * @access public + * @link http://book.cakephp.org/view/453/field + */ + function field($name, $conditions = null, $order = null) { + if ($conditions === null && $this->id !== false) { + $conditions = array($this->alias . '.' . $this->primaryKey => $this->id); + } + if ($this->recursive >= 1) { + $recursive = -1; + } else { + $recursive = $this->recursive; + } + if ($data = $this->find($conditions, $name, $order, $recursive)) { + if (strpos($name, '.') === false) { + if (isset($data[$this->alias][$name])) { + return $data[$this->alias][$name]; + } + } else { + $name = explode('.', $name); + if (isset($data[$name[0]][$name[1]])) { + return $data[$name[0]][$name[1]]; + } + } + if (isset($data[0]) && count($data[0]) > 0) { + $name = key($data[0]); + return $data[0][$name]; + } + } else { + return false; + } + } +/** + * Saves the value of a single field to the database, based on the current + * model ID. + * + * @param string $name Name of the table field + * @param mixed $value Value of the field + * @param array $validate See $options param in Model::save(). Does not respect 'fieldList' key if passed + * @return boolean See Model::save() + * @access public + * @see Model::save() + * @link http://book.cakephp.org/view/75/Saving-Your-Data + */ + function saveField($name, $value, $validate = false) { + $id = $this->id; + $this->create(false); + + if (is_array($validate)) { + $options = array_merge(array('validate' => false, 'fieldList' => array($name)), $validate); + } else { + $options = array('validate' => $validate, 'fieldList' => array($name)); + } + return $this->save(array($this->alias => array($this->primaryKey => $id, $name => $value)), $options); + } +/** + * Saves model data (based on white-list, if supplied) to the database. By + * default, validation occurs before save. + * + * @param array $data Data to save. + * @param mixed $validate Either a boolean, or an array. + * If a boolean, indicates whether or not to validate before saving. + * If an array, allows control of validate, callbacks, and fieldList + * @param array $fieldList List of fields to allow to be written + * @return mixed On success Model::$data if its not empty or true, false on failure + * @access public + * @link http://book.cakephp.org/view/75/Saving-Your-Data + */ + function save($data = null, $validate = true, $fieldList = array()) { + $defaults = array('validate' => true, 'fieldList' => array(), 'callbacks' => true); + $_whitelist = $this->whitelist; + $fields = array(); + + if (!is_array($validate)) { + $options = array_merge($defaults, compact('validate', 'fieldList', 'callbacks')); + } else { + $options = array_merge($defaults, $validate); + } + + if (!empty($options['fieldList'])) { + $this->whitelist = $options['fieldList']; + } elseif ($options['fieldList'] === null) { + $this->whitelist = array(); + } + $this->set($data); + + if (empty($this->data) && !$this->hasField(array('created', 'updated', 'modified'))) { + return false; + } + + foreach (array('created', 'updated', 'modified') as $field) { + $keyPresentAndEmpty = ( + isset($this->data[$this->alias]) && + array_key_exists($field, $this->data[$this->alias]) && + $this->data[$this->alias][$field] === null + ); + if ($keyPresentAndEmpty) { + unset($this->data[$this->alias][$field]); + } + } + + $this->exists(); + $dateFields = array('modified', 'updated'); + + if (!$this->__exists) { + $dateFields[] = 'created'; + } + if (isset($this->data[$this->alias])) { + $fields = array_keys($this->data[$this->alias]); + } + if ($options['validate'] && !$this->validates($options)) { + $this->whitelist = $_whitelist; + return false; + } + + $db =& ConnectionManager::getDataSource($this->useDbConfig); + + foreach ($dateFields as $updateCol) { + if ($this->hasField($updateCol) && !in_array($updateCol, $fields)) { + $default = array('formatter' => 'date'); + $colType = array_merge($default, $db->columns[$this->getColumnType($updateCol)]); + if (!array_key_exists('format', $colType)) { + $time = strtotime('now'); + } else { + $time = $colType['formatter']($colType['format']); + } + if (!empty($this->whitelist)) { + $this->whitelist[] = $updateCol; + } + $this->set($updateCol, $time); + } + } + + if ($options['callbacks'] === true || $options['callbacks'] === 'before') { + $result = $this->Behaviors->trigger($this, 'beforeSave', array($options), array( + 'break' => true, 'breakOn' => false + )); + if (!$result || !$this->beforeSave($options)) { + $this->whitelist = $_whitelist; + return false; + } + } + $fields = $values = array(); + + if (isset($this->data[$this->alias][$this->primaryKey]) && empty($this->data[$this->alias][$this->primaryKey])) { + unset($this->data[$this->alias][$this->primaryKey]); + } + + foreach ($this->data as $n => $v) { + if (isset($this->hasAndBelongsToMany[$n])) { + if (isset($v[$n])) { + $v = $v[$n]; + } + $joined[$n] = $v; + } else { + if ($n === $this->alias) { + foreach (array('created', 'updated', 'modified') as $field) { + if (array_key_exists($field, $v) && empty($v[$field])) { + unset($v[$field]); + } + } + + foreach ($v as $x => $y) { + if ($this->hasField($x) && (empty($this->whitelist) || in_array($x, $this->whitelist))) { + list($fields[], $values[]) = array($x, $y); + } + } + } + } + } + $count = count($fields); + + if (!$this->__exists && $count > 0) { + $this->id = false; + } + $success = true; + $created = false; + + if ($count > 0) { + $cache = $this->_prepareUpdateFields(array_combine($fields, $values)); + + if (!empty($this->id)) { + $success = (bool)$db->update($this, $fields, $values); + } else { + foreach ($this->_schema as $field => $properties) { + if ($this->primaryKey === $field) { + $fInfo = $this->_schema[$field]; + $isUUID = ($fInfo['length'] == 36 && + ($fInfo['type'] === 'string' || $fInfo['type'] === 'binary') + ); + if (empty($this->data[$this->alias][$this->primaryKey]) && $isUUID) { + list($fields[], $values[]) = array($this->primaryKey, String::uuid()); + } + break; + } + } + + if (!$db->create($this, $fields, $values)) { + $success = $created = false; + } else { + $created = true; + } + } + + if ($success && !empty($this->belongsTo)) { + $this->updateCounterCache($cache, $created); + } + } + + if (!empty($joined) && $success === true) { + $this->__saveMulti($joined, $this->id); + } + + if ($success && $count > 0) { + if (!empty($this->data)) { + $success = $this->data; + } + if ($options['callbacks'] === true || $options['callbacks'] === 'after') { + $this->Behaviors->trigger($this, 'afterSave', array($created, $options)); + $this->afterSave($created); + } + if (!empty($this->data)) { + $success = Set::merge($success, $this->data); + } + $this->data = false; + $this->__exists = null; + $this->_clearCache(); + $this->validationErrors = array(); + } + $this->whitelist = $_whitelist; + return $success; + } +/** + * Saves model hasAndBelongsToMany data to the database. + * + * @param array $joined Data to save + * @param mixed $id ID of record in this model + * @access private + */ + function __saveMulti($joined, $id) { + $db =& ConnectionManager::getDataSource($this->useDbConfig); + + foreach ($joined as $assoc => $data) { + + if (isset($this->hasAndBelongsToMany[$assoc])) { + list($join) = $this->joinModel($this->hasAndBelongsToMany[$assoc]['with']); + + $conditions = array($join . '.' . $this->hasAndBelongsToMany[$assoc]['foreignKey'] => $id); + + $links = $this->{$join}->find('all', array( + 'conditions' => $conditions, + 'recursive' => -1, + 'fields' => $this->hasAndBelongsToMany[$assoc]['associationForeignKey'] + )); + + $isUUID = !empty($this->{$join}->primaryKey) && ( + $this->{$join}->_schema[$this->{$join}->primaryKey]['length'] == 36 && ( + $this->{$join}->_schema[$this->{$join}->primaryKey]['type'] === 'string' || + $this->{$join}->_schema[$this->{$join}->primaryKey]['type'] === 'binary' + ) + ); + + $newData = $newValues = array(); + $primaryAdded = false; + + $fields = array( + $db->name($this->hasAndBelongsToMany[$assoc]['foreignKey']), + $db->name($this->hasAndBelongsToMany[$assoc]['associationForeignKey']) + ); + + $idField = $db->name($this->{$join}->primaryKey); + if ($isUUID && !in_array($idField, $fields)) { + $fields[] = $idField; + $primaryAdded = true; + } + + foreach ((array)$data as $row) { + if ((is_string($row) && (strlen($row) == 36 || strlen($row) == 16)) || is_numeric($row)) { + $values = array( + $db->value($id, $this->getColumnType($this->primaryKey)), + $db->value($row) + ); + if ($isUUID && $primaryAdded) { + $values[] = $db->value(String::uuid()); + } + $values = join(',', $values); + $newValues[] = "({$values})"; + unset($values); + } elseif (isset($row[$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) { + $newData[] = $row; + } elseif (isset($row[$join]) && isset($row[$join][$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) { + $newData[] = $row[$join]; + } + } + + if ($this->hasAndBelongsToMany[$assoc]['unique']) { + $associationForeignKey = "{$join}." . $this->hasAndBelongsToMany[$assoc]['associationForeignKey']; + $oldLinks = Set::extract($links, "{n}.{$associationForeignKey}"); + if (!empty($oldLinks)) { + $conditions[$associationForeignKey] = $oldLinks; + $db->delete($this->{$join}, $conditions); + } + } + + if (!empty($newData)) { + foreach ($newData as $data) { + $data[$this->hasAndBelongsToMany[$assoc]['foreignKey']] = $id; + $this->{$join}->create($data); + $this->{$join}->save(); + } + } + + if (!empty($newValues)) { + $fields = join(',', $fields); + $db->insertMulti($this->{$join}, $fields, $newValues); + } + } + } + } +/** + * Updates the counter cache of belongsTo associations after a save or delete operation + * + * @param array $keys Optional foreign key data, defaults to the information $this->data + * @param boolean $created True if a new record was created, otherwise only associations with + * 'counterScope' defined get updated + * @return void + * @access public + */ + function updateCounterCache($keys = array(), $created = false) { + $keys = empty($keys) ? $this->data[$this->alias] : $keys; + $keys['old'] = isset($keys['old']) ? $keys['old'] : array(); + + foreach ($this->belongsTo as $parent => $assoc) { + $foreignKey = $assoc['foreignKey']; + $fkQuoted = $this->escapeField($assoc['foreignKey']); + + if (!empty($assoc['counterCache'])) { + if ($assoc['counterCache'] === true) { + $assoc['counterCache'] = Inflector::underscore($this->alias) . '_count'; + } + if (!$this->{$parent}->hasField($assoc['counterCache'])) { + continue; + } + + if (!array_key_exists($foreignKey, $keys)) { + $keys[$foreignKey] = $this->field($foreignKey); + } + $recursive = (isset($assoc['counterScope']) ? 1 : -1); + $conditions = ($recursive == 1) ? (array)$assoc['counterScope'] : array(); + + if (isset($keys['old'][$foreignKey])) { + if ($keys['old'][$foreignKey] != $keys[$foreignKey]) { + $conditions[$fkQuoted] = $keys['old'][$foreignKey]; + $count = intval($this->find('count', compact('conditions', 'recursive'))); + + $this->{$parent}->updateAll( + array($assoc['counterCache'] => $count), + array($this->{$parent}->escapeField() => $keys['old'][$foreignKey]) + ); + } + } + $conditions[$fkQuoted] = $keys[$foreignKey]; + + if ($recursive == 1) { + $conditions = array_merge($conditions, (array)$assoc['counterScope']); + } + $count = intval($this->find('count', compact('conditions', 'recursive'))); + + $this->{$parent}->updateAll( + array($assoc['counterCache'] => $count), + array($this->{$parent}->escapeField() => $keys[$foreignKey]) + ); + } + } + } +/** + * Helper method for Model::updateCounterCache(). Checks the fields to be updated for + * + * @param array $data The fields of the record that will be updated + * @return array Returns updated foreign key values, along with an 'old' key containing the old + * values, or empty if no foreign keys are updated. + * @access protected + */ + function _prepareUpdateFields($data) { + $foreignKeys = array(); + foreach ($this->belongsTo as $assoc => $info) { + if ($info['counterCache']) { + $foreignKeys[$assoc] = $info['foreignKey']; + } + } + $included = array_intersect($foreignKeys, array_keys($data)); + + if (empty($included) || empty($this->id)) { + return array(); + } + $old = $this->find('first', array( + 'conditions' => array($this->primaryKey => $this->id), + 'fields' => array_values($included), + 'recursive' => -1 + )); + return array_merge($data, array('old' => $old[$this->alias])); + } +/** + * Saves multiple individual records for a single model; Also works with a single record, as well as + * all its associated records. + * + * @param array $data Record data to save. This can be either a numerically-indexed array (for saving multiple + * records of the same type), or an array indexed by association name. + * @param array $options Options to use when saving record data, which are as follows: + * - validate: Set to false to disable validation, true to validate each record before + * saving, 'first' to validate *all* records before any are saved, or 'only' to only + * validate the records, but not save them. + * - atomic: If true (default), will attempt to save all records in a single transaction. + * Should be set to false if database/table does not support transactions. + * If false, we return an array similar to the $data array passed, but values are set to true/false + * depending on whether each record saved successfully. + * - fieldList: Equivalent to the $fieldList parameter in Model::save() + * @return mixed True on success, or false on failure + * @access public + * @link http://book.cakephp.org/view/84/Saving-Related-Model-Data-hasOne-hasMany-belongsTo + * @link http://book.cakephp.org/view/75/Saving-Your-Data + */ + function saveAll($data = null, $options = array()) { + if (empty($data)) { + $data = $this->data; + } + $db =& ConnectionManager::getDataSource($this->useDbConfig); + + $options = array_merge(array('validate' => true, 'atomic' => true), $options); + $this->validationErrors = $validationErrors = array(); + $validates = true; + $return = array(); + + if ($options['atomic'] && $options['validate'] !== 'only') { + $db->begin($this); + } + + if (Set::numeric(array_keys($data))) { + while ($validates) { + foreach ($data as $key => $record) { + if (!$currentValidates = $this->__save($record, $options)) { + $validationErrors[$key] = $this->validationErrors; + } + + if ($options['validate'] === 'only' || $options['validate'] === 'first') { + $validating = true; + if ($options['atomic']) { + $validates = $validates && $currentValidates; + } else { + $validates = $currentValidates; + } + } else { + $validating = false; + $validates = $currentValidates; + } + + if (!$options['atomic']) { + $return[] = $validates; + } elseif (!$validates && !$validating) { + break; + } + } + $this->validationErrors = $validationErrors; + + switch (true) { + case ($options['validate'] === 'only'): + return ($options['atomic'] ? $validates : $return); + break; + case ($options['validate'] === 'first'): + $options['validate'] = true; + continue; + break; + default: + if ($options['atomic']) { + if ($validates && ($db->commit($this) !== false)) { + return true; + } + $db->rollback($this); + return false; + } + return $return; + break; + } + } + return $return; + } + $associations = $this->getAssociated(); + + while ($validates) { + foreach ($data as $association => $values) { + if (isset($associations[$association])) { + switch ($associations[$association]) { + case 'belongsTo': + if ($this->{$association}->__save($values, $options)) { + $data[$this->alias][$this->belongsTo[$association]['foreignKey']] = $this->{$association}->id; + } else { + $validationErrors[$association] = $this->{$association}->validationErrors; + $validates = false; + } + if (!$options['atomic']) { + $return[$association][] = $validates; + } + break; + } + } + } + if (!$this->__save($data, $options)) { + $validationErrors[$this->alias] = $this->validationErrors; + $validates = false; + } + if (!$options['atomic']) { + $return[$this->alias] = $validates; + } + $validating = ($options['validate'] === 'only' || $options['validate'] === 'first'); + + foreach ($data as $association => $values) { + if (!$validates && !$validating) { + break; + } + if (isset($associations[$association])) { + $type = $associations[$association]; + switch ($type) { + case 'hasOne': + $values[$this->{$type}[$association]['foreignKey']] = $this->id; + if (!$this->{$association}->__save($values, $options)) { + $validationErrors[$association] = $this->{$association}->validationErrors; + $validates = false; + } + if (!$options['atomic']) { + $return[$association][] = $validates; + } + break; + case 'hasMany': + foreach ($values as $i => $value) { + $values[$i][$this->{$type}[$association]['foreignKey']] = $this->id; + } + $_options = array_merge($options, array('atomic' => false)); + + if ($_options['validate'] === 'first') { + $_options['validate'] = 'only'; + } + $_return = $this->{$association}->saveAll($values, $_options); + + if ($_return === false || (is_array($_return) && in_array(false, $_return, true))) { + $validationErrors[$association] = $this->{$association}->validationErrors; + $validates = false; + } + if (is_array($_return)) { + foreach ($_return as $val) { + if (!isset($return[$association])) { + $return[$association] = array(); + } elseif (!is_array($return[$association])) { + $return[$association] = array($return[$association]); + } + $return[$association][] = $val; + } + } else { + $return[$association] = $_return; + } + break; + } + } + } + $this->validationErrors = $validationErrors; + + if (isset($validationErrors[$this->alias])) { + $this->validationErrors = $validationErrors[$this->alias]; + } + + switch (true) { + case ($options['validate'] === 'only'): + return ($options['atomic'] ? $validates : $return); + break; + case ($options['validate'] === 'first'): + $options['validate'] = true; + continue; + break; + default: + if ($options['atomic']) { + if ($validates) { + return ($db->commit($this) !== false); + } else { + $db->rollback($this); + } + } + return $return; + break; + } + } + } +/** + * Private helper method used by saveAll. + * + * @return boolean Success + * @access private + * @see Model::saveAll() + */ + function __save($data, $options) { + if ($options['validate'] === 'first' || $options['validate'] === 'only') { + if (!($this->create($data) && $this->validates($options))) { + return false; + } + } elseif (!($this->create(null) !== null && $this->save($data, $options))) { + return false; + } + return true; + } +/** + * Updates multiple model records based on a set of conditions. + * + * @param array $fields Set of fields and values, indexed by fields. + * Fields are treated as SQL snippets, to insert literal values manually escape your data. + * @param mixed $conditions Conditions to match, true for all records + * @return boolean True on success, false on failure + * @access public + * @link http://book.cakephp.org/view/75/Saving-Your-Data + */ + function updateAll($fields, $conditions = true) { + $db =& ConnectionManager::getDataSource($this->useDbConfig); + return $db->update($this, $fields, null, $conditions); + } +/** + * Alias for del(). + * + * @param mixed $id ID of record to delete + * @param boolean $cascade Set to true to delete records that depend on this record + * @return boolean True on success + * @access public + * @see Model::del() + * @link http://book.cakephp.org/view/691/remove + */ + function remove($id = null, $cascade = true) { + return $this->del($id, $cascade); + } +/** + * Removes record for given ID. If no ID is given, the current ID is used. Returns true on success. + * + * @param mixed $id ID of record to delete + * @param boolean $cascade Set to true to delete records that depend on this record + * @return boolean True on success + * @access public + * @link http://book.cakephp.org/view/690/del + */ + function del($id = null, $cascade = true) { + if (!empty($id)) { + $this->id = $id; + } + $id = $this->id; + + if ($this->exists() && $this->beforeDelete($cascade)) { + $db =& ConnectionManager::getDataSource($this->useDbConfig); + if (!$this->Behaviors->trigger($this, 'beforeDelete', array($cascade), array('break' => true, 'breakOn' => false))) { + return false; + } + $this->_deleteDependent($id, $cascade); + $this->_deleteLinks($id); + $this->id = $id; + + if (!empty($this->belongsTo)) { + $keys = $this->find('first', array('fields' => $this->__collectForeignKeys())); + } + + if ($db->delete($this)) { + if (!empty($this->belongsTo)) { + $this->updateCounterCache($keys[$this->alias]); + } + $this->Behaviors->trigger($this, 'afterDelete'); + $this->afterDelete(); + $this->_clearCache(); + $this->id = false; + $this->__exists = null; + return true; + } + } + return false; + } +/** + * Alias for del(). + * + * @param mixed $id ID of record to delete + * @param boolean $cascade Set to true to delete records that depend on this record + * @return boolean True on success + * @access public + * @see Model::del() + */ + function delete($id = null, $cascade = true) { + return $this->del($id, $cascade); + } +/** + * Cascades model deletes through associated hasMany and hasOne child records. + * + * @param string $id ID of record that was deleted + * @param boolean $cascade Set to true to delete records that depend on this record + * @return void + * @access protected + */ + function _deleteDependent($id, $cascade) { + if (!empty($this->__backAssociation)) { + $savedAssociatons = $this->__backAssociation; + $this->__backAssociation = array(); + } + foreach (array_merge($this->hasMany, $this->hasOne) as $assoc => $data) { + if ($data['dependent'] === true && $cascade === true) { + + $model =& $this->{$assoc}; + $conditions = array($model->escapeField($data['foreignKey']) => $id); + if ($data['conditions']) { + $conditions = array_merge($data['conditions'], $conditions); + } + $model->recursive = -1; + + if (isset($data['exclusive']) && $data['exclusive']) { + $model->deleteAll($conditions); + } else { + $records = $model->find('all', array('conditions' => $conditions, 'fields' => $model->primaryKey)); + + if (!empty($records)) { + foreach ($records as $record) { + $model->delete($record[$model->alias][$model->primaryKey]); + } + } + } + } + } + if (isset($savedAssociatons)) { + $this->__backAssociation = $savedAssociatons; + } + } +/** + * Cascades model deletes through HABTM join keys. + * + * @param string $id ID of record that was deleted + * @return void + * @access protected + */ + function _deleteLinks($id) { + foreach ($this->hasAndBelongsToMany as $assoc => $data) { + $records = $this->{$data['with']}->find('all', array( + 'conditions' => array_merge(array($this->{$data['with']}->escapeField($data['foreignKey']) => $id)), + 'fields' => $this->{$data['with']}->primaryKey, + 'recursive' => -1 + )); + if (!empty($records)) { + foreach ($records as $record) { + $this->{$data['with']}->delete($record[$this->{$data['with']}->alias][$this->{$data['with']}->primaryKey]); + } + } + } + } +/** + * Deletes multiple model records based on a set of conditions. + * + * @param mixed $conditions Conditions to match + * @param boolean $cascade Set to true to delete records that depend on this record + * @param boolean $callbacks Run callbacks (not being used) + * @return boolean True on success, false on failure + * @access public + * @link http://book.cakephp.org/view/692/deleteAll + */ + function deleteAll($conditions, $cascade = true, $callbacks = false) { + if (empty($conditions)) { + return false; + } + $db =& ConnectionManager::getDataSource($this->useDbConfig); + + if (!$cascade && !$callbacks) { + return $db->delete($this, $conditions); + } else { + $ids = Set::extract( + $this->find('all', array_merge(array('fields' => "{$this->alias}.{$this->primaryKey}", 'recursive' => 0), compact('conditions'))), + "{n}.{$this->alias}.{$this->primaryKey}" + ); + + if (empty($ids)) { + return true; + } + + if ($callbacks) { + $_id = $this->id; + $result = true; + foreach ($ids as $id) { + $result = ($result && $this->delete($id, $cascade)); + } + $this->id = $_id; + return $result; + } else { + foreach ($ids as $id) { + $this->_deleteLinks($id); + if ($cascade) { + $this->_deleteDependent($id, $cascade); + } + } + return $db->delete($this, array($this->alias . '.' . $this->primaryKey => $ids)); + } + } + } +/** + * Collects foreign keys from associations. + * + * @return array + * @access private + */ + function __collectForeignKeys($type = 'belongsTo') { + $result = array(); + + foreach ($this->{$type} as $assoc => $data) { + if (isset($data['foreignKey']) && is_string($data['foreignKey'])) { + $result[$assoc] = $data['foreignKey']; + } + } + return $result; + } +/** + * Returns true if a record with the currently set ID exists. + * + * @param boolean $reset if true will force database query + * @return boolean True if such a record exists + * @access public + */ + function exists($reset = false) { + if (is_array($reset)) { + extract($reset, EXTR_OVERWRITE); + } + + if ($this->getID() === false || $this->useTable === false) { + return false; + } + if (!empty($this->__exists) && $reset !== true) { + return $this->__exists; + } + $conditions = array($this->alias . '.' . $this->primaryKey => $this->getID()); + $query = array('conditions' => $conditions, 'recursive' => -1, 'callbacks' => false); + + if (is_array($reset)) { + $query = array_merge($query, $reset); + } + return $this->__exists = ($this->find('count', $query) > 0); + } +/** + * Returns true if a record that meets given conditions exists. + * + * @param array $conditions SQL conditions array + * @return boolean True if such a record exists + * @access public + */ + function hasAny($conditions = null) { + return ($this->find('count', array('conditions' => $conditions, 'recursive' => -1)) != false); + } +/** + * Returns a result set array. + * + * Also used to perform new-notation finds, where the first argument is type of find operation to perform + * (all / first / count / neighbors / list / threaded ), + * second parameter options for finding ( indexed array, including: 'conditions', 'limit', + * 'recursive', 'page', 'fields', 'offset', 'order') + * + * Eg: find('all', array( + * 'conditions' => array('name' => 'Thomas Anderson'), + * 'fields' => array('name', 'email'), + * 'order' => 'field3 DESC', + * 'recursive' => 2, + * 'group' => 'type')); + * + * Specifying 'fields' for new-notation 'list': + * - If no fields are specified, then 'id' is used for key and 'model->displayField' is used for value. + * - If a single field is specified, 'id' is used for key and specified field is used for value. + * - If three fields are specified, they are used (in order) for key, value and group. + * - Otherwise, first and second fields are used for key and value. + * + * @param array $conditions SQL conditions array, or type of find operation (all / first / count / neighbors / list / threaded) + * @param mixed $fields Either a single string of a field name, or an array of field names, or options for matching + * @param string $order SQL ORDER BY conditions (e.g. "price DESC" or "name ASC") + * @param integer $recursive The number of levels deep to fetch associated records + * @return array Array of records + * @access public + * @link http://book.cakephp.org/view/449/find + */ + function find($conditions = null, $fields = array(), $order = null, $recursive = null) { + if (!is_string($conditions) || (is_string($conditions) && !array_key_exists($conditions, $this->_findMethods))) { + $type = 'first'; + $query = array_merge(compact('conditions', 'fields', 'order', 'recursive'), array('limit' => 1)); + } else { + list($type, $query) = array($conditions, $fields); + } + + $db =& ConnectionManager::getDataSource($this->useDbConfig); + $this->findQueryType = $type; + $this->id = $this->getID(); + + $query = array_merge( + array( + 'conditions' => null, 'fields' => null, 'joins' => array(), 'limit' => null, + 'offset' => null, 'order' => null, 'page' => null, 'group' => null, 'callbacks' => true + ), + (array)$query + ); + + if ($type != 'all') { + if ($this->_findMethods[$type] === true) { + $query = $this->{'_find' . ucfirst($type)}('before', $query); + } + } + + if (!is_numeric($query['page']) || intval($query['page']) < 1) { + $query['page'] = 1; + } + if ($query['page'] > 1 && !empty($query['limit'])) { + $query['offset'] = ($query['page'] - 1) * $query['limit']; + } + if ($query['order'] === null && $this->order !== null) { + $query['order'] = $this->order; + } + $query['order'] = array($query['order']); + + if ($query['callbacks'] === true || $query['callbacks'] === 'before') { + $return = $this->Behaviors->trigger($this, 'beforeFind', array($query), array( + 'break' => true, 'breakOn' => false, 'modParams' => true + )); + $query = (is_array($return)) ? $return : $query; + + if ($return === false) { + return null; + } + + $return = $this->beforeFind($query); + $query = (is_array($return)) ? $return : $query; + + if ($return === false) { + return null; + } + } + + $results = $db->read($this, $query); + $this->resetAssociations(); + $this->findQueryType = null; + + if ($query['callbacks'] === true || $query['callbacks'] === 'after') { + $results = $this->__filterResults($results); + } + + if ($type === 'all') { + return $results; + } else { + if ($this->_findMethods[$type] === true) { + return $this->{'_find' . ucfirst($type)}('after', $query, $results); + } + } + } +/** + * Handles the before/after filter logic for find('first') operations. Only called by Model::find(). + * + * @param string $state Either "before" or "after" + * @param array $query + * @param array $data + * @return array + * @access protected + * @see Model::find() + */ + function _findFirst($state, $query, $results = array()) { + if ($state == 'before') { + $query['limit'] = 1; + if (empty($query['conditions']) && !empty($this->id)) { + $query['conditions'] = array($this->escapeField() => $this->id); + } + return $query; + } elseif ($state == 'after') { + if (empty($results[0])) { + return false; + } + return $results[0]; + } + } +/** + * Handles the before/after filter logic for find('count') operations. Only called by Model::find(). + * + * @param string $state Either "before" or "after" + * @param array $query + * @param array $data + * @return int The number of records found, or false + * @access protected + * @see Model::find() + */ + function _findCount($state, $query, $results = array()) { + if ($state == 'before') { + $db =& ConnectionManager::getDataSource($this->useDbConfig); + if (empty($query['fields'])) { + $query['fields'] = $db->calculate($this, 'count'); + } elseif (is_string($query['fields']) && !preg_match('/count/i', $query['fields'])) { + $query['fields'] = $db->calculate($this, 'count', array( + $db->expression($query['fields']), 'count' + )); + } + $query['order'] = false; + return $query; + } elseif ($state == 'after') { + if (isset($results[0][0]['count'])) { + return intval($results[0][0]['count']); + } elseif (isset($results[0][$this->alias]['count'])) { + return intval($results[0][$this->alias]['count']); + } + return false; + } + } +/** + * Handles the before/after filter logic for find('list') operations. Only called by Model::find(). + * + * @param string $state Either "before" or "after" + * @param array $query + * @param array $data + * @return array Key/value pairs of primary keys/display field values of all records found + * @access protected + * @see Model::find() + */ + function _findList($state, $query, $results = array()) { + if ($state == 'before') { + if (empty($query['fields'])) { + $query['fields'] = array("{$this->alias}.{$this->primaryKey}", "{$this->alias}.{$this->displayField}"); + $list = array("{n}.{$this->alias}.{$this->primaryKey}", "{n}.{$this->alias}.{$this->displayField}", null); + } else { + if (!is_array($query['fields'])) { + $query['fields'] = String::tokenize($query['fields']); + } + + if (count($query['fields']) == 1) { + if (strpos($query['fields'][0], '.') === false) { + $query['fields'][0] = $this->alias . '.' . $query['fields'][0]; + } + + $list = array("{n}.{$this->alias}.{$this->primaryKey}", '{n}.' . $query['fields'][0], null); + $query['fields'] = array("{$this->alias}.{$this->primaryKey}", $query['fields'][0]); + } elseif (count($query['fields']) == 3) { + for ($i = 0; $i < 3; $i++) { + if (strpos($query['fields'][$i], '.') === false) { + $query['fields'][$i] = $this->alias . '.' . $query['fields'][$i]; + } + } + + $list = array('{n}.' . $query['fields'][0], '{n}.' . $query['fields'][1], '{n}.' . $query['fields'][2]); + } else { + for ($i = 0; $i < 2; $i++) { + if (strpos($query['fields'][$i], '.') === false) { + $query['fields'][$i] = $this->alias . '.' . $query['fields'][$i]; + } + } + + $list = array('{n}.' . $query['fields'][0], '{n}.' . $query['fields'][1], null); + } + } + if (!isset($query['recursive']) || $query['recursive'] === null) { + $query['recursive'] = -1; + } + list($query['list']['keyPath'], $query['list']['valuePath'], $query['list']['groupPath']) = $list; + return $query; + } elseif ($state == 'after') { + if (empty($results)) { + return array(); + } + $lst = $query['list']; + return Set::combine($results, $lst['keyPath'], $lst['valuePath'], $lst['groupPath']); + } + } +/** + * Detects the previous field's value, then uses logic to find the 'wrapping' + * rows and return them. + * + * @param string $state Either "before" or "after" + * @param mixed $query + * @param array $results + * @return array + * @access protected + */ + function _findNeighbors($state, $query, $results = array()) { + if ($state == 'before') { + $query = array_merge(array('recursive' => 0), $query); + extract($query); + $conditions = (array)$conditions; + if (isset($field) && isset($value)) { + if (strpos($field, '.') === false) { + $field = $this->alias . '.' . $field; + } + } else { + $field = $this->alias . '.' . $this->primaryKey; + $value = $this->id; + } + $query['conditions'] = array_merge($conditions, array($field . ' <' => $value)); + $query['order'] = $field . ' DESC'; + $query['limit'] = 1; + $query['field'] = $field; + $query['value'] = $value; + return $query; + } elseif ($state == 'after') { + extract($query); + unset($query['conditions'][$field . ' <']); + $return = array(); + if (isset($results[0])) { + $prevVal = Set::extract('/' . str_replace('.', '/', $field), $results[0]); + $query['conditions'][$field . ' >='] = $prevVal[0]; + $query['conditions'][$field . ' !='] = $value; + $query['limit'] = 2; + } else { + $return['prev'] = null; + $query['conditions'][$field . ' >'] = $value; + $query['limit'] = 1; + } + $query['order'] = $field . ' ASC'; + $return2 = $this->find('all', $query); + if (!array_key_exists('prev', $return)) { + $return['prev'] = $return2[0]; + } + if (count($return2) == 2) { + $return['next'] = $return2[1]; + } elseif (count($return2) == 1 && !$return['prev']) { + $return['next'] = $return2[0]; + } else { + $return['next'] = null; + } + return $return; + } + } +/** + * In the event of ambiguous results returned (multiple top level results, with different parent_ids) + * top level results with different parent_ids to the first result will be dropped + * + * @param mixed $state + * @param mixed $query + * @param array $results + * @return array Threaded results + * @access protected + */ + function _findThreaded($state, $query, $results = array()) { + if ($state == 'before') { + return $query; + } elseif ($state == 'after') { + $return = $idMap = array(); + $ids = Set::extract($results, '{n}.' . $this->alias . '.' . $this->primaryKey); + + foreach ($results as $result) { + $result['children'] = array(); + $id = $result[$this->alias][$this->primaryKey]; + $parentId = $result[$this->alias]['parent_id']; + if (isset($idMap[$id]['children'])) { + $idMap[$id] = array_merge($result, (array)$idMap[$id]); + } else { + $idMap[$id] = array_merge($result, array('children' => array())); + } + if (!$parentId || !in_array($parentId, $ids)) { + $return[] =& $idMap[$id]; + } else { + $idMap[$parentId]['children'][] =& $idMap[$id]; + } + } + if (count($return) > 1) { + $ids = array_unique(Set::extract('/' . $this->alias . '/parent_id', $return)); + if (count($ids) > 1) { + $root = $return[0][$this->alias]['parent_id']; + foreach ($return as $key => $value) { + if ($value[$this->alias]['parent_id'] != $root) { + unset($return[$key]); + } + } + } + } + return $return; + } + } +/** + * Passes query results through model and behavior afterFilter() methods. + * + * @param array Results to filter + * @param boolean $primary If this is the primary model results (results from model where the find operation was performed) + * @return array Set of filtered results + * @access private + */ + function __filterResults($results, $primary = true) { + $return = $this->Behaviors->trigger($this, 'afterFind', array($results, $primary), array('modParams' => true)); + if ($return !== true) { + $results = $return; + } + return $this->afterFind($results, $primary); + } +/** + * Called only when bindTo<ModelName>() is used. + * This resets the association arrays for the model back + * to those originally defined in the model. + * + * @return boolean Success + * @access public + */ + function resetAssociations() { + if (!empty($this->__backAssociation)) { + foreach ($this->__associations as $type) { + if (isset($this->__backAssociation[$type])) { + $this->{$type} = $this->__backAssociation[$type]; + } + } + $this->__backAssociation = array(); + } + + foreach ($this->__associations as $type) { + foreach ($this->{$type} as $key => $name) { + if (!empty($this->{$key}->__backAssociation)) { + $this->{$key}->resetAssociations(); + } + } + } + $this->__backAssociation = array(); + return true; + } +/** + * Returns false if any fields passed match any (by default, all if $or = false) of their matching values. + * + * @param array $fields Field/value pairs to search (if no values specified, they are pulled from $this->data) + * @param boolean $or If false, all fields specified must match in order for a false return value + * @return boolean False if any records matching any fields are found + * @access public + */ + function isUnique($fields, $or = true) { + if (!is_array($fields)) { + $fields = func_get_args(); + if (is_bool($fields[count($fields) - 1])) { + $or = $fields[count($fields) - 1]; + unset($fields[count($fields) - 1]); + } + } + + foreach ($fields as $field => $value) { + if (is_numeric($field)) { + unset($fields[$field]); + + $field = $value; + if (isset($this->data[$this->alias][$field])) { + $value = $this->data[$this->alias][$field]; + } else { + $value = null; + } + } + + if (strpos($field, '.') === false) { + unset($fields[$field]); + $fields[$this->alias . '.' . $field] = $value; + } + } + if ($or) { + $fields = array('or' => $fields); + } + if (!empty($this->id)) { + $fields[$this->alias . '.' . $this->primaryKey . ' !='] = $this->id; + } + return ($this->find('count', array('conditions' => $fields, 'recursive' => -1)) == 0); + } +/** + * Returns a resultset for a given SQL statement. Custom SQL queries should be performed with this method. + * + * @param string $sql SQL statement + * @return array Resultset + * @access public + * @link http://book.cakephp.org/view/456/query + */ + function query() { + $params = func_get_args(); + $db =& ConnectionManager::getDataSource($this->useDbConfig); + return call_user_func_array(array(&$db, 'query'), $params); + } +/** + * Returns true if all fields pass validation. + * + * @param string $options An optional array of custom options to be made available in the beforeValidate callback + * @return boolean True if there are no errors + * @access public + * @link http://book.cakephp.org/view/410/Validating-Data-from-the-Controller + */ + function validates($options = array()) { + $errors = $this->invalidFields($options); + if (is_array($errors)) { + return count($errors) === 0; + } + return $errors; + } +/** + * Returns an array of fields that have failed validation. + * + * @param string $options An optional array of custom options to be made available in the beforeValidate callback + * @return array Array of invalid fields + * @access public + * @link http://book.cakephp.org/view/410/Validating-Data-from-the-Controller + */ + function invalidFields($options = array()) { + if ( + !$this->Behaviors->trigger( + $this, + 'beforeValidate', + array($options), + array('break' => true, 'breakOn' => false) + ) || + $this->beforeValidate($options) === false + ) { + return $this->validationErrors; + } + + if (!isset($this->validate) || empty($this->validate)) { + return $this->validationErrors; + } + + $data = $this->data; + $methods = array_map('strtolower', get_class_methods($this)); + $behaviorMethods = array_keys($this->Behaviors->methods()); + + if (isset($data[$this->alias])) { + $data = $data[$this->alias]; + } elseif (!is_array($data)) { + $data = array(); + } + + $Validation =& Validation::getInstance(); + $this->exists(); + + $_validate = $this->validate; + $whitelist = $this->whitelist; + + if (array_key_exists('fieldList', $options)) { + $whitelist = $options['fieldList']; + } + + if (!empty($whitelist)) { + $validate = array(); + foreach ((array)$whitelist as $f) { + if (!empty($this->validate[$f])) { + $validate[$f] = $this->validate[$f]; + } + } + $this->validate = $validate; + } + + foreach ($this->validate as $fieldName => $ruleSet) { + if (!is_array($ruleSet) || (is_array($ruleSet) && isset($ruleSet['rule']))) { + $ruleSet = array($ruleSet); + } + $default = array( + 'allowEmpty' => null, + 'required' => null, + 'rule' => 'blank', + 'last' => false, + 'on' => null + ); + + foreach ($ruleSet as $index => $validator) { + if (!is_array($validator)) { + $validator = array('rule' => $validator); + } + $validator = array_merge($default, $validator); + + if (isset($validator['message'])) { + $message = $validator['message']; + } else { + $message = __('This field cannot be left blank', true); + } + + if ( + empty($validator['on']) || ($validator['on'] == 'create' && + !$this->__exists) || ($validator['on'] == 'update' && $this->__exists + )) { + $required = ( + (!isset($data[$fieldName]) && $validator['required'] === true) || + ( + isset($data[$fieldName]) && (empty($data[$fieldName]) && + !is_numeric($data[$fieldName])) && $validator['allowEmpty'] === false + ) + ); + + if ($required) { + $this->invalidate($fieldName, $message); + if ($validator['last']) { + break; + } + } elseif (array_key_exists($fieldName, $data)) { + if (empty($data[$fieldName]) && $data[$fieldName] != '0' && $validator['allowEmpty'] === true) { + break; + } + if (is_array($validator['rule'])) { + $rule = $validator['rule'][0]; + unset($validator['rule'][0]); + $ruleParams = array_merge(array($data[$fieldName]), array_values($validator['rule'])); + } else { + $rule = $validator['rule']; + $ruleParams = array($data[$fieldName]); + } + + $valid = true; + + if (in_array(strtolower($rule), $methods)) { + $ruleParams[] = $validator; + $ruleParams[0] = array($fieldName => $ruleParams[0]); + $valid = $this->dispatchMethod($rule, $ruleParams); + } elseif (in_array($rule, $behaviorMethods) || in_array(strtolower($rule), $behaviorMethods)) { + $ruleParams[] = $validator; + $ruleParams[0] = array($fieldName => $ruleParams[0]); + $valid = $this->Behaviors->dispatchMethod($this, $rule, $ruleParams); + } elseif (method_exists($Validation, $rule)) { + $valid = $Validation->dispatchMethod($rule, $ruleParams); + } elseif (!is_array($validator['rule'])) { + $valid = preg_match($rule, $data[$fieldName]); + } + + if (!$valid || (is_string($valid) && strlen($valid) > 0)) { + if (is_string($valid) && strlen($valid) > 0) { + $validator['message'] = $valid; + } elseif (!isset($validator['message'])) { + if (is_string($index)) { + $validator['message'] = $index; + } elseif (is_numeric($index) && count($ruleSet) > 1) { + $validator['message'] = $index + 1; + } else { + $validator['message'] = $message; + } + } + $this->invalidate($fieldName, $validator['message']); + + if ($validator['last']) { + break; + } + } + } + } + } + } + $this->validate = $_validate; + return $this->validationErrors; + } +/** + * Marks a field as invalid, optionally setting the name of validation + * rule (in case of multiple validation for field) that was broken. + * + * @param string $field The name of the field to invalidate + * @param mixed $value Name of validation rule that was not failed, or validation message to + * be returned. If no validation key is provided, defaults to true. + * @access public + */ + function invalidate($field, $value = true) { + if (!is_array($this->validationErrors)) { + $this->validationErrors = array(); + } + $this->validationErrors[$field] = $value; + } +/** + * Returns true if given field name is a foreign key in this model. + * + * @param string $field Returns true if the input string ends in "_id" + * @return boolean True if the field is a foreign key listed in the belongsTo array. + * @access public + */ + function isForeignKey($field) { + $foreignKeys = array(); + if (!empty($this->belongsTo)) { + foreach ($this->belongsTo as $assoc => $data) { + $foreignKeys[] = $data['foreignKey']; + } + } + return in_array($field, $foreignKeys); + } +/** + * Returns the display field for this model. + * + * @return string The name of the display field for this Model (i.e. 'name', 'title'). + * @access public + * @deprecated + */ + function getDisplayField() { + return $this->displayField; + } +/** + * Escapes the field name and prepends the model name. Escaping is done according to the current database driver's rules. + * + * @param string $field Field to escape (e.g: id) + * @param string $alias Alias for the model (e.g: Post) + * @return string The name of the escaped field for this Model (i.e. id becomes `Post`.`id`). + * @access public + */ + function escapeField($field = null, $alias = null) { + if (empty($alias)) { + $alias = $this->alias; + } + if (empty($field)) { + $field = $this->primaryKey; + } + $db =& ConnectionManager::getDataSource($this->useDbConfig); + if (strpos($field, $db->name($alias)) === 0) { + return $field; + } + return $db->name($alias . '.' . $field); + } +/** + * Returns the current record's ID + * + * @param integer $list Index on which the composed ID is located + * @return mixed The ID of the current record, false if no ID + * @access public + */ + function getID($list = 0) { + if (empty($this->id) || (is_array($this->id) && isset($this->id[0]) && empty($this->id[0]))) { + return false; + } + + if (!is_array($this->id)) { + return $this->id; + } + + if (empty($this->id)) { + return false; + } + + if (isset($this->id[$list]) && !empty($this->id[$list])) { + return $this->id[$list]; + } elseif (isset($this->id[$list])) { + return false; + } + + foreach ($this->id as $id) { + return $id; + } + + return false; + } +/** + * Returns the ID of the last record this model inserted. + * + * @return mixed Last inserted ID + * @access public + */ + function getLastInsertID() { + return $this->getInsertID(); + } +/** + * Returns the ID of the last record this model inserted. + * + * @return mixed Last inserted ID + * @access public + */ + function getInsertID() { + return $this->__insertID; + } +/** + * Sets the ID of the last record this model inserted + * + * @param mixed Last inserted ID + * @access public + */ + function setInsertID($id) { + $this->__insertID = $id; + } +/** + * Returns the number of rows returned from the last query. + * + * @return int Number of rows + * @access public + */ + function getNumRows() { + $db =& ConnectionManager::getDataSource($this->useDbConfig); + return $db->lastNumRows(); + } +/** + * Returns the number of rows affected by the last query. + * + * @return int Number of rows + * @access public + */ + function getAffectedRows() { + $db =& ConnectionManager::getDataSource($this->useDbConfig); + return $db->lastAffected(); + } +/** + * Sets the DataSource to which this model is bound. + * + * @param string $dataSource The name of the DataSource, as defined in app/config/database.php + * @return boolean True on success + * @access public + */ + function setDataSource($dataSource = null) { + $oldConfig = $this->useDbConfig; + + if ($dataSource != null) { + $this->useDbConfig = $dataSource; + } + $db =& ConnectionManager::getDataSource($this->useDbConfig); + if (!empty($oldConfig) && isset($db->config['prefix'])) { + $oldDb =& ConnectionManager::getDataSource($oldConfig); + + if (!isset($this->tablePrefix) || (!isset($oldDb->config['prefix']) || $this->tablePrefix == $oldDb->config['prefix'])) { + $this->tablePrefix = $db->config['prefix']; + } + } elseif (isset($db->config['prefix'])) { + $this->tablePrefix = $db->config['prefix']; + } + + if (empty($db) || $db == null || !is_object($db)) { + return $this->cakeError('missingConnection', array(array('className' => $this->alias))); + } + } +/** + * Gets the DataSource to which this model is bound. + * Not safe for use with some versions of PHP4, because this class is overloaded. + * + * @return object A DataSource object + * @access public + */ + function &getDataSource() { + $db =& ConnectionManager::getDataSource($this->useDbConfig); + return $db; + } +/** + * Gets all the models with which this model is associated. + * + * @param string $type Only result associations of this type + * @return array Associations + * @access public + */ + function getAssociated($type = null) { + if ($type == null) { + $associated = array(); + foreach ($this->__associations as $assoc) { + if (!empty($this->{$assoc})) { + $models = array_keys($this->{$assoc}); + foreach ($models as $m) { + $associated[$m] = $assoc; + } + } + } + return $associated; + } elseif (in_array($type, $this->__associations)) { + if (empty($this->{$type})) { + return array(); + } + return array_keys($this->{$type}); + } else { + $assoc = array_merge($this->hasOne, $this->hasMany, $this->belongsTo, $this->hasAndBelongsToMany); + if (array_key_exists($type, $assoc)) { + foreach ($this->__associations as $a) { + if (isset($this->{$a}[$type])) { + $assoc[$type]['association'] = $a; + break; + } + } + return $assoc[$type]; + } + return null; + } + } +/** + * Gets the name and fields to be used by a join model. This allows specifying join fields in the association definition. + * + * @param object $model The model to be joined + * @param mixed $with The 'with' key of the model association + * @param array $keys Any join keys which must be merged with the keys queried + * @return array + * @access public + */ + function joinModel($assoc, $keys = array()) { + if (is_string($assoc)) { + return array($assoc, array_keys($this->{$assoc}->schema())); + } elseif (is_array($assoc)) { + $with = key($assoc); + return array($with, array_unique(array_merge($assoc[$with], $keys))); + } else { + trigger_error(sprintf(__('Invalid join model settings in %s', true), $model->alias), E_USER_WARNING); + } + } +/** + * Called before each find operation. Return false if you want to halt the find + * call, otherwise return the (modified) query data. + * + * @param array $queryData Data used to execute this query, i.e. conditions, order, etc. + * @return mixed true if the operation should continue, false if it should abort; or, modified $queryData to continue with new $queryData + * @access public + * @link http://book.cakephp.org/view/680/beforeFind + */ + function beforeFind($queryData) { + return true; + } +/** + * Called after each find operation. Can be used to modify any results returned by find(). + * Return value should be the (modified) results. + * + * @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 Result of the find operation + * @access public + * @link http://book.cakephp.org/view/681/afterFind + */ + function afterFind($results, $primary = false) { + return $results; + } +/** + * Called before each save operation, after validation. Return a non-true result + * to halt the save. + * + * @return boolean True if the operation should continue, false if it should abort + * @access public + * @link http://book.cakephp.org/view/683/beforeSave + */ + function beforeSave($options = array()) { + return true; + } +/** + * Called after each successful save operation. + * + * @param boolean $created True if this save created a new record + * @access public + * @link http://book.cakephp.org/view/684/afterSave + */ + function afterSave($created) { + } +/** + * Called before every deletion operation. + * + * @param boolean $cascade If true records that depend on this record will also be deleted + * @return boolean True if the operation should continue, false if it should abort + * @access public + * @link http://book.cakephp.org/view/685/beforeDelete + */ + function beforeDelete($cascade = true) { + return true; + } +/** + * Called after every deletion operation. + * + * @access public + * @link http://book.cakephp.org/view/686/afterDelete + */ + function afterDelete() { + } +/** + * Called during save operations, before validation. Please note that custom + * validation rules can be defined in $validate. + * + * @return boolean True if validate operation should continue, false to abort + * @param $options array Options passed from model::save(), see $options of model::save(). + * @access public + * @link http://book.cakephp.org/view/682/beforeValidate + */ + function beforeValidate($options = array()) { + return true; + } +/** + * Called when a DataSource-level error occurs. + * + * @access public + * @link http://book.cakephp.org/view/687/onError + */ + function onError() { + } +/** + * Private method. Clears cache for this model. + * + * @param string $type If null this deletes cached views if Cache.check is true + * Will be used to allow deleting query cache also + * @return boolean true on delete + * @access protected + * @todo + */ + function _clearCache($type = null) { + if ($type === null) { + if (Configure::read('Cache.check') === true) { + $assoc[] = strtolower(Inflector::pluralize($this->alias)); + $assoc[] = strtolower(Inflector::underscore(Inflector::pluralize($this->alias))); + foreach ($this->__associations as $key => $association) { + foreach ($this->$association as $key => $className) { + $check = strtolower(Inflector::pluralize($className['className'])); + if (!in_array($check, $assoc)) { + $assoc[] = strtolower(Inflector::pluralize($className['className'])); + $assoc[] = strtolower(Inflector::underscore(Inflector::pluralize($className['className']))); + } + } + } + clearCache($assoc); + return true; + } + } else { + //Will use for query cache deleting + } + } +/** + * Called when serializing a model. + * + * @return array Set of object variable names this model has + * @access private + */ + function __sleep() { + $return = array_keys(get_object_vars($this)); + return $return; + } +/** + * Called when de-serializing a model. + * + * @access private + * @todo + */ + function __wakeup() { + } +/** + * @deprecated + * @see Model::find('all') + */ + function findAll($conditions = null, $fields = null, $order = null, $limit = null, $page = 1, $recursive = null) { + //trigger_error(__('(Model::findAll) Deprecated, use Model::find("all")', true), E_USER_WARNING); + return $this->find('all', compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive')); + } +/** + * @deprecated + * @see Model::find('count') + */ + function findCount($conditions = null, $recursive = 0) { + //trigger_error(__('(Model::findCount) Deprecated, use Model::find("count")', true), E_USER_WARNING); + return $this->find('count', compact('conditions', 'recursive')); + } +/** + * @deprecated + * @see Model::find('threaded') + */ + function findAllThreaded($conditions = null, $fields = null, $order = null) { + //trigger_error(__('(Model::findAllThreaded) Deprecated, use Model::find("threaded")', true), E_USER_WARNING); + return $this->find('threaded', compact('conditions', 'fields', 'order')); + } +/** + * @deprecated + * @see Model::find('neighbors') + */ + function findNeighbours($conditions = null, $field, $value) { + //trigger_error(__('(Model::findNeighbours) Deprecated, use Model::find("neighbors")', true), E_USER_WARNING); + $query = compact('conditions', 'field', 'value'); + $query['fields'] = $field; + if (is_array($field)) { + $query['field'] = $field[0]; + } + return $this->find('neighbors', $query); + } +} +if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) { + Overloadable::overload('Model'); +} +?> \ No newline at end of file diff --git a/cake/libs/model/schema.php b/cake/libs/model/schema.php new file mode 100755 index 0000000..d036899 --- /dev/null +++ b/cake/libs/model/schema.php @@ -0,0 +1,552 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Schema database management for CakePHP. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.model + * @since CakePHP(tm) v 1.2.0.5550 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +App::import('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'; +/** + * 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 (strtolower($this->name) === 'cake') { + $this->name = Inflector::camelize(Inflector::slug(Configure::read('App.dir'))); + } + + if (empty($options['path'])) { + $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('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; + } + } +/** + * 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; + } + + 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'); + + $tables = array(); + $currentTables = $db->listSources(); + + $prefix = null; + if (isset($db->config['prefix'])) { + $prefix = $db->config['prefix']; + } + + if (!is_array($models) && $models !== false) { + $models = Configure::listObjects('model'); + } + + if (is_array($models)) { + foreach ($models as $model) { + 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) { + $Object->setDataSource($connection); + $table = $db->fullTableName($Object, false); + + if (in_array($table, $currentTables)) { + $key = array_search($table, $currentTables); + if (empty($tables[$Object->table])) { + $tables[$Object->table] = $this->__columns($Object); + $tables[$Object->table]['indexes'] = $db->index($Object); + unset($currentTables[$key]); + } + if (!empty($Object->hasAndBelongsToMany)) { + foreach ($Object->hasAndBelongsToMany as $Assoc => $assocData) { + if (isset($assocData['with'])) { + $class = $assocData['with']; + } elseif ($assocData['_with']) { + $class = $assocData['_with']; + } + if (is_object($Object->$class)) { + $table = $db->fullTableName($Object->$class, false); + if (in_array($table, $currentTables)) { + $key = array_search($table, $currentTables); + $tables[$Object->$class->table] = $this->__columns($Object->$class); + $tables[$Object->$class->table]['indexes'] = $db->index($Object->$class); + 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); + } elseif ($models === false) { + $tables[$table] = $this->__columns($Object); + $tables[$table]['indexes'] = $db->index($Object); + } else { + $tables['missing'][$table] = $this->__columns($Object); + $tables['missing'][$table]['indexes'] = $db->index($Object); + } + } + } + + 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 .= "\tvar \${$table} = array(\n"; + if (is_array($fields)) { + $cols = array(); + foreach ($fields as $field => $value) { + if ($field != 'indexes') { + 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)); + } else { + $col = "\t\t'indexes' => array("; + $props = array(); + foreach ((array)$value as $key => $index) { + $props[] = "'{$key}' => array(" . join(', ', $this->__values($index)) . ")"; + } + $col .= join(', ', $props); + } + $col .= ")"; + $cols[] = $col; + } + $out .= join(",\n", $cols); + } + $out .= "\n\t);\n"; + } + } + $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; + } +/** + * 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') { + break; + } + if (!array_key_exists($table, $old)) { + $tables[$table]['add'] = $fields; + } else { + $diff = array_diff_assoc($fields, $old[$table]); + if (!empty($diff)) { + $tables[$table]['add'] = $diff; + } + $diff = array_diff_assoc($old[$table], $fields); + if (!empty($diff)) { + $tables[$table]['drop'] = $diff; + } + } + foreach ($fields as $field => $value) { + if (isset($old[$table][$field])) { + $diff = array_diff_assoc($value, $old[$table][$field]); + if (!empty($diff) && $field !== 'indexes') { + $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) { + $tables[$table]['drop']['indexes'] = $diff['drop']; + $tables[$table]['add']['indexes'] = $diff['add']; + } + } + } + return $tables; + } +/** + * 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('" . join("', '", $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('Schema generation error: invalid column type ' . $value['type'] . ' does not exist in DBO', 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 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 = array_diff_assoc($new, $old); + if (!empty($diff)) { + $add = $diff; + } + + $diff = array_diff_assoc($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')); + } +} +?> \ No newline at end of file diff --git a/cake/libs/multibyte.php b/cake/libs/multibyte.php new file mode 100755 index 0000000..724b659 --- /dev/null +++ b/cake/libs/multibyte.php @@ -0,0 +1,1126 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Multibyte handling methods. + * + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v 1.2.0.6833 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +if (function_exists('mb_internal_encoding')) { + $encoding = Configure::read('App.encoding'); + if (!empty($encoding)) { + mb_internal_encoding($encoding); + } +} +/** + * Find position of first occurrence of a case-insensitive string. + * + * @param string $haystack The string from which to get the position of the first occurrence of $needle. + * @param string $needle The string to find in $haystack. + * @param integer $offset The position in $haystack to start searching. + * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used. + * @return integer|boolean The numeric position of the first occurrence of $needle in the $haystack string, or false if $needle is not found. + */ +if (!function_exists('mb_stripos')) { + function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) { + return Multibyte::stripos($haystack, $needle, $offset); + } +} +/** + * Finds first occurrence of a string within another, case insensitive. + * + * @param string $haystack The string from which to get the first occurrence of $needle. + * @param string $needle The string to find in $haystack. + * @param boolean $part Determines which portion of $haystack this function returns. + * If set to true, it returns all of $haystack from the beginning to the first occurrence of $needle. + * If set to false, it returns all of $haystack from the first occurrence of $needle to the end, Default value is false. + * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used. + * @return string|boolean The portion of $haystack, or false if $needle is not found. + */ +if (!function_exists('mb_stristr')) { + function mb_stristr($haystack, $needle, $part = false, $encoding = null) { + return Multibyte::stristr($haystack, $needle, $part); + } +} +/** + * Get string length. + * + * @param string $string The string being checked for length. + * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used. + * @return integer The number of characters in string $string having character encoding encoding. + * A multi-byte character is counted as 1. + */ +if (!function_exists('mb_strlen')) { + function mb_strlen($string, $encoding = null) { + return Multibyte::strlen($string); + } +} +/** + * Find position of first occurrence of a string. + * + * @param string $haystack The string being checked. + * @param string $needle The position counted from the beginning of haystack. + * @param integer $offset The search offset. If it is not specified, 0 is used. + * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used. + * @return integer|boolean The numeric position of the first occurrence of $needle in the $haystack string. + * If $needle is not found, it returns false. + */ +if (!function_exists('mb_strpos')) { + function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) { + return Multibyte::strpos($haystack, $needle, $offset); + } +} +/** + * Finds the last occurrence of a character in a string within another. + * + * @param string $haystack The string from which to get the last occurrence of $needle. + * @param string $needle The string to find in $haystack. + * @param boolean $part Determines which portion of $haystack this function returns. + * If set to true, it returns all of $haystack from the beginning to the last occurrence of $needle. + * If set to false, it returns all of $haystack from the last occurrence of $needle to the end, Default value is false. + * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used. + * @return string|boolean The portion of $haystack. or false if $needle is not found. + */ +if (!function_exists('mb_strrchr')) { + function mb_strrchr($haystack, $needle, $part = false, $encoding = null) { + return Multibyte::strrchr($haystack, $needle, $part); + } +} +/** + * Finds the last occurrence of a character in a string within another, case insensitive. + * + * @param string $haystack The string from which to get the last occurrence of $needle. + * @param string $needle The string to find in $haystack. + * @param boolean $part Determines which portion of $haystack this function returns. + * If set to true, it returns all of $haystack from the beginning to the last occurrence of $needle. + * If set to false, it returns all of $haystack from the last occurrence of $needle to the end, Default value is false. + * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used. + * @return string|boolean The portion of $haystack. or false if $needle is not found. + */ +if (!function_exists('mb_strrichr')) { + function mb_strrichr($haystack, $needle, $part = false, $encoding = null) { + return Multibyte::strrichr($haystack, $needle, $part); + } +} +/** + * Finds position of last occurrence of a string within another, case insensitive + * + * @param string $haystack The string from which to get the position of the last occurrence of $needle. + * @param string $needle The string to find in $haystack. + * @param integer $offset The position in $haystack to start searching. + * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used. + * @return integer|boolean The numeric position of the last occurrence of $needle in the $haystack string, or false if $needle is not found. + */ +if (!function_exists('mb_strripos')) { + function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) { + return Multibyte::strripos($haystack, $needle, $offset); + } +} +/** + * Find position of last occurrence of a string in a string. + * + * @param string $haystack The string being checked, for the last occurrence of $needle. + * @param string $needle The string to find in $haystack. + * @param integer $offset May be specified to begin searching an arbitrary number of characters into the string. + * Negative values will stop searching at an arbitrary point prior to the end of the string. + * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used. + * @return integer|boolean The numeric position of the last occurrence of $needle in the $haystack string. If $needle is not found, it returns false. + */ +if (!function_exists('mb_strrpos')) { + function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) { + return Multibyte::strrpos($haystack, $needle, $offset); + } +} +/** + * Finds first occurrence of a string within another + * + * @param string $haystack The string from which to get the first occurrence of $needle. + * @param string $needle The string to find in $haystack + * @param boolean $part Determines which portion of $haystack this function returns. + * If set to true, it returns all of $haystack from the beginning to the first occurrence of $needle. + * If set to false, it returns all of $haystack from the first occurrence of $needle to the end, Default value is FALSE. + * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used. + * @return string|boolean The portion of $haystack, or true if $needle is not found. + */ +if (!function_exists('mb_strstr')) { + function mb_strstr($haystack, $needle, $part = false, $encoding = null) { + return Multibyte::strstr($haystack, $needle, $part); + } +} +/** + * Make a string lowercase + * + * @param string $string The string being lowercased. + * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used. + * @return string with all alphabetic characters converted to lowercase. + */ +if (!function_exists('mb_strtolower')) { + function mb_strtolower($string, $encoding = null) { + return Multibyte::strtolower($string); + } +} +/** + * Make a string uppercase + * + * @param string $string The string being uppercased. + * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used. + * @return string with all alphabetic characters converted to uppercase. + */ +if (!function_exists('mb_strtoupper')) { + function mb_strtoupper($string, $encoding = null) { + return Multibyte::strtoupper($string); + } +} +/** + * Count the number of substring occurrences + * + * @param string $haystack The string being checked. + * @param string $needle The string being found. + * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used. + * @return integer The number of times the $needle substring occurs in the $haystack string. + */ +if (!function_exists('mb_substr_count')) { + function mb_substr_count($haystack, $needle, $encoding = null) { + return Multibyte::substrCount($haystack, $needle); + } +} +/** + * Get part of string + * + * @param string $string The string being checked. + * @param integer $start The first position used in $string. + * @param integer $length The maximum length of the returned string. + * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used. + * @return string The portion of $string specified by the $string and $length parameters. + */ +if (!function_exists('mb_substr')) { + function mb_substr($string, $start, $length = null, $encoding = null) { + return Multibyte::substr($string, $start, $length); + } +} +/** + * Encode string for MIME header + * + * @param string $str The string being encoded + * @param string $charset specifies the name of the character set in which str is represented in. + * The default value is determined by the current NLS setting (mbstring.language). + * @param string $transfer_encoding specifies the scheme of MIME encoding. It should be either "B" (Base64) or "Q" (Quoted-Printable). + * Falls back to "B" if not given. + * @param string $linefeed specifies the EOL (end-of-line) marker with which mb_encode_mimeheader() performs line-folding + * (a » RFC term, the act of breaking a line longer than a certain length into multiple lines. + * The length is currently hard-coded to 74 characters). Falls back to "\r\n" (CRLF) if not given. + * @param integer $indent [definition unknown and appears to have no affect] + * @return string A converted version of the string represented in ASCII. + */ +if (!function_exists('mb_encode_mimeheader')) { + function mb_encode_mimeheader($str, $charset = 'UTF-8', $transfer_encoding = 'B', $linefeed = "\r\n", $indent = 1) { + return Multibyte::mimeEncode($str, $charset, $linefeed); + } +} +/** + * Multibyte handling methods. + * + * + * @package cake + * @subpackage cake.cake.libs + */ +class Multibyte extends Object { +/** + * Holds the case folding values + * + * @var array + * @access private + */ + var $__caseFold = array(); +/** + * Holds an array of Unicode code point ranges + * + * @var array + * @access private + */ + var $__codeRange = array(); +/** + * Holds the current code point range + * + * @var string + * @access private + */ + var $__table = null; +/** + * Gets a reference to the Multibyte object instance + * + * @return object Multibyte instance + * @access public + * @static + */ + function &getInstance() { + static $instance = array(); + + if (!$instance) { + $instance[0] =& new Multibyte(); + } + return $instance[0]; + } +/** + * Converts a multibyte character string + * to the decimal value of the character + * + * @param multibyte string $string + * @return array + * @access public + * @static + */ + function utf8($string) { + $map = array(); + + $values = array(); + $find = 1; + $length = strlen($string); + + for ($i = 0; $i < $length; $i++) { + $value = ord($string[$i]); + + if ($value < 128) { + $map[] = $value; + } else { + if (count($values) == 0) { + $find = ($value < 224) ? 2 : 3; + } + $values[] = $value; + + if (count($values) === $find) { + if ($find == 3) { + $map[] = (($values[0] % 16) * 4096) + (($values[1] % 64) * 64) + ($values[2] % 64); + } else { + $map[] = (($values[0] % 32) * 64) + ($values[1] % 64); + } + $values = array(); + $find = 1; + } + } + } + return $map; + } +/** + * Converts the decimal value of a multibyte character string + * to a string + * + * @param array $array + * @return string + * @access public + * @static + */ + function ascii($array) { + $ascii = ''; + + foreach ($array as $utf8) { + if ($utf8 < 128) { + $ascii .= chr($utf8); + } elseif ($utf8 < 2048) { + $ascii .= chr(192 + (($utf8 - ($utf8 % 64)) / 64)); + $ascii .= chr(128 + ($utf8 % 64)); + } else { + $ascii .= chr(224 + (($utf8 - ($utf8 % 4096)) / 4096)); + $ascii .= chr(128 + ((($utf8 % 4096) - ($utf8 % 64)) / 64)); + $ascii .= chr(128 + ($utf8 % 64)); + } + } + return $ascii; + } +/** + * Find position of first occurrence of a case-insensitive string. + * + * @param multi-byte string $haystack The string from which to get the position of the first occurrence of $needle. + * @param multi-byte string $needle The string to find in $haystack. + * @param integer $offset The position in $haystack to start searching. + * @return integer|boolean The numeric position of the first occurrence of $needle in the $haystack string, or false if $needle is not found. + * @access public + * @static + */ + function stripos($haystack, $needle, $offset = 0) { + if (!PHP5 || Multibyte::checkMultibyte($haystack)) { + $haystack = Multibyte::strtoupper($haystack); + $needle = Multibyte::strtoupper($needle); + return Multibyte::strpos($haystack, $needle, $offset); + } + return stripos($haystack, $needle, $offset); + } +/** + * Finds first occurrence of a string within another, case insensitive. + * + * @param string $haystack The string from which to get the first occurrence of $needle. + * @param string $needle The string to find in $haystack. + * @param boolean $part Determines which portion of $haystack this function returns. + * If set to true, it returns all of $haystack from the beginning to the first occurrence of $needle. + * If set to false, it returns all of $haystack from the first occurrence of $needle to the end, Default value is false. + * @return int|boolean The portion of $haystack, or false if $needle is not found. + * @access public + * @static + */ + function stristr($haystack, $needle, $part = false) { + $php = (PHP_VERSION < 5.3); + + if (($php && $part) || Multibyte::checkMultibyte($haystack)) { + $check = Multibyte::strtoupper($haystack); + $check = Multibyte::utf8($check); + $found = false; + + $haystack = Multibyte::utf8($haystack); + $haystackCount = count($haystack); + + $needle = Multibyte::strtoupper($needle); + $needle = Multibyte::utf8($needle); + $needleCount = count($needle); + + $parts = array(); + $position = 0; + + while (($found === false) && ($position < $haystackCount)) { + if (isset($needle[0]) && $needle[0] === $check[$position]) { + for ($i = 1; $i < $needleCount; $i++) { + if ($needle[$i] !== $check[$position + $i]) { + break; + } + } + if ($i === $needleCount) { + $found = true; + } + } + if (!$found) { + $parts[] = $haystack[$position]; + unset($haystack[$position]); + } + $position++; + } + + if ($found && $part && !empty($parts)) { + return Multibyte::ascii($parts); + } elseif ($found && !empty($haystack)) { + return Multibyte::ascii($haystack); + } + return false; + } + + if (!$php) { + return stristr($haystack, $needle, $part); + } + return stristr($haystack, $needle); + } +/** + * Get string length. + * + * @param string $string The string being checked for length. + * @return integer The number of characters in string $string + * @access public + * @static + */ + function strlen($string) { + if (Multibyte::checkMultibyte($string)) { + $string = Multibyte::utf8($string); + return count($string); + } + return strlen($string); + } +/** + * Find position of first occurrence of a string. + * + * @param string $haystack The string being checked. + * @param string $needle The position counted from the beginning of haystack. + * @param integer $offset The search offset. If it is not specified, 0 is used. + * @return integer|boolean The numeric position of the first occurrence of $needle in the $haystack string. + * If $needle is not found, it returns false. + * @access public + * @static + */ + function strpos($haystack, $needle, $offset = 0) { + if (Multibyte::checkMultibyte($haystack)) { + $found = false; + + $haystack = Multibyte::utf8($haystack); + $haystackCount = count($haystack); + + $needle = Multibyte::utf8($needle); + $needleCount = count($needle); + + $position = $offset; + + while (($found === false) && ($position < $haystackCount)) { + if (isset($needle[0]) && $needle[0] === $haystack[$position]) { + for ($i = 1; $i < $needleCount; $i++) { + if ($needle[$i] !== $haystack[$position + $i]) { + break; + } + } + if ($i === $needleCount) { + $found = true; + $position--; + } + } + $position++; + } + if ($found) { + return $position; + } + return false; + } + return strpos($haystack, $needle, $offset); + } +/** + * Finds the last occurrence of a character in a string within another. + * + * @param string $haystack The string from which to get the last occurrence of $needle. + * @param string $needle The string to find in $haystack. + * @param boolean $part Determines which portion of $haystack this function returns. + * If set to true, it returns all of $haystack from the beginning to the last occurrence of $needle. + * If set to false, it returns all of $haystack from the last occurrence of $needle to the end, Default value is false. + * @return string|boolean The portion of $haystack. or false if $needle is not found. + * @access public + * @static + */ + function strrchr($haystack, $needle, $part = false) { + $check = Multibyte::utf8($haystack); + $found = false; + + $haystack = Multibyte::utf8($haystack); + $haystackCount = count($haystack); + + $matches = array_count_values($check); + + $needle = Multibyte::utf8($needle); + $needleCount = count($needle); + + $parts = array(); + $position = 0; + + while (($found === false) && ($position < $haystackCount)) { + if (isset($needle[0]) && $needle[0] === $check[$position]) { + for ($i = 1; $i < $needleCount; $i++) { + if ($needle[$i] !== $check[$position + $i]) { + if ($needle[$i] === $check[($position + $i) -1]) { + $found = true; + } + unset($parts[$position - 1]); + $haystack = array_merge(array($haystack[$position]), $haystack); + break; + } + } + if (isset($matches[$needle[0]]) && $matches[$needle[0]] > 1) { + $matches[$needle[0]] = $matches[$needle[0]] - 1; + } elseif ($i === $needleCount) { + $found = true; + } + } + + if (!$found && isset($haystack[$position])) { + $parts[] = $haystack[$position]; + unset($haystack[$position]); + } + $position++; + } + + if ($found && $part && !empty($parts)) { + return Multibyte::ascii($parts); + } elseif ($found && !empty($haystack)) { + return Multibyte::ascii($haystack); + } + return false; + } +/** + * Finds the last occurrence of a character in a string within another, case insensitive. + * + * @param string $haystack The string from which to get the last occurrence of $needle. + * @param string $needle The string to find in $haystack. + * @param boolean $part Determines which portion of $haystack this function returns. + * If set to true, it returns all of $haystack from the beginning to the last occurrence of $needle. + * If set to false, it returns all of $haystack from the last occurrence of $needle to the end, Default value is false. + * @return string|boolean The portion of $haystack. or false if $needle is not found. + * @access public + * @static + */ + function strrichr($haystack, $needle, $part = false) { + $check = Multibyte::strtoupper($haystack); + $check = Multibyte::utf8($check); + $found = false; + + $haystack = Multibyte::utf8($haystack); + $haystackCount = count($haystack); + + $matches = array_count_values($check); + + $needle = Multibyte::strtoupper($needle); + $needle = Multibyte::utf8($needle); + $needleCount = count($needle); + + $parts = array(); + $position = 0; + + while (($found === false) && ($position < $haystackCount)) { + if (isset($needle[0]) && $needle[0] === $check[$position]) { + for ($i = 1; $i < $needleCount; $i++) { + if ($needle[$i] !== $check[$position + $i]) { + if ($needle[$i] === $check[($position + $i) -1]) { + $found = true; + } + unset($parts[$position - 1]); + $haystack = array_merge(array($haystack[$position]), $haystack); + break; + } + } + if (isset($matches[$needle[0]]) && $matches[$needle[0]] > 1) { + $matches[$needle[0]] = $matches[$needle[0]] - 1; + } elseif ($i === $needleCount) { + $found = true; + } + } + + if (!$found && isset($haystack[$position])) { + $parts[] = $haystack[$position]; + unset($haystack[$position]); + } + $position++; + } + + if ($found && $part && !empty($parts)) { + return Multibyte::ascii($parts); + } elseif ($found && !empty($haystack)) { + return Multibyte::ascii($haystack); + } + return false; + } +/** + * Finds position of last occurrence of a string within another, case insensitive + * + * @param string $haystack The string from which to get the position of the last occurrence of $needle. + * @param string $needle The string to find in $haystack. + * @param integer $offset The position in $haystack to start searching. + * @return integer|boolean The numeric position of the last occurrence of $needle in the $haystack string, or false if $needle is not found. + * @access public + * @static + */ + function strripos($haystack, $needle, $offset = 0) { + if (!PHP5 || Multibyte::checkMultibyte($haystack)) { + $found = false; + $haystack = Multibyte::strtoupper($haystack); + $haystack = Multibyte::utf8($haystack); + $haystackCount = count($haystack); + + $matches = array_count_values($haystack); + + $needle = Multibyte::strtoupper($needle); + $needle = Multibyte::utf8($needle); + $needleCount = count($needle); + + $position = $offset; + + while (($found === false) && ($position < $haystackCount)) { + if (isset($needle[0]) && $needle[0] === $haystack[$position]) { + for ($i = 1; $i < $needleCount; $i++) { + if ($needle[$i] !== $haystack[$position + $i]) { + if ($needle[$i] === $haystack[($position + $i) -1]) { + $position--; + $found = true; + continue; + } + } + } + + if (!$offset && isset($matches[$needle[0]]) && $matches[$needle[0]] > 1) { + $matches[$needle[0]] = $matches[$needle[0]] - 1; + } elseif ($i === $needleCount) { + $found = true; + $position--; + } + } + $position++; + } + return ($found) ? $position : false; + } + return strripos($haystack, $needle, $offset); + } + +/** + * Find position of last occurrence of a string in a string. + * + * @param string $haystack The string being checked, for the last occurrence of $needle. + * @param string $needle The string to find in $haystack. + * @param integer $offset May be specified to begin searching an arbitrary number of characters into the string. + * Negative values will stop searching at an arbitrary point prior to the end of the string. + * @return integer|boolean The numeric position of the last occurrence of $needle in the $haystack string. If $needle is not found, it returns false. + * @access public + * @static + */ + function strrpos($haystack, $needle, $offset = 0) { + if (!PHP5 || Multibyte::checkMultibyte($haystack)) { + $found = false; + + $haystack = Multibyte::utf8($haystack); + $haystackCount = count($haystack); + + $matches = array_count_values($haystack); + + $needle = Multibyte::utf8($needle); + $needleCount = count($needle); + + $position = $offset; + + while (($found === false) && ($position < $haystackCount)) { + if (isset($needle[0]) && $needle[0] === $haystack[$position]) { + for ($i = 1; $i < $needleCount; $i++) { + if ($needle[$i] !== $haystack[$position + $i]) { + if ($needle[$i] === $haystack[($position + $i) -1]) { + $position--; + $found = true; + continue; + } + } + } + + if (!$offset && isset($matches[$needle[0]]) && $matches[$needle[0]] > 1) { + $matches[$needle[0]] = $matches[$needle[0]] - 1; + } elseif ($i === $needleCount) { + $found = true; + $position--; + } + } + $position++; + } + return ($found) ? $position : false; + } + return strrpos($haystack, $needle, $offset); + } +/** + * Finds first occurrence of a string within another + * + * @param string $haystack The string from which to get the first occurrence of $needle. + * @param string $needle The string to find in $haystack + * @param boolean $part Determines which portion of $haystack this function returns. + * If set to true, it returns all of $haystack from the beginning to the first occurrence of $needle. + * If set to false, it returns all of $haystack from the first occurrence of $needle to the end, Default value is FALSE. + * @return string|boolean The portion of $haystack, or true if $needle is not found. + * @access public + * @static + */ + function strstr($haystack, $needle, $part = false) { + $php = (PHP_VERSION < 5.3); + + if (($php && $part) || Multibyte::checkMultibyte($haystack)) { + $check = Multibyte::utf8($haystack); + $found = false; + + $haystack = Multibyte::utf8($haystack); + $haystackCount = count($haystack); + + $needle = Multibyte::utf8($needle); + $needleCount = count($needle); + + $parts = array(); + $position = 0; + + while (($found === false) && ($position < $haystackCount)) { + if (isset($needle[0]) && $needle[0] === $check[$position]) { + for ($i = 1; $i < $needleCount; $i++) { + if ($needle[$i] !== $check[$position + $i]) { + break; + } + } + if ($i === $needleCount) { + $found = true; + } + } + if (!$found) { + $parts[] = $haystack[$position]; + unset($haystack[$position]); + } + $position++; + } + + if ($found && $part && !empty($parts)) { + return Multibyte::ascii($parts); + } elseif ($found && !empty($haystack)) { + return Multibyte::ascii($haystack); + } + return false; + } + + if (!$php) { + return strstr($haystack, $needle, $part); + } + return strstr($haystack, $needle); + } +/** + * Make a string lowercase + * + * @param string $string The string being lowercased. + * @return string with all alphabetic characters converted to lowercase. + * @access public + * @static + */ + function strtolower($string) { + $_this =& Multibyte::getInstance(); + $utf8Map = Multibyte::utf8($string); + + $length = count($utf8Map); + $lowerCase = array(); + $matched = false; + + for ($i = 0 ; $i < $length; $i++) { + $char = $utf8Map[$i]; + + if ($char < 128) { + $str = strtolower(chr($char)); + $strlen = strlen($str); + for ($ii = 0 ; $ii < $strlen; $ii++) { + $lower = ord(substr($str, $ii, 1)); + } + $lowerCase[] = $lower; + $matched = true; + } else { + $matched = false; + $keys = $_this->__find($char, 'upper'); + + if (!empty($keys)) { + foreach ($keys as $key => $value) { + if ($keys[$key]['upper'] == $char && count($keys[$key]['lower'][0]) === 1) { + $lowerCase[] = $keys[$key]['lower'][0]; + $matched = true; + break 1; + } + } + } + } + if ($matched === false) { + $lowerCase[] = $char; + } + } + return Multibyte::ascii($lowerCase); + } +/** + * Make a string uppercase + * + * @param string $string The string being uppercased. + * @param string $encoding Character encoding name to use. If it is omitted, internal character encoding is used. + * @return string with all alphabetic characters converted to uppercase. + * @access public + * @static + */ + function strtoupper($string) { + $_this =& Multibyte::getInstance(); + $utf8Map = Multibyte::utf8($string); + + $length = count($utf8Map); + $matched = false; + $replaced = array(); + $upperCase = array(); + + for ($i = 0 ; $i < $length; $i++) { + $char = $utf8Map[$i]; + + if ($char < 128) { + $str = strtoupper(chr($char)); + $strlen = strlen($str); + for ($ii = 0 ; $ii < $strlen; $ii++) { + $upper = ord(substr($str, $ii, 1)); + } + $upperCase[] = $upper; + $matched = true; + + } else { + $matched = false; + $keys = $_this->__find($char); + $keyCount = count($keys); + + if (!empty($keys)) { + foreach ($keys as $key => $value) { + $matched = false; + $replace = 0; + if ($length > 1 && count($keys[$key]['lower']) > 1) { + $j = 0; + + for ($ii = 0; $ii < count($keys[$key]['lower']); $ii++) { + $nextChar = $utf8Map[$i + $ii]; + + if (isset($nextChar) && ($nextChar == $keys[$key]['lower'][$j + $ii])) { + $replace++; + } + } + if ($replace == count($keys[$key]['lower'])) { + $upperCase[] = $keys[$key]['upper']; + $replaced = array_merge($replaced, array_values($keys[$key]['lower'])); + $matched = true; + break 1; + } + } elseif ($length > 1 && $keyCount > 1) { + $j = 0; + for ($ii = 1; $ii < $keyCount; $ii++) { + $nextChar = $utf8Map[$i + $ii - 1]; + + if (in_array($nextChar, $keys[$ii]['lower'])) { + + for ($jj = 0; $jj < count($keys[$ii]['lower']); $jj++) { + $nextChar = $utf8Map[$i + $jj]; + + if (isset($nextChar) && ($nextChar == $keys[$ii]['lower'][$j + $jj])) { + $replace++; + } + } + if ($replace == count($keys[$ii]['lower'])) { + $upperCase[] = $keys[$ii]['upper']; + $replaced = array_merge($replaced, array_values($keys[$ii]['lower'])); + $matched = true; + break 2; + } + } + } + } + if ($keys[$key]['lower'][0] == $char) { + $upperCase[] = $keys[$key]['upper']; + $matched = true; + break 1; + } + } + } + } + if ($matched === false && !in_array($char, $replaced, true)) { + $upperCase[] = $char; + } + } + return Multibyte::ascii($upperCase); + } +/** + * Count the number of substring occurrences + * + * @param string $haystack The string being checked. + * @param string $needle The string being found. + * @return integer The number of times the $needle substring occurs in the $haystack string. + * @access public + * @static + */ + function substrCount($haystack, $needle) { + $count = 0; + $haystack = Multibyte::utf8($haystack); + $haystackCount = count($haystack); + $matches = array_count_values($haystack); + $needle = Multibyte::utf8($needle); + $needleCount = count($needle); + + if ($needleCount === 1 && isset($matches[$needle[0]])) { + return $matches[$needle[0]]; + } + + for ($i = 0; $i < $haystackCount; $i++) { + if (isset($needle[0]) && $needle[0] === $haystack[$i]) { + for ($ii = 1; $ii < $needleCount; $ii++) { + if ($needle[$ii] === $haystack[$i + 1]) { + if ((isset($needle[$ii + 1]) && $haystack[$i + 2]) && $needle[$ii + 1] !== $haystack[$i + 2]) { + $count--; + } else { + $count++; + } + } + } + } + } + return $count; + } +/** + * Get part of string + * + * @param string $string The string being checked. + * @param integer $start The first position used in $string. + * @param integer $length The maximum length of the returned string. + * @return string The portion of $string specified by the $string and $length parameters. + * @access public + * @static + */ + function substr($string, $start, $length = null) { + if ($start === 0 && $length === null) { + return $string; + } + + $string = Multibyte::utf8($string); + $stringCount = count($string); + + for ($i = 1; $i <= $start; $i++) { + unset($string[$i - 1]); + } + + if ($length === null || count($string) < $length) { + return Multibyte::ascii($string); + } + $string = array_values($string); + + $value = array(); + for ($i = 0; $i < $length; $i++) { + $value[] = $string[$i]; + } + return Multibyte::ascii($value); + } +/** + * Prepare a string for mail transport, using the provided encoding + * + * @param string $string value to encode + * @param string $charset charset to use for encoding. defaults to UTF-8 + * @param string $newline + * @return string + * @access public + * @static + * @TODO: add support for 'Q'('Quoted Printable') encoding + */ + function mimeEncode($string, $charset = null, $newline = "\r\n") { + if (!Multibyte::checkMultibyte($string) && strlen($string) < 75) { + return $string; + } + + if (empty($charset)) { + $charset = Configure::read('App.encoding'); + } + $charset = strtoupper($charset); + + $start = '=?' . $charset . '?B?'; + $end = '?='; + $spacer = $end . $newline . ' ' . $start; + + $length = 75 - strlen($start) - strlen($end); + $length = $length - ($length % 4); + if ($charset == 'UTF-8') { + $parts = array(); + $maxchars = floor(($length * 3) / 4); + while (strlen($string) > $maxchars) { + $i = $maxchars; + $test = ord($string[$i]); + while ($test >= 128 && $test <= 191) { + $i--; + $test = ord($string[$i]); + } + $parts[] = base64_encode(substr($string, 0, $i)); + $string = substr($string, $i); + } + $parts[] = base64_encode($string); + $string = implode($spacer, $parts); + } else { + $string = chunk_split(base64_encode($string), $length, $spacer); + $string = preg_replace('/' . preg_quote($spacer) . '$/', '', $string); + } + return $start . $string . $end; + } +/** + * Return the Code points range for Unicode characters + * + * @param interger $decimal + * @return string + * @access private + */ + function __codepoint ($decimal) { + if ($decimal > 128 && $decimal < 256) { + $return = '0080_00ff'; // Latin-1 Supplement + } elseif ($decimal < 384) { + $return = '0100_017f'; // Latin Extended-A + } elseif ($decimal < 592) { + $return = '0180_024F'; // Latin Extended-B + } elseif ($decimal < 688) { + $return = '0250_02af'; // IPA Extensions + } elseif ($decimal >= 880 && $decimal < 1024) { + $return = '0370_03ff'; // Greek and Coptic + } elseif ($decimal < 1280) { + $return = '0400_04ff'; // Cyrillic + } elseif ($decimal < 1328) { + $return = '0500_052f'; // Cyrillic Supplement + } elseif ($decimal < 1424) { + $return = '0530_058f'; // Armenian + } elseif ($decimal >= 7680 && $decimal < 7936) { + $return = '1e00_1eff'; // Latin Extended Additional + } elseif ($decimal < 8192) { + $return = '1f00_1fff'; // Greek Extended + } elseif ($decimal >= 8448 && $decimal < 8528) { + $return = '2100_214f'; // Letterlike Symbols + } elseif ($decimal < 8592) { + $return = '2150_218f'; // Number Forms + } elseif ($decimal >= 9312 && $decimal < 9472) { + $return = '2460_24ff'; // Enclosed Alphanumerics + } elseif ($decimal >= 11264 && $decimal < 11360) { + $return = '2c00_2c5f'; // Glagolitic + } elseif ($decimal < 11392) { + $return = '2c60_2c7f'; // Latin Extended-C + } elseif ($decimal < 11520) { + $return = '2c80_2cff'; // Coptic + } elseif ($decimal >= 65280 && $decimal < 65520) { + $return = 'ff00_ffef'; // Halfwidth and Fullwidth Forms + } else { + $return = false; + } + $this->__codeRange[$decimal] = $return; + return $return; + } +/** + * Find the related code folding values for $char + * + * @param integer $char decimal value of character + * @param string $type + * @return array + * @access private + */ + function __find($char, $type = 'lower') { + $value = false; + $found = array(); + if (!isset($this->__codeRange[$char])) { + $range = $this->__codepoint($char); + if ($range === false) { + return null; + } + Configure::load('unicode' . DS . 'casefolding' . DS . $range); + $this->__caseFold[$range] = Configure::read($range); + Configure::delete($range); + } + + if (!$this->__codeRange[$char]) { + return null; + } + $this->__table = $this->__codeRange[$char]; + $count = count($this->__caseFold[$this->__table]); + + for ($i = 0; $i < $count; $i++) { + if ($type === 'lower' && $this->__caseFold[$this->__table][$i][$type][0] === $char) { + $found[] = $this->__caseFold[$this->__table][$i]; + } elseif ($type === 'upper' && $this->__caseFold[$this->__table][$i][$type] === $char) { + $found[] = $this->__caseFold[$this->__table][$i]; + } + } + return $found; + } +/** + * Check the $string for multibyte characters + * @param string $string value to test + * @return boolean + * @access public + * @static + */ + function checkMultibyte($string) { + $length = strlen($string); + + for ($i = 0; $i < $length; $i++ ) { + $value = ord(($string[$i])); + if ($value > 128) { + return true; + } + } + return false; + } +} +?> diff --git a/cake/libs/object.php b/cake/libs/object.php new file mode 100755 index 0000000..61c0896 --- /dev/null +++ b/cake/libs/object.php @@ -0,0 +1,298 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * 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://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * 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 { +/** + * Log object + * + * @var CakeLog + * @access protected + */ + var $_log = null; +/** + * 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. + * + * @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, and improves performance by using straight method calls + * in most cases. + * + * @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 + * + * @param $status see http://php.net/exit for values + * @return void + * @access public + */ + function _stop($status = 0) { + exit($status); + } +/** + * API for logging events. + * + * @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')) { + uses('cake_log'); + } + if (is_null($this->_log)) { + $this->_log = new CakeLog(); + } + if (!is_string($msg)) { + $msg = print_r($msg, true); + } + return $this->_log->write($type, $msg); + } +/** + * Allows setting of multiple properties of the object in a single line of code. + * + * @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::classify($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; + } + } +} +?> \ No newline at end of file diff --git a/cake/libs/overloadable.php b/cake/libs/overloadable.php new file mode 100755 index 0000000..a3b170a --- /dev/null +++ b/cake/libs/overloadable.php @@ -0,0 +1,41 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Overload abstraction interface. Merges differences between PHP4 and 5. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v 1.2 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * 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'); +} +?> \ No newline at end of file diff --git a/cake/libs/overloadable_php4.php b/cake/libs/overloadable_php4.php new file mode 100755 index 0000000..b60411c --- /dev/null +++ b/cake/libs/overloadable_php4.php @@ -0,0 +1,164 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Overload abstraction interface. Merges differences between PHP4 and 5. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v 1.2 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * 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'); + +?> \ No newline at end of file diff --git a/cake/libs/overloadable_php5.php b/cake/libs/overloadable_php5.php new file mode 100755 index 0000000..906b6b3 --- /dev/null +++ b/cake/libs/overloadable_php5.php @@ -0,0 +1,107 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Overload abstraction interface. Merges differences between PHP4 and 5. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v 1.2 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * 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); + } +} +?> \ No newline at end of file diff --git a/cake/libs/router.php b/cake/libs/router.php new file mode 100755 index 0000000..0b2b3b6 --- /dev/null +++ b/cake/libs/router.php @@ -0,0 +1,1367 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Parses the request URL into controller, action, and parameters. + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Included libraries. + * + */ +if (!class_exists('Object')) { + App::import('Core', 'Object'); +} +/** + * Parses the request URL into controller, action, and parameters. + * + * @package cake + * @subpackage cake.cake.libs + */ +class Router extends Object { +/** + * Array of routes + * + * @var array + * @access public + */ + var $routes = array(); +/** + * Caches admin setting from Configure class + * + * @var array + * @access private + */ + var $__admin = null; +/** + * List of action prefixes used in connected routes + * + * @var array + * @access private + */ + var $__prefixes = array(); +/** + * Directive for Router to parse out file extensions for mapping to Content-types. + * + * @var boolean + * @access private + */ + var $__parseExtensions = false; +/** + * List of valid extensions to parse from a URL. If null, any extension is allowed. + * + * @var array + * @access private + */ + var $__validExtensions = null; +/** + * 'Constant' regular expression definitions for named route elements + * + * @var array + * @access private + */ + var $__named = array( + 'Action' => 'index|show|add|create|edit|update|remove|del|delete|view|item', + 'Year' => '[12][0-9]{3}', + 'Month' => '0[1-9]|1[012]', + 'Day' => '0[1-9]|[12][0-9]|3[01]', + 'ID' => '[0-9]+', + 'UUID' => '[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}' + ); +/** + * Stores all information necessary to decide what named arguments are parsed under what conditions. + * + * @var string + * @access public + */ + var $named = array( + 'default' => array('page', 'fields', 'order', 'limit', 'recursive', 'sort', 'direction', 'step'), + 'greedy' => true, + 'separator' => ':', + 'rules' => false, + ); +/** + * The route matching the URL of the current request + * + * @var array + * @access private + */ + var $__currentRoute = array(); +/** + * HTTP header shortcut map. Used for evaluating header-based route expressions. + * + * @var array + * @access private + */ + var $__headerMap = array( + 'type' => 'content_type', + 'method' => 'request_method', + 'server' => 'server_name' + ); +/** + * Default HTTP request method => controller action map. + * + * @var array + * @access private + */ + var $__resourceMap = array( + array('action' => 'index', 'method' => 'GET', 'id' => false), + array('action' => 'view', 'method' => 'GET', 'id' => true), + array('action' => 'add', 'method' => 'POST', 'id' => false), + array('action' => 'edit', 'method' => 'PUT', 'id' => true), + array('action' => 'delete', 'method' => 'DELETE', 'id' => true), + array('action' => 'edit', 'method' => 'POST', 'id' => true) + ); +/** + * List of resource-mapped controllers + * + * @var array + * @access private + */ + var $__resourceMapped = array(); +/** + * Maintains the parameter stack for the current request + * + * @var array + * @access private + */ + var $__params = array(); +/** + * Maintains the path stack for the current request + * + * @var array + * @access private + */ + var $__paths = array(); +/** + * Keeps Router state to determine if default routes have already been connected + * + * @var boolean + * @access private + */ + var $__defaultsMapped = false; +/** + * Gets a reference to the Router object instance + * + * @return object Object instance + * @access public + * @static + */ + function &getInstance() { + static $instance = array(); + + if (!$instance) { + $instance[0] =& new Router(); + $instance[0]->__admin = Configure::read('Routing.admin'); + } + return $instance[0]; + } +/** + * Gets the named route elements for use in app/config/routes.php + * + * @return array Named route elements + * @access public + * @see Router::$__named + * @static + */ + function getNamedExpressions() { + $_this =& Router::getInstance(); + return $_this->__named; + } +/** + * Returns this object's routes array. Returns false if there are no routes available. + * + * @param string $route An empty string, or a route string "/" + * @param array $default NULL or an array describing the default route + * @param array $params An array matching the named elements in the route to regular expressions which that element should match. + * @see routes + * @return array Array of routes + * @access public + * @static + */ + function connect($route, $default = array(), $params = array()) { + $_this =& Router::getInstance(); + + if (!isset($default['action'])) { + $default['action'] = 'index'; + } + if (isset($default[$_this->__admin])) { + $default['prefix'] = $_this->__admin; + } + if (isset($default['prefix'])) { + $_this->__prefixes[] = $default['prefix']; + $_this->__prefixes = array_keys(array_flip($_this->__prefixes)); + } + $_this->routes[] = array($route, $default, $params); + return $_this->routes; + } +/** + * Specifies what named parameters CakePHP should be parsing. The most common setups are: + * + * Do not parse any named parameters: + * {{{ Router::connectNamed(false); }}} + * + * Parse only default parameters used for CakePHP's pagination: + * {{{ Router::connectNamed(false, array('default' => true)); }}} + * + * Parse only the page parameter if its value is a number: + * {{{ Router::connectNamed(array('page' => '[\d]+'), array('default' => false, 'greedy' => false)); }}} + * + * Parse only the page parameter no mater what. + * {{{ Router::connectNamed(array('page'), array('default' => false, 'greedy' => false)); }}} + * + * Parse only the page parameter if the current action is 'index'. + * {{{ Router::connectNamed(array('page' => array('action' => 'index')), array('default' => false, 'greedy' => false)); }}} + * + * Parse only the page parameter if the current action is 'index' and the controller is 'pages'. + * {{{ Router::connectNamed(array('page' => array('action' => 'index', 'controller' => 'pages')), array('default' => false, 'greedy' => false)); }}} + * + * @param array $named A list of named parameters. Key value pairs are accepted where values are either regex strings to match, or arrays as seen above. + * @param array $options Allows to control all settings: separator, greedy, reset, default + * @return array + * @access public + * @static + */ + function connectNamed($named, $options = array()) { + $_this =& Router::getInstance(); + + if (isset($options['argSeparator'])) { + $_this->named['separator'] = $options['argSeparator']; + unset($options['argSeparator']); + } + + if ($named === true || $named === false) { + $options = array_merge(array('default' => $named, 'reset' => true, 'greedy' => $named), $options); + $named = array(); + } + $options = array_merge(array('default' => false, 'reset' => false, 'greedy' => true), $options); + + if ($options['reset'] == true || $_this->named['rules'] === false) { + $_this->named['rules'] = array(); + } + + if ($options['default']) { + $named = array_merge($named, $_this->named['default']); + } + + foreach ($named as $key => $val) { + if (is_numeric($key)) { + $_this->named['rules'][$val] = true; + } else { + $_this->named['rules'][$key] = $val; + } + } + $_this->named['greedy'] = $options['greedy']; + return $_this->named; + } +/** + * Creates REST resource routes for the given controller(s) + * + * Options: + * + * - 'id' - The regular expression fragment to use when matching IDs. By default, matches + * integer values and UUIDs. + * - 'prefix' - URL prefix to use for the generated routes. Defaults to '/'. + * + * @param mixed $controller A controller name or array of controller names (i.e. "Posts" or "ListItems") + * @param array $options Options to use when generating REST routes + * @return void + * @access public + * @static + */ + function mapResources($controller, $options = array()) { + $_this =& Router::getInstance(); + $options = array_merge(array('prefix' => '/', 'id' => $_this->__named['ID'] . '|' . $_this->__named['UUID']), $options); + $prefix = $options['prefix']; + + foreach ((array)$controller as $ctlName) { + $urlName = Inflector::underscore($ctlName); + + foreach ($_this->__resourceMap as $params) { + extract($params); + $url = $prefix . $urlName . (($id) ? '/:id' : ''); + + Router::connect($url, + array('controller' => $urlName, 'action' => $action, '[method]' => $params['method']), + array('id' => $options['id'], 'pass' => array('id')) + ); + } + $_this->__resourceMapped[] = $urlName; + } + } +/** + * Builds a route regular expression + * + * @param string $route An empty string, or a route string "/" + * @param array $default NULL or an array describing the default route + * @param array $params An array matching the named elements in the route to regular expressions which that element should match. + * @return array + * @see routes + * @access public + * @static + */ + function writeRoute($route, $default, $params) { + if (empty($route) || ($route === '/')) { + return array('/^[\/]*$/', array()); + } + $names = array(); + $elements = explode('/', $route); + + foreach ($elements as $element) { + if (empty($element)) { + continue; + } + $q = null; + $element = trim($element); + $namedParam = strpos($element, ':') !== false; + + if ($namedParam && preg_match('/^:([^:]+)$/', $element, $r)) { + if (isset($params[$r[1]])) { + if ($r[1] != 'plugin' && array_key_exists($r[1], $default)) { + $q = '?'; + } + $parsed[] = '(?:/(' . $params[$r[1]] . ')' . $q . ')' . $q; + } else { + $parsed[] = '(?:/([^\/]+))?'; + } + $names[] = $r[1]; + } elseif ($element === '*') { + $parsed[] = '(?:/(.*))?'; + } else if ($namedParam && preg_match_all('/(?!\\\\):([a-z_0-9]+)/i', $element, $matches)) { + $matchCount = count($matches[1]); + + foreach ($matches[1] as $i => $name) { + $pos = strpos($element, ':' . $name); + $before = substr($element, 0, $pos); + $element = substr($element, $pos + strlen($name) + 1); + $after = null; + + if ($i + 1 === $matchCount && $element) { + $after = preg_quote($element); + } + + if ($i === 0) { + $before = '/' . $before; + } + $before = preg_quote($before, '#'); + + if (isset($params[$name])) { + if (isset($default[$name]) && $name != 'plugin') { + $q = '?'; + } + $parsed[] = '(?:' . $before . '(' . $params[$name] . ')' . $q . $after . ')' . $q; + } else { + $parsed[] = '(?:' . $before . '([^\/]+)' . $after . ')?'; + } + $names[] = $name; + } + } else { + $parsed[] = '/' . $element; + } + } + return array('#^' . join('', $parsed) . '[\/]*$#', $names); + } +/** + * Returns the list of prefixes used in connected routes + * + * @return array A list of prefixes used in connected routes + * @access public + * @static + */ + function prefixes() { + $_this =& Router::getInstance(); + return $_this->__prefixes; + } +/** + * Parses given URL and returns an array of controllers, action and parameters + * taken from that URL. + * + * @param string $url URL to be parsed + * @return array Parsed elements from URL + * @access public + * @static + */ + function parse($url) { + $_this =& Router::getInstance(); + if (!$_this->__defaultsMapped) { + $_this->__connectDefaultRoutes(); + } + $out = array('pass' => array(), 'named' => array()); + $r = $ext = null; + + if (ini_get('magic_quotes_gpc') === '1') { + $url = stripslashes_deep($url); + } + + if ($url && strpos($url, '/') !== 0) { + $url = '/' . $url; + } + if (strpos($url, '?') !== false) { + $url = substr($url, 0, strpos($url, '?')); + } + extract($_this->__parseExtension($url)); + + foreach ($_this->routes as $i => $route) { + if (count($route) === 3) { + $route = $_this->compile($i); + } + + if (($r = $_this->__matchRoute($route, $url)) !== false) { + $_this->__currentRoute[] = $route; + list($route, $regexp, $names, $defaults, $params) = $route; + $argOptions = array(); + + if (array_key_exists('named', $params)) { + $argOptions['named'] = $params['named']; + unset($params['named']); + } + if (array_key_exists('greedy', $params)) { + $argOptions['greedy'] = $params['greedy']; + unset($params['greedy']); + } + array_shift($r); + + foreach ($names as $name) { + $out[$name] = null; + } + if (is_array($defaults)) { + foreach ($defaults as $name => $value) { + if (preg_match('#[a-zA-Z_\-]#i', $name)) { + $out[$name] = $value; + } else { + $out['pass'][] = $value; + } + } + } + + foreach ($r as $key => $found) { + if (empty($found) && $found != 0) { + continue; + } + + if (isset($names[$key])) { + $out[$names[$key]] = $_this->stripEscape($found); + } else { + $argOptions['context'] = array('action' => $out['action'], 'controller' => $out['controller']); + extract($_this->getArgs($found, $argOptions)); + $out['pass'] = array_merge($out['pass'], $pass); + $out['named'] = $named; + } + } + + if (isset($params['pass'])) { + for ($j = count($params['pass']) - 1; $j > -1; $j--) { + if (isset($out[$params['pass'][$j]])) { + array_unshift($out['pass'], $out[$params['pass'][$j]]); + } + } + } + break; + } + } + + if (!empty($ext)) { + $out['url']['ext'] = $ext; + } + return $out; + } +/** + * Checks to see if the given URL matches the given route + * + * @param array $route + * @param string $url + * @return mixed Boolean false on failure, otherwise array + * @access private + */ + function __matchRoute($route, $url) { + list($route, $regexp, $names, $defaults) = $route; + + if (!preg_match($regexp, $url, $r)) { + return false; + } else { + foreach ($defaults as $key => $val) { + if ($key{0} === '[' && preg_match('/^\[(\w+)\]$/', $key, $header)) { + if (isset($this->__headerMap[$header[1]])) { + $header = $this->__headerMap[$header[1]]; + } else { + $header = 'http_' . $header[1]; + } + + $val = (array)$val; + $h = false; + + foreach ($val as $v) { + if (env(strtoupper($header)) === $v) { + $h = true; + } + } + if (!$h) { + return false; + } + } + } + } + return $r; + } +/** + * Compiles a route by numeric key and returns the compiled expression, replacing + * the existing uncompiled route. Do not call statically. + * + * @param integer $i + * @return array Returns an array containing the compiled route + * @access public + */ + function compile($i) { + $route = $this->routes[$i]; + + list($pattern, $names) = $this->writeRoute($route[0], $route[1], $route[2]); + $this->routes[$i] = array( + $route[0], $pattern, $names, + array_merge(array('plugin' => null, 'controller' => null), (array)$route[1]), + $route[2] + ); + return $this->routes[$i]; + } +/** + * Parses a file extension out of a URL, if Router::parseExtensions() is enabled. + * + * @param string $url + * @return array Returns an array containing the altered URL and the parsed extension. + * @access private + */ + function __parseExtension($url) { + $ext = null; + + if ($this->__parseExtensions) { + if (preg_match('/\.[0-9a-zA-Z]*$/', $url, $match) === 1) { + $match = substr($match[0], 1); + if (empty($this->__validExtensions)) { + $url = substr($url, 0, strpos($url, '.' . $match)); + $ext = $match; + } else { + foreach ($this->__validExtensions as $name) { + if (strcasecmp($name, $match) === 0) { + $url = substr($url, 0, strpos($url, '.' . $name)); + $ext = $match; + } + } + } + } + if (empty($ext)) { + $ext = 'html'; + } + } + return compact('ext', 'url'); + } +/** + * Connects the default, built-in routes, including admin routes, and (deprecated) web services + * routes. + * + * @return void + * @access private + */ + function __connectDefaultRoutes() { + if ($this->__defaultsMapped) { + return; + } + + if ($this->__admin) { + $params = array('prefix' => $this->__admin, $this->__admin => true); + } + + if ($plugins = Configure::listObjects('plugin')) { + foreach ($plugins as $key => $value) { + $plugins[$key] = Inflector::underscore($value); + } + + $match = array('plugin' => implode('|', $plugins)); + $this->connect('/:plugin/:controller/:action/*', array(), $match); + + if ($this->__admin) { + $this->connect("/{$this->__admin}/:plugin/:controller", $params, $match); + $this->connect("/{$this->__admin}/:plugin/:controller/:action/*", $params, $match); + } + } + + if ($this->__admin) { + $this->connect("/{$this->__admin}/:controller", $params); + $this->connect("/{$this->__admin}/:controller/:action/*", $params); + } + $this->connect('/:controller', array('action' => 'index')); + $this->connect('/:controller/:action/*'); + + if ($this->named['rules'] === false) { + $this->connectNamed(true); + } + $this->__defaultsMapped = true; + } +/** + * Takes parameter and path information back from the Dispatcher + * + * @param array $params Parameters and path information + * @return void + * @access public + * @static + */ + function setRequestInfo($params) { + $_this =& Router::getInstance(); + $defaults = array('plugin' => null, 'controller' => null, 'action' => null); + $params[0] = array_merge($defaults, (array)$params[0]); + $params[1] = array_merge($defaults, (array)$params[1]); + list($_this->__params[], $_this->__paths[]) = $params; + + if (count($_this->__paths)) { + if (isset($_this->__paths[0]['namedArgs'])) { + foreach ($_this->__paths[0]['namedArgs'] as $arg => $value) { + $_this->named['rules'][$arg] = true; + } + } + } + } +/** + * Gets parameter information + * + * @param boolean $current Get current parameter (true) + * @return array Parameter information + * @access public + * @static + */ + function getParams($current = false) { + $_this =& Router::getInstance(); + if ($current) { + return $_this->__params[count($_this->__params) - 1]; + } + if (isset($_this->__params[0])) { + return $_this->__params[0]; + } + return array(); + } +/** + * Gets URL parameter by name + * + * @param string $name Parameter name + * @param boolean $current Current parameter + * @return string Parameter value + * @access public + * @static + */ + function getParam($name = 'controller', $current = false) { + $params = Router::getParams($current); + if (isset($params[$name])) { + return $params[$name]; + } + return null; + } +/** + * Gets path information + * + * @param boolean $current Current parameter + * @return array + * @access public + * @static + */ + function getPaths($current = false) { + $_this =& Router::getInstance(); + if ($current) { + return $_this->__paths[count($_this->__paths) - 1]; + } + if (!isset($_this->__paths[0])) { + return array('base' => null); + } + return $_this->__paths[0]; + } +/** + * Reloads default Router settings + * + * @access public + * @return void + * @static + */ + function reload() { + $_this =& Router::getInstance(); + foreach (get_class_vars('Router') as $key => $val) { + $_this->{$key} = $val; + } + $_this->__admin = Configure::read('Routing.admin'); + } +/** + * Promote a route (by default, the last one added) to the beginning of the list + * + * @param $which A zero-based array index representing the route to move. For example, + * if 3 routes have been added, the last route would be 2. + * @return boolean Retuns false if no route exists at the position specified by $which. + * @access public + * @static + */ + function promote($which = null) { + $_this =& Router::getInstance(); + if ($which === null) { + $which = count($_this->routes) - 1; + } + if (!isset($_this->routes[$which])) { + return false; + } + $route = $_this->routes[$which]; + unset($_this->routes[$which]); + array_unshift($_this->routes, $route); + return true; + } +/** + * Finds URL for specified action. + * + * Returns an URL pointing to a combination of controller and action. Param + * $url can be: + * + * - Empty - the method will find adress to actuall controller/action. + * - '/' - the method will find base URL of application. + * - A combination of controller/action - the method will find url for it. + * + * @param mixed $url Cake-relative URL, like "/products/edit/92" or "/presidents/elect/4" + * or an array specifying any of the following: 'controller', 'action', + * and/or 'plugin', in addition to named arguments (keyed array elements), + * and standard URL arguments (indexed array elements) + * @param mixed $full If (bool) true, the full base URL will be prepended to the result. + * If an array accepts the following keys + * - escape - used when making urls embedded in html escapes query string '&' + * - full - if true the full base URL will be prepended. + * @return string Full translated URL with base path. + * @access public + * @static + */ + function url($url = null, $full = false) { + $_this =& Router::getInstance(); + $defaults = $params = array('plugin' => null, 'controller' => null, 'action' => 'index'); + + if (is_bool($full)) { + $escape = false; + } else { + extract(array_merge(array('escape' => false, 'full' => false), $full)); + } + + if (!empty($_this->__params)) { + if (isset($this) && !isset($this->params['requested'])) { + $params = $_this->__params[0]; + } else { + $params = end($_this->__params); + } + if (isset($params['prefix']) && strpos($params['action'], $params['prefix']) === 0) { + $params['action'] = substr($params['action'], strlen($params['prefix']) + 1); + } + } + $path = array('base' => null); + + if (!empty($_this->__paths)) { + if (isset($this) && !isset($this->params['requested'])) { + $path = $_this->__paths[0]; + } else { + $path = end($_this->__paths); + } + } + $base = $path['base']; + $extension = $output = $mapped = $q = $frag = null; + + if (is_array($url)) { + if (isset($url['base']) && $url['base'] === false) { + $base = null; + unset($url['base']); + } + if (isset($url['full_base']) && $url['full_base'] === true) { + $full = true; + unset($url['full_base']); + } + if (isset($url['?'])) { + $q = $url['?']; + unset($url['?']); + } + if (isset($url['#'])) { + $frag = '#' . urlencode($url['#']); + unset($url['#']); + } + if (empty($url['action'])) { + if (empty($url['controller']) || $params['controller'] === $url['controller']) { + $url['action'] = $params['action']; + } else { + $url['action'] = 'index'; + } + } + if ($_this->__admin) { + if (!isset($url[$_this->__admin]) && !empty($params[$_this->__admin])) { + $url[$_this->__admin] = true; + } elseif ($_this->__admin && isset($url[$_this->__admin]) && !$url[$_this->__admin]) { + unset($url[$_this->__admin]); + } + } + $plugin = false; + + if (array_key_exists('plugin', $url)) { + $plugin = $url['plugin']; + } + + $url = array_merge(array('controller' => $params['controller'], 'plugin' => $params['plugin']), Set::filter($url, true)); + + if ($plugin !== false) { + $url['plugin'] = $plugin; + } + + if (isset($url['ext'])) { + $extension = '.' . $url['ext']; + unset($url['ext']); + } + $match = false; + + foreach ($_this->routes as $i => $route) { + if (count($route) === 3) { + $route = $_this->compile($i); + } + $originalUrl = $url; + + if (isset($route[4]['persist'], $_this->__params[0])) { + $url = array_merge(array_intersect_key($params, Set::combine($route[4]['persist'], '/')), $url); + } + if ($match = $_this->mapRouteElements($route, $url)) { + $output = trim($match, '/'); + $url = array(); + break; + } + $url = $originalUrl; + } + + $named = $args = array(); + $skip = array( + 'bare', 'action', 'controller', 'plugin', 'ext', '?', '#', 'prefix', $_this->__admin + ); + + $keys = array_values(array_diff(array_keys($url), $skip)); + $count = count($keys); + + // Remove this once parsed URL parameters can be inserted into 'pass' + for ($i = 0; $i < $count; $i++) { + if ($i === 0 && is_numeric($keys[$i]) && in_array('id', $keys)) { + $args[0] = $url[$keys[$i]]; + } elseif (is_numeric($keys[$i]) || $keys[$i] === 'id') { + $args[] = $url[$keys[$i]]; + } else { + $named[$keys[$i]] = $url[$keys[$i]]; + } + } + + if ($match === false) { + list($args, $named) = array(Set::filter($args, true), Set::filter($named)); + if (!empty($url[$_this->__admin])) { + $url['action'] = str_replace($_this->__admin . '_', '', $url['action']); + } + + if (empty($named) && empty($args) && (!isset($url['action']) || $url['action'] === 'index')) { + $url['action'] = null; + } + + $urlOut = Set::filter(array($url['controller'], $url['action'])); + + if (isset($url['plugin']) && $url['plugin'] != $url['controller']) { + array_unshift($urlOut, $url['plugin']); + } + + if ($_this->__admin && isset($url[$_this->__admin])) { + array_unshift($urlOut, $_this->__admin); + } + $output = join('/', $urlOut) . '/'; + } + + if (!empty($args)) { + $args = join('/', $args); + if ($output{strlen($output) - 1} != '/') { + $args = '/'. $args; + } + $output .= $args; + } + + if (!empty($named)) { + foreach ($named as $name => $value) { + $output .= '/' . $name . $_this->named['separator'] . $value; + } + } + $output = str_replace('//', '/', $base . '/' . $output); + } else { + if (((strpos($url, '://')) || (strpos($url, 'javascript:') === 0) || (strpos($url, 'mailto:') === 0)) || (!strncmp($url, '#', 1))) { + return $url; + } + if (empty($url)) { + if (!isset($path['here'])) { + $path['here'] = '/'; + } + $output = $path['here']; + } elseif (substr($url, 0, 1) === '/') { + $output = $base . $url; + } else { + $output = $base . '/'; + if ($_this->__admin && isset($params[$_this->__admin])) { + $output .= $_this->__admin . '/'; + } + if (!empty($params['plugin']) && $params['plugin'] !== $params['controller']) { + $output .= Inflector::underscore($params['plugin']) . '/'; + } + $output .= Inflector::underscore($params['controller']) . '/' . $url; + } + $output = str_replace('//', '/', $output); + } + if ($full && defined('FULL_BASE_URL')) { + $output = FULL_BASE_URL . $output; + } + if (!empty($extension) && substr($output, -1) === '/') { + $output = substr($output, 0, -1); + } + + return $output . $extension . $_this->queryString($q, array(), $escape) . $frag; + } +/** + * Maps a URL array onto a route and returns the string result, or false if no match + * + * @param array $route Route Route + * @param array $url URL URL to map + * @return mixed Result (as string) or false if no match + * @access public + * @static + */ + function mapRouteElements($route, $url) { + if (isset($route[3]['prefix'])) { + $prefix = $route[3]['prefix']; + unset($route[3]['prefix']); + } + + $pass = array(); + $defaults = $route[3]; + $routeParams = $route[2]; + $params = Set::diff($url, $defaults); + $urlInv = array_combine(array_values($url), array_keys($url)); + + $i = 0; + while (isset($defaults[$i])) { + if (isset($urlInv[$defaults[$i]])) { + if (!in_array($defaults[$i], $url) && is_int($urlInv[$defaults[$i]])) { + return false; + } + unset($urlInv[$defaults[$i]], $defaults[$i]); + } else { + return false; + } + $i++; + } + + foreach ($params as $key => $value) { + if (is_int($key)) { + $pass[] = $value; + unset($params[$key]); + } + } + list($named, $params) = Router::getNamedElements($params); + + if (!strpos($route[0], '*') && (!empty($pass) || !empty($named))) { + return false; + } + + $urlKeys = array_keys($url); + $paramsKeys = array_keys($params); + $defaultsKeys = array_keys($defaults); + + if (!empty($params)) { + if (array_diff($paramsKeys, $routeParams) != array()) { + return false; + } + $required = array_values(array_diff($routeParams, $urlKeys)); + $reqCount = count($required); + + for ($i = 0; $i < $reqCount; $i++) { + if (array_key_exists($required[$i], $defaults) && $defaults[$required[$i]] === null) { + unset($required[$i]); + } + } + } + $isFilled = true; + + if (!empty($routeParams)) { + $filled = array_intersect_key($url, array_combine($routeParams, array_keys($routeParams))); + $isFilled = (array_diff($routeParams, array_keys($filled)) === array()); + if (!$isFilled && empty($params)) { + return false; + } + } + + if (empty($params)) { + return Router::__mapRoute($route, array_merge($url, compact('pass', 'named', 'prefix'))); + } elseif (!empty($routeParams) && !empty($route[3])) { + + if (!empty($required)) { + return false; + } + foreach ($params as $key => $val) { + if ((!isset($url[$key]) || $url[$key] != $val) || (!isset($defaults[$key]) || $defaults[$key] != $val) && !in_array($key, $routeParams)) { + if (!isset($defaults[$key])) { + continue; + } + return false; + } + } + } else { + if (empty($required) && $defaults['plugin'] === $url['plugin'] && $defaults['controller'] === $url['controller'] && $defaults['action'] === $url['action']) { + return Router::__mapRoute($route, array_merge($url, compact('pass', 'named', 'prefix'))); + } + return false; + } + + if (!empty($route[4])) { + foreach ($route[4] as $key => $reg) { + if (array_key_exists($key, $url) && !preg_match('#' . $reg . '#', $url[$key])) { + return false; + } + } + } + return Router::__mapRoute($route, array_merge($filled, compact('pass', 'named', 'prefix'))); + } +/** + * Merges URL parameters into a route string + * + * @param array $route Route + * @param array $params Parameters + * @return string Merged URL with parameters + * @access private + */ + function __mapRoute($route, $params = array()) { + if (isset($params['plugin']) && isset($params['controller']) && $params['plugin'] === $params['controller']) { + unset($params['controller']); + } + + if (isset($params['prefix']) && isset($params['action'])) { + $params['action'] = str_replace($params['prefix'] . '_', '', $params['action']); + unset($params['prefix']); + } + + if (isset($params['pass']) && is_array($params['pass'])) { + $params['pass'] = implode('/', Set::filter($params['pass'], true)); + } elseif (!isset($params['pass'])) { + $params['pass'] = ''; + } + + if (isset($params['named'])) { + if (is_array($params['named'])) { + $count = count($params['named']); + $keys = array_keys($params['named']); + $named = array(); + + for ($i = 0; $i < $count; $i++) { + $named[] = $keys[$i] . $this->named['separator'] . $params['named'][$keys[$i]]; + } + $params['named'] = join('/', $named); + } + $params['pass'] = str_replace('//', '/', $params['pass'] . '/' . $params['named']); + } + $out = $route[0]; + + foreach ($route[2] as $key) { + $string = null; + if (isset($params[$key])) { + $string = $params[$key]; + unset($params[$key]); + } elseif (strpos($out, $key) != strlen($out) - strlen($key)) { + $key = $key . '/'; + } + $out = str_replace(':' . $key, $string, $out); + } + + if (strpos($route[0], '*')) { + $out = str_replace('*', $params['pass'], $out); + } + + return $out; + } +/** + * Takes an array of URL parameters and separates the ones that can be used as named arguments + * + * @param array $params Associative array of URL parameters. + * @param string $controller Name of controller being routed. Used in scoping. + * @param string $action Name of action being routed. Used in scoping. + * @return array + * @access public + * @static + */ + function getNamedElements($params, $controller = null, $action = null) { + $_this =& Router::getInstance(); + $named = array(); + + foreach ($params as $param => $val) { + if (isset($_this->named['rules'][$param])) { + $rule = $_this->named['rules'][$param]; + if (Router::matchNamed($param, $val, $rule, compact('controller', 'action'))) { + $named[$param] = $val; + unset($params[$param]); + } + } + } + return array($named, $params); + } +/** + * Return true if a given named $param's $val matches a given $rule depending on $context. Currently implemented + * rule types are controller, action and match that can be combined with each other. + * + * @param string $param The name of the named parameter + * @param string $val The value of the named parameter + * @param array $rule The rule(s) to apply, can also be a match string + * @param string $context An array with additional context information (controller / action) + * @return boolean + * @access public + */ + function matchNamed($param, $val, $rule, $context = array()) { + if ($rule === true || $rule === false) { + return $rule; + } + if (is_string($rule)) { + $rule = array('match' => $rule); + } + if (!is_array($rule)) { + return false; + } + + $controllerMatches = !isset($rule['controller'], $context['controller']) || in_array($context['controller'], (array)$rule['controller']); + if (!$controllerMatches) { + return false; + } + $actionMatches = !isset($rule['action'], $context['action']) || in_array($context['action'], (array)$rule['action']); + if (!$actionMatches) { + return false; + } + $valueMatches = !isset($rule['match']) || preg_match(sprintf('/%s/', $rule['match']), $val); + return $valueMatches; + } +/** + * Generates a well-formed querystring from $q + * + * @param mixed $q Query string + * @param array $extra Extra querystring parameters. + * @param bool $escape Whether or not to use escaped & + * @return array + * @access public + * @static + */ + function queryString($q, $extra = array(), $escape = false) { + if (empty($q) && empty($extra)) { + return null; + } + $join = '&'; + if ($escape === true) { + $join = '&amp;'; + } + $out = ''; + + if (is_array($q)) { + $q = array_merge($extra, $q); + } else { + $out = $q; + $q = $extra; + } + $out .= http_build_query($q, null, $join); + if (isset($out[0]) && $out[0] != '?') { + $out = '?' . $out; + } + return $out; + } +/** + * Normalizes a URL for purposes of comparison + * + * @param mixed $url URL to normalize + * @return string Normalized URL + * @access public + */ + function normalize($url = '/') { + if (is_array($url)) { + $url = Router::url($url); + } elseif (preg_match('/^[a-z\-]+:\/\//', $url)) { + return $url; + } + $paths = Router::getPaths(); + + if (!empty($paths['base']) && stristr($url, $paths['base'])) { + $url = preg_replace('/^' . preg_quote($paths['base'], '/') . '/', '', $url, 1); + } + $url = '/' . $url; + + while (strpos($url, '//') !== false) { + $url = str_replace('//', '/', $url); + } + $url = preg_replace('/(?:(\/$))/', '', $url); + + if (empty($url)) { + return '/'; + } + return $url; + } +/** + * Returns the route matching the current request URL. + * + * @return array Matching route + * @access public + * @static + */ + function requestRoute() { + $_this =& Router::getInstance(); + return $_this->__currentRoute[0]; + } +/** + * Returns the route matching the current request (useful for requestAction traces) + * + * @return array Matching route + * @access public + * @static + */ + function currentRoute() { + $_this =& Router::getInstance(); + return $_this->__currentRoute[count($_this->__currentRoute) - 1]; + } +/** + * Removes the plugin name from the base URL. + * + * @param string $base Base URL + * @param string $plugin Plugin name + * @return base url with plugin name removed if present + * @access public + * @static + */ + function stripPlugin($base, $plugin = null) { + if ($plugin != null) { + $base = preg_replace('/(?:' . $plugin . ')/', '', $base); + $base = str_replace('//', '', $base); + $pos1 = strrpos($base, '/'); + $char = strlen($base) - 1; + + if ($pos1 === $char) { + $base = substr($base, 0, $char); + } + } + return $base; + } +/** + * Strip escape characters from parameter values. + * + * @param mixed $param Either an array, or a string + * @return mixed Array or string escaped + * @access public + * @static + */ + function stripEscape($param) { + $_this =& Router::getInstance(); + if (!is_array($param) || empty($param)) { + if (is_bool($param)) { + return $param; + } + + return preg_replace('/^(?:[\\t ]*(?:-!)+)/', '', $param); + } + + foreach ($param as $key => $value) { + if (is_string($value)) { + $return[$key] = preg_replace('/^(?:[\\t ]*(?:-!)+)/', '', $value); + } else { + foreach ($value as $array => $string) { + $return[$key][$array] = $_this->stripEscape($string); + } + } + } + return $return; + } +/** + * Instructs the router to parse out file extensions from the URL. For example, + * http://example.com/posts.rss would yield an file extension of "rss". + * The file extension itself is made available in the controller as + * $this->params['url']['ext'], and is used by the RequestHandler component to + * automatically switch to alternate layouts and templates, and load helpers + * corresponding to the given content, i.e. RssHelper. + * + * A list of valid extension can be passed to this method, i.e. Router::parseExtensions('rss', 'xml'); + * If no parameters are given, anything after the first . (dot) after the last / in the URL will be + * parsed, excluding querystring parameters (i.e. ?q=...). + * + * @access public + * @return void + * @static + */ + function parseExtensions() { + $_this =& Router::getInstance(); + $_this->__parseExtensions = true; + if (func_num_args() > 0) { + $_this->__validExtensions = func_get_args(); + } + } +/** + * Takes an passed params and converts it to args + * + * @param array $params + * @return array Array containing passed and named parameters + * @access public + * @static + */ + function getArgs($args, $options = array()) { + $_this =& Router::getInstance(); + $pass = $named = array(); + $args = explode('/', $args); + + $greedy = $_this->named['greedy']; + if (isset($options['greedy'])) { + $greedy = $options['greedy']; + } + $context = array(); + if (isset($options['context'])) { + $context = $options['context']; + } + $rules = $_this->named['rules']; + if (isset($options['named'])) { + $greedy = isset($options['greedy']) && $options['greedy'] === true; + foreach ((array)$options['named'] as $key => $val) { + if (is_numeric($key)) { + $rules[$val] = true; + continue; + } + $rules[$key] = $val; + } + } + + foreach ($args as $param) { + if (empty($param) && $param !== '0' && $param !== 0) { + continue; + } + $param = $_this->stripEscape($param); + + $separatorIsPresent = strpos($param, $_this->named['separator']) !== false; + if ((!isset($options['named']) || !empty($options['named'])) && $separatorIsPresent) { + list($key, $val) = explode($_this->named['separator'], $param, 2); + $hasRule = isset($rules[$key]); + $passIt = (!$hasRule && !$greedy) || ($hasRule && !Router::matchNamed($key, $val, $rules[$key], $context)); + if ($passIt) { + $pass[] = $param; + } else { + $named[$key] = $val; + } + } else { + $pass[] = $param; + } + } + return compact('pass', 'named'); + } +} +?> \ No newline at end of file diff --git a/cake/libs/sanitize.php b/cake/libs/sanitize.php new file mode 100755 index 0000000..30d424f --- /dev/null +++ b/cake/libs/sanitize.php @@ -0,0 +1,308 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Washes strings from unwanted noise. + * + * Helpful methods to make unsafe strings usable. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * 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 + * @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. + * + * @param string $string String from where to strip tags + * @param boolean $remove If true, the string is stripped of all HTML tags + * @return string Sanitized string + * @access public + * @static + */ + function html($string, $remove = false) { + if ($remove) { + $string = strip_tags($string); + } else { + $patterns = array("/\&/", "/%/", "/</", "/>/", '/"/', "/'/", "/\(/", "/\)/", "/\+/", "/-/"); + $replacements = array("&amp;", "&#37;", "&lt;", "&gt;", "&quot;", "&#39;", "&#40;", "&#41;", "&#43;", "&#45;"); + $string = preg_replace($patterns, $replacements, $string); + } + return $string; + } +/** + * 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>|<!--.*?-->/i', '', $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. + * + * @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; $i < count($params); $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, encode, dollar, carriage, unicode, + * escape, backslash. + * + * @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, + '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); + } + 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; + } + */ + } + } + } + } + } +} +?> diff --git a/cake/libs/security.php b/cake/libs/security.php new file mode 100755 index 0000000..15ec7c1 --- /dev/null +++ b/cake/libs/security.php @@ -0,0 +1,196 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Short description for file. + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v .0.10.0.1233 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Short description for file. + * + * Long description for file + * + * @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() { + $_this =& Security::getInstance(); + 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 ''; + } + + $_this =& Security::getInstance(); + if (!defined('CIPHER_SEED')) { + //This is temporary will change later + define('CIPHER_SEED', '76859309657453542496749683645'); + } + srand(CIPHER_SEED); + $out = ''; + + for ($i = 0; $i < strlen($text); $i++) { + for ($j = 0; $j < ord(substr($key, $i % strlen($key), 1)); $j++) { + $toss = rand(0, 255); + } + $mask = rand(0, 255); + $out .= chr(ord(substr($text, $i, 1)) ^ $mask); + } + return $out; + } +} +?> \ No newline at end of file diff --git a/cake/libs/session.php b/cake/libs/session.php new file mode 100755 index 0000000..c90318c --- /dev/null +++ b/cake/libs/session.php @@ -0,0 +1,779 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * 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://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v .0.10.0.1222 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Database name for cake sessions. + * + */ +if (!class_exists('Set')) { + require LIBS . 'set.php'; +} +if (!class_exists('Security')) { + require LIBS . 'security.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; +/** + * 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; +/** + * 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) { + if (Configure::read('Session.save') === 'database' && !class_exists('ConnectionManager')) { + App::import('Core', 'ConnectionManager'); + } + + 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')); + } + } + $this->time = time(); + + 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 (!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. + * + * @param string $name Variable name to check for + * @return boolean True if variable is there + * @access public + */ + function start() { + if (function_exists('session_write_close')) { + session_write_close(); + } + $this->__initSession(); + return $this->__startSession(); + } +/** + * Determine if Session has been started. + * + * @access public + * @return boolean True if session has been started. + */ + function started() { + if (isset($_SESSION)) { + 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) { + $var = $this->__validateKeys($name); + if (empty($var)) { + return false; + } + $result = Set::extract($_SESSION, $var); + 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 (isset($_SESSION)) { + 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 del($name) { + if ($this->check($name)) { + if ($var = $this->__validateKeys($name)) { + if (in_array($var, $this->watchKeys)) { + trigger_error('Deleting session key {' . $var . '}', E_USER_NOTICE); + } + $this->__overwrite($_SESSION, Set::remove($_SESSION, $var)); + return ($this->check($var) == false); + } + } + $this->__setError(2, "$name doesn't exist"); + 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::extract($_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) { + $var = $this->__validateKeys($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) { + $var = $this->__validateKeys($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) { + $var = $this->__validateKeys($name); + + if (empty($var)) { + return false; + } + if (in_array($var, $this->watchKeys)) { + trigger_error('Writing session key {' . $var . '}: ' . Debugger::exportVar($value), E_USER_NOTICE); + } + $this->__overwrite($_SESSION, Set::insert($_SESSION, $var, $value)); + return (Set::extract($_SESSION, $var) === $value); + } +/** + * Helper method to destroy invalid sessions. + * + * @return void + * @access public + */ + function destroy() { + $_SESSION = array(); + $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); + } + + switch ($this->security) { + case 'high': + $this->cookieLifeTime = 0; + if ($iniSet) { + ini_set('session.referer_check', $this->host); + } + break; + case 'medium': + $this->cookieLifeTime = 7 * 86400; + if ($iniSet) { + ini_set('session.referer_check', $this->host); + } + break; + case 'low': + default: + $this->cookieLifeTime = 788940000; + break; + } + + 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.table') === null) { + trigger_error(__("You must set the all Configure::write('Session.*') in core.php to use database storage"), E_USER_WARNING); + exit(); + } elseif (Configure::read('Session.database') === null) { + Configure::write('Session.database', 'default'); + } + 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')) { + uses('Cache'); + } + 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: + if (empty($_SESSION)) { + $config = CONFIGS . Configure::read('Session.save') . '.php'; + + if (is_file($config)) { + require_once ($config); + } + } + break; + } + } +/** + * Helper method to start a session + * + * @access private + */ + function __startSession() { + if (headers_sent()) { + if (empty($_SESSION)) { + $_SESSION = array(); + } + return false; + } 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 = $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) { + $sessionpath = session_save_path(); + if (empty($sessionpath)) { + $sessionpath = "/tmp"; + } + 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) { + $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(); + } +/** + * Validate that the $name is in correct dot notation + * example: $name = 'ControllerName.key'; + * + * @param string $name Session key names as string. + * @return mixed false is $name is not correct format, or $name if it is correct + * @access private + */ + function __validateKeys($name) { + if (is_string($name) && preg_match("/^[ 0-9a-zA-Z._-]*$/", $name)) { + return $name; + } + $this->__setError(3, "$name is not a string"); + return false; + } +/** + * 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 $key 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($key) { + $db =& ConnectionManager::getDataSource(Configure::read('Session.database')); + $table = $db->fullTableName(Configure::read('Session.table'), false); + $row = $db->query("SELECT " . $db->name($table.'.data') . " FROM " . $db->name($table) . " WHERE " . $db->name($table.'.id') . " = " . $db->value($key), false); + + if ($row && !isset($row[0][$table]) && isset($row[0][0])) { + $table = 0; + } + + if ($row && $row[0][$table]['data']) { + return $row[0][$table]['data']; + } else { + return false; + } + } +/** + * Helper function called on write for database sessions. + * + * @param mixed $key The name of the var + * @param mixed $value The value of the var + * @return boolean Success + * @access private + */ + function __write($key, $value) { + $db =& ConnectionManager::getDataSource(Configure::read('Session.database')); + $table = $db->fullTableName(Configure::read('Session.table')); + + switch (Configure::read('Security.level')) { + case 'high': + $factor = 10; + break; + case 'medium': + $factor = 100; + break; + case 'low': + $factor = 300; + break; + default: + $factor = 10; + break; + } + $expires = time() + Configure::read('Session.timeout') * $factor; + $row = $db->query("SELECT COUNT(id) AS count FROM " . $db->name($table) . " WHERE " + . $db->name('id') . " = " + . $db->value($key), false); + + if ($row[0][0]['count'] > 0) { + $db->execute("UPDATE " . $db->name($table) . " SET " . $db->name('data') . " = " + . $db->value($value) . ", " . $db->name('expires') . " = " + . $db->value($expires) . " WHERE " . $db->name('id') . " = " + . $db->value($key)); + } else { + $db->execute("INSERT INTO " . $db->name($table) . " (" . $db->name('data') . "," + . $db->name('expires') . "," . $db->name('id') + . ") VALUES (" . $db->value($value) . ", " . $db->value($expires) . ", " + . $db->value($key) . ")"); + } + return true; + } +/** + * Method called on the destruction of a database session. + * + * @param integer $key Key that uniquely identifies session in database + * @return boolean Success + * @access private + */ + function __destroy($key) { + $db =& ConnectionManager::getDataSource(Configure::read('Session.database')); + $table = $db->fullTableName(Configure::read('Session.table')); + $db->execute("DELETE FROM " . $db->name($table) . " WHERE " . $db->name($table.'.id') . " = " . $db->value($key)); + return true; + } +/** + * 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) { + $db =& ConnectionManager::getDataSource(Configure::read('Session.database')); + $table = $db->fullTableName(Configure::read('Session.table')); + $db->execute("DELETE FROM " . $db->name($table) . " WHERE " . $db->name($table.'.expires') . " < ". $db->value(time())); + return true; + } +} +?> \ No newline at end of file diff --git a/cake/libs/set.php b/cake/libs/set.php new file mode 100755 index 0000000..2afb715 --- /dev/null +++ b/cake/libs/set.php @@ -0,0 +1,1121 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Library of array functions for Cake. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v 1.2.0 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Class used for manipulation of arrays. + * + * Long description for class + * + * @package cake + * @subpackage cake.cake.libs + */ +class Set extends Object { +/** + * Deprecated + * + */ + var $value = array(); +/** + * This function can be thought of as a hybrid between PHP's array_merge and array_merge_recursive. The difference + * to the two is that if an array key contains another array then the function behaves recursive (unlike array_merge) + * but does not do if for keys containing strings (unlike array_merge_recursive). See the unit test for more information. + * + * Note: This function will work with an unlimited amount of arguments and typecasts non-array parameters into arrays. + * + * @param array $arr1 Array to be merged + * @param array $arr2 Array to merge with + * @return array Merged array + * @access public + * @static + */ + function merge($arr1, $arr2 = null) { + $args = func_get_args(); + + $r = (array)current($args); + while (($arg = next($args)) !== false) { + foreach ((array)$arg as $key => $val) { + if (is_array($val) && isset($r[$key]) && is_array($r[$key])) { + $r[$key] = Set::merge($r[$key], $val); + } elseif (is_int($key)) { + $r[] = $val; + } else { + $r[$key] = $val; + } + } + } + return $r; + } +/** + * Filters empty elements out of a route array, excluding '0'. + * + * @param mixed $var Either an array to filter, or value when in callback + * @param boolean $isArray Force to tell $var is an array when $var is empty + * @return mixed Either filtered array, or true/false when in callback + * @access public + * @static + */ + function filter($var, $isArray = false) { + if (is_array($var) && (!empty($var) || $isArray)) { + return array_filter($var, array('Set', 'filter')); + } + + if ($var === 0 || $var === '0' || !empty($var)) { + return true; + } + return false; + } +/** + * Pushes the differences in $array2 onto the end of $array + * + * @param mixed $array Original array + * @param mixed $array2 Differences to push + * @return array Combined array + * @access public + * @static + */ + function pushDiff($array, $array2) { + if (empty($array) && !empty($array2)) { + return $array2; + } + if (!empty($array) && !empty($array2)) { + foreach ($array2 as $key => $value) { + if (!array_key_exists($key, $array)) { + $array[$key] = $value; + } else { + if (is_array($value)) { + $array[$key] = Set::pushDiff($array[$key], $array2[$key]); + } + } + } + } + return $array; + } +/** + * Maps the contents of the Set object to an object hierarchy. + * Maintains numeric keys as arrays of objects + * + * @param string $class A class name of the type of object to map to + * @param string $tmp A temporary class name used as $class if $class is an array + * @return object Hierarchical object + * @access public + * @static + */ + function map($class = 'stdClass', $tmp = 'stdClass') { + if (is_array($class)) { + $val = $class; + $class = $tmp; + } + + if (empty($val)) { + return null; + } + return Set::__map($val, $class); + } + +/** + * Get the array value of $array. If $array is null, it will return + * the current array Set holds. If it is an object of type Set, it + * will return its value. If it is another object, its object variables. + * If it is anything else but an array, it will return an array whose first + * element is $array. + * + * @param mixed $array Data from where to get the array. + * @return array Array from $array. + * @access private + */ + function __array($array) { + if (empty($array)) { + $array = array(); + } elseif (is_object($array)) { + $array = get_object_vars($array); + } elseif (!is_array($array)) { + $array = array($array); + } + return $array; + } + +/** + * Maps the given value as an object. If $value is an object, + * it returns $value. Otherwise it maps $value as an object of + * type $class, and if primary assign _name_ $key on first array. + * If $value is not empty, it will be used to set properties of + * returned object (recursively). If $key is numeric will maintain array + * structure + * + * @param mixed $value Value to map + * @param string $class Class name + * @param boolean $primary whether to assign first array key as the _name_ + * @return mixed Mapped object + * @access private + * @static + */ + function __map(&$array, $class, $primary = false) { + if ($class === true) { + $out = new stdClass; + } else { + $out = new $class; + } + if (is_array($array)) { + $keys = array_keys($array); + foreach ($array as $key => $value) { + if ($keys[0] === $key && $class !== true) { + $primary = true; + } + if (is_numeric($key)) { + if (is_object($out)) { + $out = get_object_vars($out); + } + $out[$key] = Set::__map($value, $class); + if (is_object($out[$key])) { + if ($primary !== true && is_array($value) && Set::countDim($value, true) === 2) { + if (!isset($out[$key]->_name_)) { + $out[$key]->_name_ = $primary; + } + } + } + } elseif (is_array($value)) { + if ($primary === true) { + if (!isset($out->_name_)) { + $out->_name_ = $key; + } + $primary = false; + foreach ($value as $key2 => $value2) { + $out->{$key2} = Set::__map($value2, true); + } + } else { + if (!is_numeric($key)) { + $out->{$key} = Set::__map($value, true, $key); + if (is_object($out->{$key}) && !is_numeric($key)) { + if (!isset($out->{$key}->_name_)) { + $out->{$key}->_name_ = $key; + } + } + } else { + $out->{$key} = Set::__map($value, true); + } + } + } else { + $out->{$key} = $value; + } + } + } else { + $out = $array; + } + return $out; + } +/** + * Checks to see if all the values in the array are numeric + * + * @param array $array The array to check. If null, the value of the current Set object + * @return boolean true if values are numeric, false otherwise + * @access public + * @static + */ + function numeric($array = null) { + if (empty($array)) { + return null; + } + + if ($array === range(0, count($array) - 1)) { + return true; + } + + $numeric = true; + $keys = array_keys($array); + $count = count($keys); + + for ($i = 0; $i < $count; $i++) { + if (!is_numeric($array[$keys[$i]])) { + $numeric = false; + break; + } + } + return $numeric; + } +/** + * Return a value from an array list if the key exists. + * + * If a comma separated $list is passed arrays are numeric with the key of the first being 0 + * $list = 'no, yes' would translate to $list = array(0 => 'no', 1 => 'yes'); + * + * If an array is used, keys can be strings example: array('no' => 0, 'yes' => 1); + * + * $list defaults to 0 = no 1 = yes if param is not passed + * + * @param mixed $select Key in $list to return + * @param mixed $list can be an array or a comma-separated list. + * @return string the value of the array key or null if no match + * @access public + * @static + */ + function enum($select, $list = null) { + if (empty($list)) { + $list = array('no', 'yes'); + } + + $return = null; + $list = Set::normalize($list, false); + + if (array_key_exists($select, $list)) { + $return = $list[$select]; + } + return $return; + } +/** + * Returns a series of values extracted from an array, formatted in a format string. + * + * @param array $data Source array from which to extract the data + * @param string $format Format string into which values will be inserted, see sprintf() + * @param array $keys An array containing one or more Set::extract()-style key paths + * @return array An array of strings extracted from $keys and formatted with $format + * @access public + * @static + */ + function format($data, $format, $keys) { + + $extracted = array(); + $count = count($keys); + + if (!$count) { + return; + } + + for ($i = 0; $i < $count; $i++) { + $extracted[] = Set::extract($data, $keys[$i]); + } + $out = array(); + $data = $extracted; + $count = count($data[0]); + + if (preg_match_all('/\{([0-9]+)\}/msi', $format, $keys2) && isset($keys2[1])) { + $keys = $keys2[1]; + $format = preg_split('/\{([0-9]+)\}/msi', $format); + $count2 = count($format); + + for ($j = 0; $j < $count; $j++) { + $formatted = ''; + for ($i = 0; $i <= $count2; $i++) { + if (isset($format[$i])) { + $formatted .= $format[$i]; + } + if (isset($keys[$i]) && isset($data[$keys[$i]][$j])) { + $formatted .= $data[$keys[$i]][$j]; + } + } + $out[] = $formatted; + } + } else { + $count2 = count($data); + for ($j = 0; $j < $count; $j++) { + $args = array(); + for ($i = 0; $i < $count2; $i++) { + if (isset($data[$i][$j])) { + $args[] = $data[$i][$j]; + } + } + $out[] = vsprintf($format, $args); + } + } + return $out; + } +/** + * Implements partial support for XPath 2.0. If $path is an array or $data is empty it the call is delegated to Set::classicExtract. + * + * Currently implemented selectors: + * - /User/id (similar to the classic {n}.User.id) + * - /User[2]/name (selects the name of the second User) + * - /User[id>2] (selects all Users with an id > 2) + * - /User[id>2][<5] (selects all Users with an id > 2 but < 5) + * - /Post/Comment[author_name=john]/../name (Selects the name of all Posts that have at least one Comment written by john) + * - /Posts[name] (Selects all Posts that have a 'name' key) + * - /Comment/.[1] (Selects the contents of the first comment) + * - /Comment/.[:last] (Selects the last comment) + * - /Comment/.[:first] (Selects the first comment) + * - /Comment[text=/cakephp/i] (Selects the all comments that have a text matching the regex /cakephp/i) + * - /Comment/@* (Selects the all key names of all comments) + * + * Other limitations: + * - Only absolute paths starting with a single '/' are supported right now + * + * Warning: Even so it has plenty of unit tests the XPath support has not gone through a lot of real-world testing. Please report + * Bugs as you find them. Suggestions for additional features to imlement are also very welcome! + * + * @param string $path An absolute XPath 2.0 path + * @param string $data An array of data to extract from + * @param string $options Currently only supports 'flatten' which can be disabled for higher XPath-ness + * @return array An array of matched items + * @access public + * @static + */ + function extract($path, $data = null, $options = array()) { + if (is_string($data)) { + $tmp = $data; + $data = $path; + $path = $tmp; + } + if (strpos($path, '/') === false) { + return Set::classicExtract($data, $path); + } + if (empty($data)) { + return array(); + } + if ($path === '/') { + return $data; + } + $contexts = $data; + $options = array_merge(array('flatten' => true), $options); + if (!isset($contexts[0])) { + $current = current($data); + if ((is_array($current) && count($data) <= 1) || !is_array($current) || !Set::numeric(array_keys($data))) { + $contexts = array($data); + } + } + $tokens = array_slice(preg_split('/(?<!=)\/(?![a-z-]*\])/', $path), 1); + + do { + $token = array_shift($tokens); + $conditions = false; + if (preg_match_all('/\[([^=]+=\/[^\/]+\/|[^\]]+)\]/', $token, $m)) { + $conditions = $m[1]; + $token = substr($token, 0, strpos($token, '[')); + } + $matches = array(); + foreach ($contexts as $key => $context) { + if (!isset($context['trace'])) { + $context = array('trace' => array(null), 'item' => $context, 'key' => $key); + } + if ($token === '..') { + if (count($context['trace']) == 1) { + $context['trace'][] = $context['key']; + } + $parent = join('/', $context['trace']) . '/.'; + $context['item'] = Set::extract($parent, $data); + $context['key'] = array_pop($context['trace']); + if (isset($context['trace'][1]) && $context['trace'][1] > 0) { + $context['item'] = $context['item'][0]; + } else if(!empty($context['item'][$key])){ + $context['item'] = $context['item'][$key]; + } else { + $context['item'] = array_shift($context['item']); + } + $matches[] = $context; + continue; + } + $match = false; + if ($token === '@*' && is_array($context['item'])) { + $matches[] = array( + 'trace' => array_merge($context['trace'], (array)$key), + 'key' => $key, + 'item' => array_keys($context['item']), + ); + } elseif (is_array($context['item']) && array_key_exists($token, $context['item'])) { + $items = $context['item'][$token]; + if (!is_array($items)) { + $items = array($items); + } elseif (!isset($items[0])) { + $current = current($items); + if ((is_array($current) && count($items) <= 1) || !is_array($current)) { + $items = array($items); + } + } + + foreach ($items as $key => $item) { + $ctext = array($context['key']); + if (!is_numeric($key)) { + $ctext[] = $token; + $token = array_shift($tokens); + if (isset($items[$token])) { + $ctext[] = $token; + $item = $items[$token]; + $matches[] = array( + 'trace' => array_merge($context['trace'], $ctext), + 'key' => $key, + 'item' => $item, + ); + break; + } else { + array_unshift($tokens, $token); + } + } else { + $key = $token; + } + + $matches[] = array( + 'trace' => array_merge($context['trace'], $ctext), + 'key' => $key, + 'item' => $item, + ); + } + } elseif (($key === $token || (ctype_digit($token) && $key == $token) || $token === '.')) { + $context['trace'][] = $key; + $matches[] = array( + 'trace' => $context['trace'], + 'key' => $key, + 'item' => $context['item'], + ); + } + } + if ($conditions) { + foreach ($conditions as $condition) { + $filtered = array(); + $length = count($matches); + foreach ($matches as $i => $match) { + if (Set::matches(array($condition), $match['item'], $i + 1, $length)) { + $filtered[] = $match; + } + } + $matches = $filtered; + } + } + $contexts = $matches; + + if (empty($tokens)) { + break; + } + } while(1); + + $r = array(); + + foreach ($matches as $match) { + if ((!$options['flatten'] || is_array($match['item'])) && !is_int($match['key'])) { + $r[] = array($match['key'] => $match['item']); + } else { + $r[] = $match['item']; + } + } + return $r; + } +/** + * This function can be used to see if a single item or a given xpath match certain conditions. + * + * @param mixed $conditions An array of condition strings or an XPath expression + * @param array $data An array of data to execute the match on + * @param integer $i Optional: The 'nth'-number of the item being matched. + * @return boolean + * @access public + * @static + */ + function matches($conditions, $data = array(), $i = null, $length = null) { + if (empty($conditions)) { + return true; + } + if (is_string($conditions)) { + return !!Set::extract($conditions, $data); + } + foreach ($conditions as $condition) { + if ($condition === ':last') { + if ($i != $length) { + return false; + } + continue; + } elseif ($condition === ':first') { + if ($i != 1) { + return false; + } + continue; + } + if (!preg_match('/(.+?)([><!]?[=]|[><])(.*)/', $condition, $match)) { + if (ctype_digit($condition)) { + if ($i != $condition) { + return false; + } + } elseif (preg_match_all('/(?:^[0-9]+|(?<=,)[0-9]+)/', $condition, $matches)) { + return in_array($i, $matches[0]); + } elseif (!array_key_exists($condition, $data)) { + return false; + } + continue; + } + list(,$key,$op,$expected) = $match; + if (!isset($data[$key])) { + return false; + } + + $val = $data[$key]; + + if ($op === '=' && $expected && $expected{0} === '/') { + return preg_match($expected, $val); + } + if ($op === '=' && $val != $expected) { + return false; + } + if ($op === '!=' && $val == $expected) { + return false; + } + if ($op === '>' && $val <= $expected) { + return false; + } + if ($op === '<' && $val >= $expected) { + return false; + } + if ($op === '<=' && $val > $expected) { + return false; + } + if ($op === '>=' && $val < $expected) { + return false; + } + } + return true; + } +/** + * Gets a value from an array or object that is contained in a given path using an array path syntax, i.e.: + * "{n}.Person.{[a-z]+}" - Where "{n}" represents a numeric key, "Person" represents a string literal, + * and "{[a-z]+}" (i.e. any string literal enclosed in brackets besides {n} and {s}) is interpreted as + * a regular expression. + * + * @param array $data Array from where to extract + * @param mixed $path As an array, or as a dot-separated string. + * @return array Extracted data + * @access public + * @static + */ + function classicExtract($data, $path = null) { + if (empty($path)) { + return $data; + } + if (is_object($data)) { + $data = get_object_vars($data); + } + if (!is_array($data)) { + return $data; + } + + if (!is_array($path)) { + if (!class_exists('String')) { + App::import('Core', 'String'); + } + $path = String::tokenize($path, '.', '{', '}'); + } + $tmp = array(); + + if (!is_array($path) || empty($path)) { + return null; + } + + foreach ($path as $i => $key) { + if (is_numeric($key) && intval($key) > 0 || $key === '0') { + if (isset($data[intval($key)])) { + $data = $data[intval($key)]; + } else { + return null; + } + } elseif ($key === '{n}') { + foreach ($data as $j => $val) { + if (is_int($j)) { + $tmpPath = array_slice($path, $i + 1); + if (empty($tmpPath)) { + $tmp[] = $val; + } else { + $tmp[] = Set::classicExtract($val, $tmpPath); + } + } + } + return $tmp; + } elseif ($key === '{s}') { + foreach ($data as $j => $val) { + if (is_string($j)) { + $tmpPath = array_slice($path, $i + 1); + if (empty($tmpPath)) { + $tmp[] = $val; + } else { + $tmp[] = Set::classicExtract($val, $tmpPath); + } + } + } + return $tmp; + } elseif (false !== strpos($key,'{') && false !== strpos($key,'}')) { + $pattern = substr($key, 1, -1); + + foreach ($data as $j => $val) { + if (preg_match('/^'.$pattern.'/s', $j) !== 0) { + $tmpPath = array_slice($path, $i + 1); + if (empty($tmpPath)) { + $tmp[$j] = $val; + } else { + $tmp[$j] = Set::classicExtract($val, $tmpPath); + } + } + } + return $tmp; + } else { + if (isset($data[$key])) { + $data = $data[$key]; + } else { + return null; + } + } + } + return $data; + } +/** + * Inserts $data into an array as defined by $path. + * + * @param mixed $list Where to insert into + * @param mixed $path A dot-separated string. + * @param array $data Data to insert + * @return array + * @access public + * @static + */ + function insert($list, $path, $data = null) { + if (!is_array($path)) { + $path = explode('.', $path); + } + $_list =& $list; + + foreach ($path as $i => $key) { + if (is_numeric($key) && intval($key) > 0 || $key === '0') { + $key = intval($key); + } + if ($i === count($path) - 1) { + $_list[$key] = $data; + } else { + if (!isset($_list[$key])) { + $_list[$key] = array(); + } + $_list =& $_list[$key]; + } + } + return $list; + } +/** + * Removes an element from a Set or array as defined by $path. + * + * @param mixed $list From where to remove + * @param mixed $path A dot-separated string. + * @return array Array with $path removed from its value + * @access public + * @static + */ + function remove($list, $path = null) { + if (empty($path)) { + return $list; + } + if (!is_array($path)) { + $path = explode('.', $path); + } + $_list =& $list; + + foreach ($path as $i => $key) { + if (is_numeric($key) && intval($key) > 0 || $key === '0') { + $key = intval($key); + } + if ($i === count($path) - 1) { + unset($_list[$key]); + } else { + if (!isset($_list[$key])) { + return $list; + } + $_list =& $_list[$key]; + } + } + return $list; + } +/** + * Checks if a particular path is set in an array + * + * @param mixed $data Data to check on + * @param mixed $path A dot-separated string. + * @return boolean true if path is found, false otherwise + * @access public + * @static + */ + function check($data, $path = null) { + if (empty($path)) { + return $data; + } + if (!is_array($path)) { + $path = explode('.', $path); + } + + foreach ($path as $i => $key) { + if (is_numeric($key) && intval($key) > 0 || $key === '0') { + $key = intval($key); + } + if ($i === count($path) - 1) { + return (is_array($data) && array_key_exists($key, $data)); + } + + if (!is_array($data) || !array_key_exists($key, $data)) { + return false; + } + $data =& $data[$key]; + } + return true; + } +/** + * Computes the difference between a Set and an array, two Sets, or two arrays + * + * @param mixed $val1 First value + * @param mixed $val2 Second value + * @return array Computed difference + * @access public + * @static + */ + function diff($val1, $val2 = null) { + if (empty($val1)) { + return (array)$val2; + } + if (empty($val2)) { + return (array)$val1; + } + $out = array(); + + foreach ($val1 as $key => $val) { + $exists = array_key_exists($key, $val2); + + if ($exists && $val2[$key] != $val) { + $out[$key] = $val; + } elseif (!$exists) { + $out[$key] = $val; + } + unset($val2[$key]); + } + + foreach ($val2 as $key => $val) { + if (!array_key_exists($key, $out)) { + $out[$key] = $val; + } + } + return $out; + } +/** + * Determines if two Sets or arrays are equal + * + * @param array $val1 First value + * @param array $val2 Second value + * @return boolean true if they are equal, false otherwise + * @access public + * @static + */ + function isEqual($val1, $val2 = null) { + return ($val1 == $val2); + } +/** + * Determines if one Set or array contains the exact keys and values of another. + * + * @param array $val1 First value + * @param array $val2 Second value + * @return boolean true if $val1 contains $val2, false otherwise + * @access public + * @static + */ + function contains($val1, $val2 = null) { + if (empty($val1) || empty($val2)) { + return false; + } + + foreach ($val2 as $key => $val) { + if (is_numeric($key)) { + Set::contains($val, $val1); + } else { + if (!isset($val1[$key]) || $val1[$key] != $val) { + return false; + } + } + } + return true; + } +/** + * Counts the dimensions of an array. If $all is set to false (which is the default) it will + * only consider the dimension of the first element in the array. + * + * @param array $array Array to count dimensions on + * @param boolean $all Set to true to count the dimension considering all elements in array + * @param integer $count Start the dimension count at this number + * @return integer The number of dimensions in $array + * @access public + * @static + */ + function countDim($array = null, $all = false, $count = 0) { + if ($all) { + $depth = array($count); + if (is_array($array) && reset($array) !== false) { + foreach ($array as $value) { + $depth[] = Set::countDim($value, true, $count + 1); + } + } + $return = max($depth); + } else { + if (is_array(reset($array))) { + $return = Set::countDim(reset($array)) + 1; + } else { + $return = 1; + } + } + return $return; + } +/** + * Normalizes a string or array list. + * + * @param mixed $list List to normalize + * @param boolean $assoc If true, $list will be converted to an associative array + * @param string $sep If $list is a string, it will be split into an array with $sep + * @param boolean $trim If true, separated strings will be trimmed + * @return array + * @access public + * @static + */ + function normalize($list, $assoc = true, $sep = ',', $trim = true) { + if (is_string($list)) { + $list = explode($sep, $list); + if ($trim) { + foreach ($list as $key => $value) { + $list[$key] = trim($value); + } + } + if ($assoc) { + return Set::normalize($list); + } + } elseif (is_array($list)) { + $keys = array_keys($list); + $count = count($keys); + $numeric = true; + + if (!$assoc) { + for ($i = 0; $i < $count; $i++) { + if (!is_int($keys[$i])) { + $numeric = false; + break; + } + } + } + if (!$numeric || $assoc) { + $newList = array(); + for ($i = 0; $i < $count; $i++) { + if (is_int($keys[$i])) { + $newList[$list[$keys[$i]]] = null; + } else { + $newList[$keys[$i]] = $list[$keys[$i]]; + } + } + $list = $newList; + } + } + return $list; + } +/** + * Creates an associative array using a $path1 as the path to build its keys, and optionally + * $path2 as path to get the values. If $path2 is not specified, all values will be initialized + * to null (useful for Set::merge). You can optionally group the values by what is obtained when + * following the path specified in $groupPath. + * + * @param mixed $data Array or object from where to extract keys and values + * @param mixed $path1 As an array, or as a dot-separated string. + * @param mixed $path2 As an array, or as a dot-separated string. + * @param string $groupPath As an array, or as a dot-separated string. + * @return array Combined array + * @access public + * @static + */ + function combine($data, $path1 = null, $path2 = null, $groupPath = null) { + if (empty($data)) { + return array(); + } + + if (is_object($data)) { + $data = get_object_vars($data); + } + + if (is_array($path1)) { + $format = array_shift($path1); + $keys = Set::format($data, $format, $path1); + } else { + $keys = Set::extract($data, $path1); + } + + if (!empty($path2) && is_array($path2)) { + $format = array_shift($path2); + $vals = Set::format($data, $format, $path2); + + } elseif (!empty($path2)) { + $vals = Set::extract($data, $path2); + + } else { + $count = count($keys); + for ($i = 0; $i < $count; $i++) { + $vals[$i] = null; + } + } + + if ($groupPath != null) { + $group = Set::extract($data, $groupPath); + if (!empty($group)) { + $c = count($keys); + for ($i = 0; $i < $c; $i++) { + if (!isset($group[$i])) { + $group[$i] = 0; + } + if (!isset($out[$group[$i]])) { + $out[$group[$i]] = array(); + } + $out[$group[$i]][$keys[$i]] = $vals[$i]; + } + return $out; + } + } + + return array_combine($keys, $vals); + } +/** + * Converts an object into an array. If $object is no object, reverse + * will return the same value. + * + * @param object $object Object to reverse + * @return array + * @static + */ + function reverse($object) { + $out = array(); + if (is_a($object, 'XmlNode')) { + $out = $object->toArray(); + return $out; + } else if (is_object($object)) { + $keys = get_object_vars($object); + if (isset($keys['_name_'])) { + $identity = $keys['_name_']; + unset($keys['_name_']); + } + $new = array(); + foreach ($keys as $key => $value) { + if (is_array($value)) { + $new[$key] = (array)Set::reverse($value); + } else { + if (isset($value->_name_)) { + $new = array_merge($new, Set::reverse($value)); + } else { + $new[$key] = Set::reverse($value); + } + } + } + if (isset($identity)) { + $out[$identity] = $new; + } else { + $out = $new; + } + } elseif (is_array($object)) { + foreach ($object as $key => $value) { + $out[$key] = Set::reverse($value); + } + } else { + $out = $object; + } + return $out; + } +/** + * Collapses a multi-dimensional array into a single dimension, using a delimited array path for + * each array element's key, i.e. array(array('Foo' => array('Bar' => 'Far'))) becomes + * array('0.Foo.Bar' => 'Far'). + * + * @param array $data Array to flatten + * @param string $separator String used to separate array key elements in a path, defaults to '.' + * @return array + * @access public + * @static + */ + function flatten($data, $separator = '.') { + $result = array(); + $path = null; + + if (is_array($separator)) { + extract($separator, EXTR_OVERWRITE); + } + + if (!is_null($path)) { + $path .= $separator; + } + + foreach ($data as $key => $val) { + if (is_array($val)) { + $result += (array)Set::flatten($val, array( + 'separator' => $separator, + 'path' => $path . $key + )); + } else { + $result[$path . $key] = $val; + } + } + return $result; + } +/** + * Flattens an array for sorting + * + * @param array $results + * @param string $key + * @return array + * @access private + */ + function __flatten($results, $key = null) { + $stack = array(); + foreach ($results as $k => $r) { + $id = $k; + if (!is_null($key)) { + $id = $key; + } + if (is_array($r) && count($r)) { + $stack = array_merge($stack, Set::__flatten($r, $id)); + } else { + $stack[] = array('id' => $id, 'value' => $r); + } + } + return $stack; + } +/** + * Sorts an array by any value, determined by a Set-compatible path + * + * @param array $data + * @param string $path A Set-compatible path to the array value + * @param string $dir asc/desc + * @return array + * @static + */ + function sort($data, $path, $dir) { + $result = Set::__flatten(Set::extract($data, $path)); + list($keys, $values) = array(Set::extract($result, '{n}.id'), Set::extract($result, '{n}.value')); + + $dir = strtolower($dir); + if ($dir === 'asc') { + $dir = SORT_ASC; + } elseif ($dir === 'desc') { + $dir = SORT_DESC; + } + array_multisort($values, $dir, $keys, $dir); + $sorted = array(); + + $keys = array_unique($keys); + + foreach ($keys as $k) { + $sorted[] = $data[$k]; + } + return $sorted; + } +/** + * Deprecated, Set class should be called statically + * + */ + function &get() { + trigger_error('get() is deprecated. Set class should be called statically', E_USER_WARNING); + } +} +?> \ No newline at end of file diff --git a/cake/libs/socket.php b/cake/libs/socket.php new file mode 100755 index 0000000..c5c945a --- /dev/null +++ b/cake/libs/socket.php @@ -0,0 +1,291 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Cake Socket connection class. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v 1.2.0 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +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 + */ + 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; + } +} +?> \ No newline at end of file diff --git a/cake/libs/string.php b/cake/libs/string.php new file mode 100755 index 0000000..286017b --- /dev/null +++ b/cake/libs/string.php @@ -0,0 +1,327 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * String handling methods. + * + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v 1.2.0.5551 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * String handling methods. + * + * + * @package cake + * @subpackage cake.cake.libs + */ +class String extends Object { +/** + * Gets a reference to the String object instance + * + * @return object String instance + * @access public + * @static + */ + function &getInstance() { + static $instance = array(); + + if (!$instance) { + $instance[0] =& new String(); + } + return $instance[0]; + } +/** + * 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 + * @return array + * @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: + * + * Sample: String::insert('My name is :name and I am :age years old.', array('name' => 'Bob', '65')); + * Returns: My name is Bob and I am 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']; + + 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 (!is_array($data)) { + $data = array($data); + } + + if (array_keys($data) === array_keys(array_values($data))) { + $offset = 0; + while (($pos = strpos($str, '?', $offset)) !== false) { + $val = array_shift($data); + $offset = $pos + strlen($val); + $str = substr_replace($str, $val, $pos, 1); + } + } else { + asort($data); + + $hashKeys = array_map('md5', array_keys($data)); + $tempData = array_combine(array_keys($data), array_values($hashKeys)); + 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 => $data) { + $str = str_replace($tmpHash, $data, $str); + } + } + + if (!isset($options['format']) && isset($options['before'])) { + $str = str_replace($options['escape'].$options['before'], $options['before'], $str); + } + if (!$options['clean']) { + return $str; + } + return String::cleanInsert($str, $options); + } +/** + * Cleans up a Set::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 Set::insert. + * + * @param string $str + * @param string $options + * @return string + * @access public + * @static + */ + 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; + } +} +?> \ No newline at end of file diff --git a/cake/libs/validation.php b/cake/libs/validation.php new file mode 100755 index 0000000..be0d20c --- /dev/null +++ b/cake/libs/validation.php @@ -0,0 +1,929 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Validation Class. Used for validation of model data + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v 1.2.0.3830 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Deprecated + */ +/** + * Not empty. + */ + define('VALID_NOT_EMPTY', '/.+/'); +/** + * Numbers [0-9] only. + */ + define('VALID_NUMBER', '/^[-+]?\\b[0-9]*\\.?[0-9]+\\b$/'); +/** + * A valid email address. + */ + define('VALID_EMAIL', "/^[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+(?:[a-z]{2,4}|museum|travel)$/i"); +/** + * A valid year (1000-2999). + */ + define('VALID_YEAR', '/^[12][0-9]{3}$/'); +/** + * Offers different validation methods. + * + * Long description for file + * + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP v 1.2.0.3830 + */ +class Validation extends Object { +/** + * Set the the value of methods $check param. + * + * @var string + * @access public + */ + var $check = null; +/** + * Set to a valid regular expression in the class methods. + * Can be set from $regex param also + * + * @var string + * @access public + */ + var $regex = null; +/** + * Some complex patterns needed in multiple places + * + * @var array + * @access private + */ + var $__pattern = array( + 'ip' => '(?:(?:25[0-5]|2[0-4][0-9]|(?:(?:1[0-9])?|[1-9]?)[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|(?:(?:1[0-9])?|[1-9]?)[0-9])', + 'hostname' => '(?:[a-z0-9][-a-z0-9]*\.)*(?:[a-z0-9][-a-z0-9]{0,62})\.(?:(?:[a-z]{2}\.)?[a-z]{2,4}|museum|travel)' + ); +/** + * Some class methods use a country to determine proper validation. + * This can be passed to methods in the $country param + * + * @var string + * @access public + */ + var $country = null; +/** + * Some class methods use a deeper validation when set to true + * + * @var string + * @access public + */ + var $deep = null; +/** + * Some class methods use the $type param to determine which validation to perfom in the method + * + * @var string + * @access public + */ + var $type = null; +/** + * Holds an array of errors messages set in this class. + * These are used for debugging purposes + * + * @var array + * @access public + */ + var $errors = array(); +/** + * Gets a reference to the Validation object instance + * + * @return object Validation instance + * @access public + * @static + */ + function &getInstance() { + static $instance = array(); + + if (!$instance) { + $instance[0] =& new Validation(); + } + return $instance[0]; + } +/** + * Checks that a string contains something other than whitespace + * + * Returns true if string contains something other than whitespace + * + * $check can be passed as an array: + * array('check' => 'valueToCheck'); + * + * @param mixed $check Value to check + * @return boolean Success + * @access public + */ + function notEmpty($check) { + $_this =& Validation::getInstance(); + $_this->__reset(); + $_this->check = $check; + + if (is_array($check)) { + $_this->_extract($check); + } + + if (empty($_this->check) && $_this->check != '0') { + return false; + } + $_this->regex = '/[^\s]+/m'; + return $_this->_check(); + } +/** + * Checks that a string contains only integer or letters + * + * Returns true if string contains only integer or letters + * + * $check can be passed as an array: + * array('check' => 'valueToCheck'); + * + * @param mixed $check Value to check + * @return boolean Success + * @access public + */ + function alphaNumeric($check) { + $_this =& Validation::getInstance(); + $_this->__reset(); + $_this->check = $check; + + if (is_array($check)) { + $_this->_extract($check); + } + + if (empty($_this->check) && $_this->check != '0') { + return false; + } + $_this->regex = '/^[\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{Nd}]+$/mu'; + return $_this->_check(); + } +/** + * Checks that a string length is within s specified range. + * Spaces are included in the character count. + * Returns true is string matches value min, max, or between min and max, + * + * @param string $check Value to check for length + * @param integer $min Minimum value in range (inclusive) + * @param integer $max Maximum value in range (inclusive) + * @return boolean Success + * @access public + */ + function between($check, $min, $max) { + $length = strlen($check); + return ($length >= $min && $length <= $max); + } +/** + * Returns true if field is left blank -OR- only whitespace characters are present in it's value + * Whitespace characters include Space, Tab, Carriage Return, Newline + * + * $check can be passed as an array: + * array('check' => 'valueToCheck'); + * + * @param mixed $check Value to check + * @return boolean Success + * @access public + */ + function blank($check) { + $_this =& Validation::getInstance(); + $_this->__reset(); + $_this->check = $check; + + if (is_array($check)) { + $_this->_extract($check); + } + + $_this->regex = '/[^\\s]/'; + return !$_this->_check(); + } +/** + * Validation of credit card numbers. + * Returns true if $check is in the proper credit card format. + * + * @param mixed $check credit card number to validate + * @param mixed $type 'all' may be passed as a sting, defaults to fast which checks format of most major credit cards + * if an array is used only the values of the array are checked. + * Example: array('amex', 'bankcard', 'maestro') + * @param boolean $deep set to true this will check the Luhn algorithm of the credit card. + * @param string $regex A custom regex can also be passed, this will be used instead of the defined regex values + * @return boolean Success + * @access public + * @see Validation::_luhn() + */ + function cc($check, $type = 'fast', $deep = false, $regex = null) { + $_this =& Validation::getInstance(); + $_this->__reset(); + $_this->check = $check; + $_this->type = $type; + $_this->deep = $deep; + $_this->regex = $regex; + + if (is_array($check)) { + $_this->_extract($check); + } + $_this->check = str_replace(array('-', ' '), '', $_this->check); + + if (strlen($_this->check) < 13) { + return false; + } + + if (!is_null($_this->regex)) { + if ($_this->_check()) { + return $_this->_luhn(); + } + } + $cards = array('all' => array('amex' => '/^3[4|7]\\d{13}$/', + 'bankcard' => '/^56(10\\d\\d|022[1-5])\\d{10}$/', + 'diners' => '/^(?:3(0[0-5]|[68]\\d)\\d{11})|(?:5[1-5]\\d{14})$/', + 'disc' => '/^(?:6011|650\\d)\\d{12}$/', + 'electron' => '/^(?:417500|4917\\d{2}|4913\\d{2})\\d{10}$/', + 'enroute' => '/^2(?:014|149)\\d{11}$/', + 'jcb' => '/^(3\\d{4}|2100|1800)\\d{11}$/', + 'maestro' => '/^(?:5020|6\\d{3})\\d{12}$/', + 'mc' => '/^5[1-5]\\d{14}$/', + 'solo' => '/^(6334[5-9][0-9]|6767[0-9]{2})\\d{10}(\\d{2,3})?$/', + 'switch' => '/^(?:49(03(0[2-9]|3[5-9])|11(0[1-2]|7[4-9]|8[1-2])|36[0-9]{2})\\d{10}(\\d{2,3})?)|(?:564182\\d{10}(\\d{2,3})?)|(6(3(33[0-4][0-9])|759[0-9]{2})\\d{10}(\\d{2,3})?)$/', + 'visa' => '/^4\\d{12}(\\d{3})?$/', + 'voyager' => '/^8699[0-9]{11}$/'), + 'fast' => '/^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6011[0-9]{12}|3(?:0[0-5]|[68][0-9])[0-9]{11}|3[47][0-9]{13})$/'); + + if (is_array($_this->type)) { + foreach ($_this->type as $value) { + $_this->regex = $cards['all'][strtolower($value)]; + + if ($_this->_check()) { + return $_this->_luhn(); + } + } + } elseif ($_this->type == 'all') { + foreach ($cards['all'] as $value) { + $_this->regex = $value; + + if ($_this->_check()) { + return $_this->_luhn(); + } + } + } else { + $_this->regex = $cards['fast']; + + if ($_this->_check()) { + return $_this->_luhn(); + } + } + } +/** + * Used to compare 2 numeric values. + * + * @param mixed $check1 if string is passed for a string must also be passed for $check2 + * used as an array it must be passed as array('check1' => value, 'operator' => 'value', 'check2' -> value) + * @param string $operator Can be either a word or operand + * is greater >, is less <, greater or equal >= + * less or equal <=, is less <, equal to ==, not equal != + * @param integer $check2 only needed if $check1 is a string + * @return boolean Success + * @access public + */ + function comparison($check1, $operator = null, $check2 = null) { + if (is_array($check1)) { + extract($check1, EXTR_OVERWRITE); + } + $operator = str_replace(array(' ', "\t", "\n", "\r", "\0", "\x0B"), '', strtolower($operator)); + + switch ($operator) { + case 'isgreater': + case '>': + if ($check1 > $check2) { + return true; + } + break; + case 'isless': + case '<': + if ($check1 < $check2) { + return true; + } + break; + case 'greaterorequal': + case '>=': + if ($check1 >= $check2) { + return true; + } + break; + case 'lessorequal': + case '<=': + if ($check1 <= $check2) { + return true; + } + break; + case 'equalto': + case '==': + if ($check1 == $check2) { + return true; + } + break; + case 'notequal': + case '!=': + if ($check1 != $check2) { + return true; + } + break; + default: + $_this =& Validation::getInstance(); + $_this->errors[] = __('You must define the $operator parameter for Validation::comparison()', true); + break; + } + return false; + } +/** + * Used when a custom regular expression is needed. + * + * @param mixed $check When used as a string, $regex must also be a valid regular expression. + * As and array: array('check' => value, 'regex' => 'valid regular expression') + * @param string $regex If $check is passed as a string, $regex must also be set to valid regular expression + * @return boolean Success + * @access public + */ + function custom($check, $regex = null) { + $_this =& Validation::getInstance(); + $_this->__reset(); + $_this->check = $check; + $_this->regex = $regex; + if (is_array($check)) { + $_this->_extract($check); + } + if ($_this->regex === null) { + $_this->errors[] = __('You must define a regular expression for Validation::custom()', true); + return false; + } + return $_this->_check(); + } +/** + * Date validation, determines if the string passed is a valid date. + * keys that expect full month, day and year will validate leap years + * + * @param string $check a valid date string + * @param mixed $format Use a string or an array of the keys below. Arrays should be passed as array('dmy', 'mdy', etc) + * Keys: dmy 27-12-2006 or 27-12-06 separators can be a space, period, dash, forward slash + * mdy 12-27-2006 or 12-27-06 separators can be a space, period, dash, forward slash + * ymd 2006-12-27 or 06-12-27 separators can be a space, period, dash, forward slash + * dMy 27 December 2006 or 27 Dec 2006 + * Mdy December 27, 2006 or Dec 27, 2006 comma is optional + * My December 2006 or Dec 2006 + * my 12/2006 separators can be a space, period, dash, forward slash + * @param string $regex If a custom regular expression is used this is the only validation that will occur. + * @return boolean Success + * @access public + */ + function date($check, $format = 'ymd', $regex = null) { + $_this =& Validation::getInstance(); + $_this->__reset(); + $_this->check = $check; + $_this->regex = $regex; + + if (!is_null($_this->regex)) { + return $_this->_check(); + } + + $regex['dmy'] = '%^(?:(?:31(\\/|-|\\.|\\x20)(?:0?[13578]|1[02]))\\1|(?:(?:29|30)(\\/|-|\\.|\\x20)(?:0?[1,3-9]|1[0-2])\\2))(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$|^(?:29(\\/|-|\\.|\\x20)0?2\\3(?:(?:(?:1[6-9]|[2-9]\\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\\d|2[0-8])(\\/|-|\\.|\\x20)(?:(?:0?[1-9])|(?:1[0-2]))\\4(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$%'; + $regex['mdy'] = '%^(?:(?:(?:0?[13578]|1[02])(\\/|-|\\.|\\x20)31)\\1|(?:(?:0?[13-9]|1[0-2])(\\/|-|\\.|\\x20)(?:29|30)\\2))(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$|^(?:0?2(\\/|-|\\.|\\x20)29\\3(?:(?:(?:1[6-9]|[2-9]\\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:(?:0?[1-9])|(?:1[0-2]))(\\/|-|\\.|\\x20)(?:0?[1-9]|1\\d|2[0-8])\\4(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$%'; + $regex['ymd'] = '%^(?:(?:(?:(?:(?:1[6-9]|[2-9]\\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))(\\/|-|\\.|\\x20)(?:0?2\\1(?:29)))|(?:(?:(?:1[6-9]|[2-9]\\d)?\\d{2})(\\/|-|\\.|\\x20)(?:(?:(?:0?[13578]|1[02])\\2(?:31))|(?:(?:0?[1,3-9]|1[0-2])\\2(29|30))|(?:(?:0?[1-9])|(?:1[0-2]))\\2(?:0?[1-9]|1\\d|2[0-8]))))$%'; + $regex['dMy'] = '/^((31(?!\\ (Feb(ruary)?|Apr(il)?|June?|(Sep(?=\\b|t)t?|Nov)(ember)?)))|((30|29)(?!\\ Feb(ruary)?))|(29(?=\\ Feb(ruary)?\\ (((1[6-9]|[2-9]\\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00)))))|(0?[1-9])|1\\d|2[0-8])\\ (Jan(uary)?|Feb(ruary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?|(Sep(?=\\b|t)t?|Nov|Dec)(ember)?)\\ ((1[6-9]|[2-9]\\d)\\d{2})$/'; + $regex['Mdy'] = '/^(?:(((Jan(uary)?|Ma(r(ch)?|y)|Jul(y)?|Aug(ust)?|Oct(ober)?|Dec(ember)?)\\ 31)|((Jan(uary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?|(Sept|Nov|Dec)(ember)?)\\ (0?[1-9]|([12]\\d)|30))|(Feb(ruary)?\\ (0?[1-9]|1\\d|2[0-8]|(29(?=,?\\ ((1[6-9]|[2-9]\\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00)))))))\\,?\\ ((1[6-9]|[2-9]\\d)\\d{2}))$/'; + $regex['My'] = '%^(Jan(uary)?|Feb(ruary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?|(Sep(?=\\b|t)t?|Nov|Dec)(ember)?)[ /]((1[6-9]|[2-9]\\d)\\d{2})$%'; + $regex['my'] = '%^(((0[123456789]|10|11|12)([- /.])(([1][9][0-9][0-9])|([2][0-9][0-9][0-9]))))$%'; + + $format = (is_array($format)) ? array_values($format) : array($format); + foreach ($format as $key) { + $_this->regex = $regex[$key]; + + if ($_this->_check() === true) { + return true; + } + } + return false; + } + +/** + * Time validation, determines if the string passed is a valid time. + * Validates time as 24hr (HH:MM) or am/pm ([H]H:MM[a|p]m) + * Does not allow/validate seconds. + * + * @param string $check a valid time string + * @return boolean Success + * @access public + */ + + function time($check) { + $_this =& Validation::getInstance(); + $_this->__reset(); + $_this->check = $check; + $_this->regex = '%^((0?[1-9]|1[012])(:[0-5]\d){0,2}([AP]M|[ap]m))$|^([01]\d|2[0-3])(:[0-5]\d){0,2}$%'; + return $_this->_check(); + } + +/** + * Boolean validation, determines if value passed is a boolean integer or true/false. + * + * @param string $check a valid boolean + * @return boolean Success + * @access public + */ + function boolean($check) { + $booleanList = array(0, 1, '0', '1', true, false); + return in_array($check, $booleanList, true); + } + +/** + * Checks that a value is a valid decimal. If $places is null, the $check is allowed to be a scientific float + * If no decimal point is found a false will be returned. Both the sign and exponent are optional. + * + * @param integer $check The value the test for decimal + * @param integer $places if set $check value must have exactly $places after the decimal point + * @param string $regex If a custom regular expression is used this is the only validation that will occur. + * @return boolean Success + * @access public + */ + function decimal($check, $places = null, $regex = null) { + $_this =& Validation::getInstance(); + $_this->__reset(); + $_this->regex = $regex; + $_this->check = $check; + + if (is_null($_this->regex)) { + if (is_null($places)) { + $_this->regex = '/^[-+]?[0-9]*\\.{1}[0-9]+(?:[eE][-+]?[0-9]+)?$/'; + } else { + $_this->regex = '/^[-+]?[0-9]*\\.{1}[0-9]{'.$places.'}$/'; + } + } + return $_this->_check(); + } +/** + * Validates for an email address. + * + * @param string $check Value to check + * @param boolean $deep Perform a deeper validation (if true), by also checking availability of host + * @param string $regex Regex to use (if none it will use built in regex) + * @return boolean Success + * @access public + */ + function email($check, $deep = false, $regex = null) { + $_this =& Validation::getInstance(); + $_this->__reset(); + $_this->check = $check; + $_this->regex = $regex; + $_this->deep = $deep; + + if (is_array($check)) { + $_this->_extract($check); + } + + if (is_null($_this->regex)) { + $_this->regex = '/^[a-z0-9!#$%&\'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&\'*+\/=?^_`{|}~-]+)*@' . $_this->__pattern['hostname'] . '$/i'; + } + $return = $_this->_check(); + + if ($_this->deep === false || $_this->deep === null) { + return $return; + } + + if ($return === true && preg_match('/@(' . $_this->__pattern['hostname'] . ')$/i', $_this->check, $regs)) { + $host = gethostbynamel($regs[1]); + return is_array($host); + } + return false; + } +/** + * Check that value is exactly $comparedTo. + * + * @param mixed $check Value to check + * @param mixed $comparedTo Value to compare + * @return boolean Success + * @access public + */ + function equalTo($check, $comparedTo) { + return ($check === $comparedTo); + } +/** + * Check that value has a valid file extension. + * + * @param mixed $check Value to check + * @param array $extensions file extenstions to allow + * @return boolean Success + * @access public + */ + function extension($check, $extensions = array('gif', 'jpeg', 'png', 'jpg')) { + if (is_array($check)) { + return Validation::extension(array_shift($check), $extensions); + } + $extension = strtolower(array_pop(explode('.', $check))); + foreach ($extensions as $value) { + if ($extension == strtolower($value)) { + return true; + } + } + return false; + } +/** + * Check that value is a file name + * + * @param mixed $check Value to check + * @access public + * @todo finish implementation + */ + function file($check) { + // if (is_array($check)) { + // foreach ($check as $value) { + // if (!Validation::file($value)) { + // return false; + // } + // } + // return true; + // } + // + // return preg_match('/[\w| |_]+\.[\w]+/', $check); + } +/** + * Validation of an IPv4 address. + * + * @param string $check The string to test. + * @return boolean Success + * @access public + */ + function ip($check) { + $_this =& Validation::getInstance(); + $_this->check = $check; + $_this->regex = '/^' . $_this->__pattern['ip'] . '$/'; + return $_this->_check(); + } +/** + * Checks whether the length of a string is greater or equal to a minimal length. + * + * @param string $check The string to test + * @param integer $min The minimal string length + * @return boolean Success + * @access public + */ + function minLength($check, $min) { + $length = strlen($check); + return ($length >= $min); + } +/** + * Checks whether the length of a string is smaller or equal to a maximal length.. + * + * @param string $check The string to test + * @param integer $max The maximal string length + * @return boolean Success + * @access public + */ + function maxLength($check, $max) { + $length = strlen($check); + return ($length <= $max); + } +/** + * Checks that a value is a monetary amount. + * + * @param string $check Value to check + * @param string $symbolPosition Where symbol is located (left/right) + * @return boolean Success + * @access public + */ + function money($check, $symbolPosition = 'left') { + $_this =& Validation::getInstance(); + $_this->check = $check; + + if ($symbolPosition == 'right') { + $_this->regex = '/^(?!0,?\d)(?:\d{1,3}(?:([, .])\d{3})?(?:\1\d{3})*|(?:\d+))((?!\1)[,.]\d{2})?(?<!\x{00a2})\p{Sc}?$/u'; + } else { + $_this->regex = '/^(?!\x{00a2})\p{Sc}?(?!0,?\d)(?:\d{1,3}(?:([, .])\d{3})?(?:\1\d{3})*|(?:\d+))((?!\1)[,.]\d{2})?$/u'; + } + return $_this->_check(); + } +/** + * Validate a multiple select. + * + * @param mixed $check Value to check + * @param mixed $options Options for the check. + * Valid options + * in => provide a list of choices that selections must be made from + * max => maximun number of non-zero choices that can be made + * min => minimum number of non-zero choices that can be made + * @return boolean Success + * @access public + */ + function multiple($check, $options = array()) { + $defaults = array('in' => null, 'max' => null, 'min' => null); + $options = array_merge($defaults, $options); + $check = array_filter((array)$check); + if (empty($check)) { + return false; + } + if ($options['max'] && sizeof($check) > $options['max']) { + return false; + } + if ($options['min'] && sizeof($check) < $options['min']) { + return false; + } + if ($options['in'] && is_array($options['in'])) { + foreach ($check as $val) { + if (!in_array($val, $options['in'])) { + return false; + } + } + } + return true; + } +/** + * Checks if a value is numeric. + * + * @param string $check Value to check + * @return boolean Succcess + * @access public + */ + function numeric($check) { + return is_numeric($check); + } +/** + * Check that a value is a valid phone number. + * + * @param mixed $check Value to check (string or array) + * @param string $regex Regular expression to use + * @param string $country Country code (defaults to 'all') + * @return boolean Success + * @access public + */ + function phone($check, $regex = null, $country = 'all') { + $_this =& Validation::getInstance(); + $_this->check = $check; + $_this->regex = $regex; + $_this->country = $country; + if (is_array($check)) { + $_this->_extract($check); + } + + if (is_null($_this->regex)) { + switch ($_this->country) { + case 'us': + // includes all NANPA members. see http://en.wikipedia.org/wiki/North_American_Numbering_Plan#List_of_NANPA_countries_and_territories + default: + $_this->regex = '/^(?:\+?1)?[-. ]?\\(?[2-9][0-8][0-9]\\)?[-. ]?[2-9][0-9]{2}[-. ]?[0-9]{4}$/'; + break; + } + } + return $_this->_check(); + } +/** + * Checks that a given value is a valid postal code. + * + * @param mixed $check Value to check + * @param string $regex Regular expression to use + * @param string $country Country to use for formatting + * @return boolean Success + * @access public + */ + function postal($check, $regex = null, $country = null) { + $_this =& Validation::getInstance(); + $_this->check = $check; + $_this->regex = $regex; + $_this->country = $country; + if (is_array($check)) { + $_this->_extract($check); + } + + if (is_null($_this->regex)) { + switch ($_this->country) { + case 'uk': + $_this->regex = '/\\A\\b[A-Z]{1,2}[0-9][A-Z0-9]? [0-9][ABD-HJLNP-UW-Z]{2}\\b\\z/i'; + break; + case 'ca': + $_this->regex = '/\\A\\b[ABCEGHJKLMNPRSTVXY][0-9][A-Z] [0-9][A-Z][0-9]\\b\\z/i'; + break; + case 'it': + case 'de': + $_this->regex = '/^[0-9]{5}$/i'; + break; + case 'be': + $_this->regex = '/^[1-9]{1}[0-9]{3}$/i'; + break; + case 'us': + default: + $_this->regex = '/\\A\\b[0-9]{5}(?:-[0-9]{4})?\\b\\z/i'; + break; + } + } + return $_this->_check(); + } +/** + * Validate that a number is in specified range. + * if $lower and $upper are not set, will return true if + * $check is a legal finite on this platform + * + * @param string $check Value to check + * @param integer $lower Lower limit + * @param integer $upper Upper limit + * @return boolean Success + * @access public + */ + function range($check, $lower = null, $upper = null ) { + if (!is_numeric($check)) { + return false; + } + if (isset($lower) && isset($upper)) { + return ($check > $lower && $check < $upper); + } + return is_finite($check); + } +/** + * Checks that a value is a valid Social Security Number. + * + * @param mixed $check Value to check + * @param string $regex Regular expression to use + * @param string $country Country + * @return boolean Success + * @access public + */ + function ssn($check, $regex = null, $country = null) { + $_this =& Validation::getInstance(); + $_this->check = $check; + $_this->regex = $regex; + $_this->country = $country; + if (is_array($check)) { + $_this->_extract($check); + } + + if (is_null($_this->regex)) { + switch ($_this->country) { + case 'dk': + $_this->regex = '/\\A\\b[0-9]{6}-[0-9]{4}\\b\\z/i'; + break; + case 'nl': + $_this->regex = '/\\A\\b[0-9]{9}\\b\\z/i'; + break; + case 'us': + default: + $_this->regex = '/\\A\\b[0-9]{3}-[0-9]{2}-[0-9]{4}\\b\\z/i'; + break; + } + } + return $_this->_check(); + } +/** + * Checks that a value is a valid URL according to http://www.w3.org/Addressing/URL/url-spec.txt + * + * The regex checks for the following component parts: + * a valid, optional, scheme + * a valid ip address OR + * a valid domain name as defined by section 2.3.1 of http://www.ietf.org/rfc/rfc1035.txt + * with an optional port number + * an optional valid path + * an optional query string (get parameters) + * an optional fragment (anchor tag) + * + * @param string $check Value to check + * @param boolean $strict Require URL to be prefixed by a valid scheme (one of http(s)/ftp(s)/file/news/gopher) + * @return boolean Success + * @access public + */ + function url($check, $strict = false) { + $_this =& Validation::getInstance(); + $_this->check = $check; + $validChars = '([' . preg_quote('!"$&\'()*+,-.@_:;=') . '\/0-9a-z]|(%[0-9a-f]{2}))'; + $_this->regex = '/^(?:(?:https?|ftps?|file|news|gopher):\/\/)' . ife($strict, '', '?') . + '(?:' . $_this->__pattern['ip'] . '|' . $_this->__pattern['hostname'] . ')(?::[1-9][0-9]{0,3})?' . + '(?:\/?|\/' . $validChars . '*)?' . + '(?:\?' . $validChars . '*)?' . + '(?:#' . $validChars . '*)?$/i'; + return $_this->_check(); + } +/** + * Checks if a value is in a given list. + * + * @param string $check Value to check + * @param array $list List to check against + * @return boolean Succcess + * @access public + */ + function inList($check, $list) { + return in_array($check, $list); + } +/** + * Runs an user-defined validation. + * + * @param mixed $check value that will be validated in user-defined methods. + * @param object $object class that holds validation method + * @param string $method class method name for validation to run + * @param array $args arguments to send to method + * @return mixed user-defined class class method returns + * @access public + */ + function userDefined($check, $object, $method, $args = null) { + return call_user_func_array(array(&$object, $method), array($check, $args)); + } +/** + * Runs a regular expression match. + * + * @return boolean Success of match + * @access protected + */ + function _check() { + $_this =& Validation::getInstance(); + if (preg_match($_this->regex, $_this->check)) { + $_this->error[] = false; + return true; + } else { + $_this->error[] = true; + return false; + } + } +/** + * Get the values to use when value sent to validation method is + * an array. + * + * @param array $params Parameters sent to validation method + * @return void + * @access protected + */ + function _extract($params) { + $_this =& Validation::getInstance(); + extract($params, EXTR_OVERWRITE); + + if (isset($check)) { + $_this->check = $check; + } + if (isset($regex)) { + $_this->regex = $regex; + } + if (isset($country)) { + $_this->country = strtolower($country); + } + if (isset($deep)) { + $_this->deep = $deep; + } + if (isset($type)) { + $_this->type = $type; + } + } +/** + * Luhn algorithm + * + * @see http://en.wikipedia.org/wiki/Luhn_algorithm + * @return boolean Success + * @access protected + */ + function _luhn() { + $_this =& Validation::getInstance(); + if ($_this->deep !== true) { + return true; + } + if ($_this->check == 0) { + return false; + } + $sum = 0; + $length = strlen($_this->check); + + for ($position = 1 - ($length % 2); $position < $length; $position += 2) { + $sum += $_this->check[$position]; + } + + for ($position = ($length % 2); $position < $length; $position += 2) { + $number = $_this->check[$position] * 2; + $sum += ($number < 10) ? $number : $number - 9; + } + + return ($sum % 10 == 0); + } +/** + * Reset internal variables for another validation run. + * + * @return void + * @access private + */ + function __reset() { + $this->check = null; + $this->regex = null; + $this->country = null; + $this->deep = null; + $this->type = null; + $this->error = array(); + $this->errors = array(); + } +} +?> diff --git a/cake/libs/view/elements/dump.ctp b/cake/libs/view/elements/dump.ctp new file mode 100755 index 0000000..54c59ac --- /dev/null +++ b/cake/libs/view/elements/dump.ctp @@ -0,0 +1,30 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.elements + * @since CakePHP(tm) v 0.10.5.1782 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<div id="cakeControllerDump"> + <h2><?php __('Controller dump:'); ?></h2> + <pre> + <?php echo h(print_r($controller, true)); ?> + </pre> +</div> \ No newline at end of file diff --git a/cake/libs/view/elements/email/html/default.ctp b/cake/libs/view/elements/email/html/default.ctp new file mode 100755 index 0000000..6e51c0f --- /dev/null +++ b/cake/libs/view/elements/email/html/default.ctp @@ -0,0 +1,31 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.elements.email.html + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<?php +$content = explode("\n", $content); + +foreach ($content as $line): + echo '<p> ' . $line . '</p>'; +endforeach; +?> \ No newline at end of file diff --git a/cake/libs/view/elements/email/text/default.ctp b/cake/libs/view/elements/email/text/default.ctp new file mode 100755 index 0000000..cbb261c --- /dev/null +++ b/cake/libs/view/elements/email/text/default.ctp @@ -0,0 +1,25 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.elements.email.text + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<?php echo $content; ?> \ No newline at end of file diff --git a/cake/libs/view/errors/error404.ctp b/cake/libs/view/errors/error404.ctp new file mode 100755 index 0000000..b849db7 --- /dev/null +++ b/cake/libs/view/errors/error404.ctp @@ -0,0 +1,29 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.errors + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<h2><?php echo $name; ?></h2> +<p class="error"> + <strong><?php __('Error'); ?>: </strong> + <?php echo sprintf(__("The requested address %s was not found on this server.", true), "<strong>'{$message}'</strong>")?> +</p> \ No newline at end of file diff --git a/cake/libs/view/errors/missing_action.ctp b/cake/libs/view/errors/missing_action.ctp new file mode 100755 index 0000000..0de1c6c --- /dev/null +++ b/cake/libs/view/errors/missing_action.ctp @@ -0,0 +1,51 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.errors + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<h2><?php echo sprintf(__('Missing Method in %s', true), $controller);?></h2> +<p class="error"> + <strong><?php __('Error') ?>: </strong> + <?php echo sprintf(__('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 echo sprintf(__('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 echo sprintf(__('If you want to customize this error message, create %s.', true), APP_DIR . DS . "views" . DS . "errors" . DS . "missing_action.ctp");?> +</p> \ No newline at end of file diff --git a/cake/libs/view/errors/missing_component_class.ctp b/cake/libs/view/errors/missing_component_class.ctp new file mode 100755 index 0000000..ec154de --- /dev/null +++ b/cake/libs/view/errors/missing_component_class.ctp @@ -0,0 +1,44 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.errors + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<h2><?php __('Missing Component Class'); ?></h2> +<p class="error"> + <strong><?php __('Error'); ?>: </strong> + <?php echo sprintf(__('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 echo sprintf(__('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 echo sprintf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . "views" . DS . "errors" . DS . "missing_component_class.ctp");?> +</p> \ No newline at end of file diff --git a/cake/libs/view/errors/missing_component_file.ctp b/cake/libs/view/errors/missing_component_file.ctp new file mode 100755 index 0000000..3b8b874 --- /dev/null +++ b/cake/libs/view/errors/missing_component_file.ctp @@ -0,0 +1,44 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.errors + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<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 echo sprintf(__('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 echo sprintf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . "views" . DS . "errors" . DS . "missing_component_file.ctp");?> +</p> \ No newline at end of file diff --git a/cake/libs/view/errors/missing_connection.ctp b/cake/libs/view/errors/missing_connection.ctp new file mode 100755 index 0000000..1a1c1ad --- /dev/null +++ b/cake/libs/view/errors/missing_connection.ctp @@ -0,0 +1,37 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.errors + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<h2><?php __('Missing Database Connection'); ?></h2> +<p class="error"> + <strong><?php __('Error'); ?>: </strong> + <?php echo sprintf(__('%s requires a database connection', true), $model);?> +</p> +<p class="error"> + <strong><?php __('Error'); ?>: </strong> + <?php echo sprintf(__('Confirm you have created the file : %s.', true), APP_DIR.DS.'config'.DS.'database.php');?> +</p> +<p class="notice"> + <strong><?php __('Notice'); ?>: </strong> + <?php echo sprintf(__('If you want to customize this error message, create %s.', true), APP_DIR.DS.'views'.DS.'errors'.DS.basename(__FILE__));?> +</p> \ No newline at end of file diff --git a/cake/libs/view/errors/missing_controller.ctp b/cake/libs/view/errors/missing_controller.ctp new file mode 100755 index 0000000..6767472 --- /dev/null +++ b/cake/libs/view/errors/missing_controller.ctp @@ -0,0 +1,45 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.errors + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<h2><?php __('Missing Controller'); ?></h2> +<p class="error"> + <strong><?php __('Error'); ?>: </strong> + <?php echo sprintf(__('%s could not be found.', true), "<em>" . $controller . "</em>");?> +</p> +<p class="error"> + <strong><?php __('Error'); ?>: </strong> + <?php echo sprintf(__('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 echo sprintf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . "views" . DS . "errors" . DS . "missing_controller.ctp");?> +</p> \ No newline at end of file diff --git a/cake/libs/view/errors/missing_helper_class.ctp b/cake/libs/view/errors/missing_helper_class.ctp new file mode 100755 index 0000000..e1f464c --- /dev/null +++ b/cake/libs/view/errors/missing_helper_class.ctp @@ -0,0 +1,44 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.errors + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<h2><?php __('Missing Helper Class'); ?></h2> +<p class="error"> + <strong><?php __('Error'); ?>: </strong> + <?php echo sprintf(__("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 echo sprintf(__('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 echo sprintf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . "views" . DS . "errors" . DS . "missing_helper_class.ctp");?> +</p> \ No newline at end of file diff --git a/cake/libs/view/errors/missing_helper_file.ctp b/cake/libs/view/errors/missing_helper_file.ctp new file mode 100755 index 0000000..34d6d5d --- /dev/null +++ b/cake/libs/view/errors/missing_helper_file.ctp @@ -0,0 +1,44 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.errors + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<h2><?php __('Missing Helper File'); ?></h2> +<p class="error"> + <strong><?php __('Error'); ?>: </strong> + <?php echo sprintf(__("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 echo sprintf(__('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 echo sprintf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . "views" . DS . "errors" . DS . "missing_helper_file.ctp");?> +</p> diff --git a/cake/libs/view/errors/missing_layout.ctp b/cake/libs/view/errors/missing_layout.ctp new file mode 100755 index 0000000..cb8445a --- /dev/null +++ b/cake/libs/view/errors/missing_layout.ctp @@ -0,0 +1,37 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.errors + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<h2><?php __('Missing Layout'); ?></h2> +<p class="error"> + <strong><?php __('Error'); ?>: </strong> + <?php echo sprintf(__("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 echo sprintf(__('Confirm you have created the file: %s', true), "<em>" . $file . "</em>");?> +</p> +<p class="notice"> + <strong><?php __('Notice'); ?>: </strong> + <?php echo sprintf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . "views" . DS . "errors" . DS . "missing_layout.ctp");?> +</p> \ No newline at end of file diff --git a/cake/libs/view/errors/missing_model.ctp b/cake/libs/view/errors/missing_model.ctp new file mode 100755 index 0000000..c2d64e9 --- /dev/null +++ b/cake/libs/view/errors/missing_model.ctp @@ -0,0 +1,46 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.errors + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<h2><?php __('Missing Model'); ?></h2> +<p class="error"> + <strong><?php __('Error'); ?>: </strong> + <?php echo sprintf(__("<em>%s</em> could not be found.", true), $model);?> +</p> +<p class="error"> + <strong><?php __('Error'); ?>: </strong> + <?php echo sprintf(__('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 echo sprintf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . "views" . DS . "errors" . DS . "missing_model.ctp");?> +</p> \ No newline at end of file diff --git a/cake/libs/view/errors/missing_scaffolddb.ctp b/cake/libs/view/errors/missing_scaffolddb.ctp new file mode 100755 index 0000000..4431992 --- /dev/null +++ b/cake/libs/view/errors/missing_scaffolddb.ctp @@ -0,0 +1,37 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.errors + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<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 echo sprintf(__('Confirm you have created the file: %s', true), APP_DIR . DS . "config" . DS . "database.php");?> +</p> +<p class="notice"> + <strong><?php __('Notice'); ?>: </strong> + <?php echo sprintf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . "views" . DS . "errors" . DS . "missing_scaffolddb.ctp");?> +</p> \ No newline at end of file diff --git a/cake/libs/view/errors/missing_table.ctp b/cake/libs/view/errors/missing_table.ctp new file mode 100755 index 0000000..1bdc59f --- /dev/null +++ b/cake/libs/view/errors/missing_table.ctp @@ -0,0 +1,33 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.errors + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<h2><?php __('Missing Database Table'); ?></h2> +<p class="error"> + <strong><?php __('Error'); ?>: </strong> + <?php echo sprintf(__('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 echo sprintf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . "views" . DS . "errors" . DS . "missing_table.ctp");?> +</p> \ No newline at end of file diff --git a/cake/libs/view/errors/missing_view.ctp b/cake/libs/view/errors/missing_view.ctp new file mode 100755 index 0000000..737da88 --- /dev/null +++ b/cake/libs/view/errors/missing_view.ctp @@ -0,0 +1,37 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.errors + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<h2><?php __('Missing View'); ?></h2> +<p class="error"> + <strong><?php __('Error'); ?>: </strong> + <?php echo sprintf(__('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 echo sprintf(__('Confirm you have created the file: %s', true), $file);?> +</p> +<p class="notice"> + <strong><?php __('Notice'); ?>: </strong> + <?php echo sprintf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . "views" . DS . "errors" . DS . "missing_view.ctp");?> +</p> \ No newline at end of file diff --git a/cake/libs/view/errors/private_action.ctp b/cake/libs/view/errors/private_action.ctp new file mode 100755 index 0000000..6d9e92c --- /dev/null +++ b/cake/libs/view/errors/private_action.ctp @@ -0,0 +1,33 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.errors + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<h2><?php echo sprintf(__('Private Method in %s', true), $controller);?></h2> +<p class="error"> + <strong><?php __('Error'); ?>: </strong> + <?php echo sprintf(__("%s%s cannot be accessed directly.", true), "<em>" . $controller . "::</em>", "<em>" . $action . "()</em>");?> +</p> +<p class="notice"> + <strong><?php __('Notice'); ?>: </strong> + <?php echo sprintf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . "views" . DS . "errors" . DS . "private_action.ctp");?> +</p> \ No newline at end of file diff --git a/cake/libs/view/errors/scaffold_error.ctp b/cake/libs/view/errors/scaffold_error.ctp new file mode 100755 index 0000000..9ce156a --- /dev/null +++ b/cake/libs/view/errors/scaffold_error.ctp @@ -0,0 +1,40 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.errors + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<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 echo sprintf(__('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> \ No newline at end of file diff --git a/cake/libs/view/helper.php b/cake/libs/view/helper.php new file mode 100755 index 0000000..076622c --- /dev/null +++ b/cake/libs/view/helper.php @@ -0,0 +1,753 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Backend for helpers. + * + * Internal methods for the Helpers. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Included libs + */ +App::import('Core', 'Overloadable'); + +/** + * Backend for helpers. + * + * Long description for class + * + * @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; +/** + * Theme name + * + * @var string + */ + var $themeWeb = 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 + * @return array merged tags from config/$name.php + */ + 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 an URL pointing to a combination of controller and action. Param + * $url can be: + * + Empty - the method will find adress to actuall controller/action. + * + '/' - the method will find base URL of application. + * + A combination of controller/action - the method will find url for it. + * + * @param mixed $url Cake-relative URL, like "/products/edit/92" or "/presidents/elect/4" + * or an array specifying any of the following: 'controller', 'action', + * and/or 'plugin', in addition to named arguments (keyed array elements), + * and standard URL arguments (indexed array elements) + * @param boolean $full If true, the full base URL will be prepended to the result + * @return string Full translated URL with base path. + */ + 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 + * @return string $webPath web path to file. + */ + function webroot($file) { + $webPath = "{$this->webroot}" . $file; + if (!empty($this->themeWeb)) { + $os = env('OS'); + if (!empty($os) && strpos($os, 'Windows') !== false) { + if (strpos(WWW_ROOT . $this->themeWeb . $file, '\\') !== false) { + $path = str_replace('/', '\\', WWW_ROOT . $this->themeWeb . $file); + } + } else { + $path = WWW_ROOT . $this->themeWeb . $file; + } + if (file_exists($path)) { + $webPath = "{$this->webroot}" . $this->themeWeb . $file; + } + } + if (strpos($webPath, '//') !== false) { + return str_replace('//', '/', $webPath); + } + return $webPath; + } + +/** + * Used to remove harmful tags from content + * + * @param mixed $output + * @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 + * + true + * + 'true' + * + * 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. + * + * @param array $options Array of options. + * @param array $exclude Array of options to be excluded. + * @param string $insertBefore String to be inserted before options. + * @param string $insertAfter String to be inserted ater options. + * @return string + */ + 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((array)$exclude, array('escape'))); + $values = array_intersect_key(array_values($options), $keys); + $escape = $options['escape']; + $attributes = array(); + + foreach ($keys as $index => $key) { + $attributes[] = $this->__formatAttribute($key, $values[$index], $escape); + } + $out = implode(' ', $attributes); + } else { + $out = $options; + } + return $out ? $insertBefore . $out . $insertAfter : ''; + } +/** + * @param string $key + * @param string $value + * @return string + * @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 == $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 + */ + function setEntity($entity, $setScope = false) { + $view =& ClassRegistry::getObject('view'); + + if ($setScope) { + $view->modelScope = false; + } elseif (join('.', $view->entity()) == $entity) { + return; + } + + if ($entity === null) { + $view->model = null; + $view->association = null; + $view->modelId = null; + $view->modelScope = false; + return; + } + + $model = $view->model; + $sameScope = $hasField = false; + $parts = array_values(Set::filter(explode('.', $entity), true)); + + if (empty($parts)) { + return; + } + + if (count($parts) === 1 || is_numeric($parts[0])) { + $sameScope = true; + } else { + if (ClassRegistry::isKeySet($parts[0])) { + $model = $parts[0]; + } + } + + if (ClassRegistry::isKeySet($model)) { + $ModelObj =& ClassRegistry::getObject($model); + for ($i = 0; $i < count($parts); $i++) { + if ($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; + } + + 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 + */ + 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 + */ + function modelID() { + $view =& ClassRegistry::getObject('view'); + return $view->modelId; + } +/** + * Gets the currently-used model field of the rendering context. + * + * @return string + */ + 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 string + * @param string $field Fieldname as 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) { + foreach (array('model', 'field', 'modelID') as $key) { + if (empty(${$key})) { + ${$key} = $this->{$key}(); + } + } + $view =& ClassRegistry::getObject('view'); + $errors = $this->validationErrors; + + if ($view->model !== $model && isset($errors[$view->model][$model])) { + $errors = $errors[$view->model]; + } + + if (!isset($modelID)) { + return empty($errors[$model][$field]) ? 0 : $errors[$model][$field]; + } else { + return empty($errors[$model][$modelID][$field]) ? 0 : $errors[$model][$modelID][$field]; + } + } +/** + * Generates a DOM ID for the selected element, if one is not set. + * + * @param mixed $options + * @param string $id + * @return mixed + */ + 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(); + } + + $dom = $this->model() . $this->modelID() . Inflector::camelize($view->field) . Inflector::camelize($view->fieldSuffix); + + 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 + * + * @param array $options + * @param string $key + * @return array + */ + 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[' . join('][', $view->entity()) . ']'; + break; + } + + if (is_array($options)) { + $options[$key] = $name; + return $options; + } else { + return $name; + } + } +/** + * Gets the data for the current tag + * + * @param array $options + * @param string $key + * @return array + * @access public + */ + function value($options = array(), $field = null, $key = 'value') { + if ($options === null) { + $options = array(); + } elseif (is_string($options)) { + $field = $options; + $options = 0; + } + + if (!empty($field)) { + $this->setEntity($field); + } + + if (is_array($options) && isset($options[$key])) { + return $options; + } + + $result = null; + + $modelName = $this->model(); + $fieldName = $this->field(); + $modelID = $this->modelID(); + + if (is_null($fieldName)) { + $fieldName = $modelName; + $modelName = null; + } + + if (isset($this->data[$fieldName]) && $modelName === null) { + $result = $this->data[$fieldName]; + } elseif (isset($this->data[$modelName][$fieldName])) { + $result = $this->data[$modelName][$fieldName]; + } elseif (isset($this->data[$fieldName]) && is_array($this->data[$fieldName])) { + if (ClassRegistry::isKeySet($fieldName)) { + $model =& ClassRegistry::getObject($fieldName); + $result = $this->__selectedArray($this->data[$fieldName], $model->primaryKey); + } + } elseif (isset($this->data[$modelName][$modelID][$fieldName])) { + $result = $this->data[$modelName][$modelID][$fieldName]; + } + + if (is_array($result)) { + $view =& ClassRegistry::getObject('view'); + if (array_key_exists($view->fieldSuffix, $result)) { + $result = $result[$view->fieldSuffix]; + } + } + + if (is_array($options)) { + if (empty($result) && 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 + * + * @param array $options + * @param string $key + * @return array + * @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 + * @param string $class + * @param string $key + * @return array + */ + 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 + */ + function output($str) { + return $str; + } +/** + * Before render callback. Overridden in subclasses. + * + */ + function beforeRender() { + } +/** + * After render callback. Overridden in subclasses. + * + */ + function afterRender() { + } +/** + * Before layout callback. Overridden in subclasses. + * + */ + function beforeLayout() { + } +/** + * After layout callback. Overridden in subclasses. + * + */ + 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 + * + * @access private + */ + function __reset() { + $this->__tainted = null; + $this->__cleaned = null; + } +/** + * Removes harmful content from output + * + * @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); + } +} +?> \ No newline at end of file diff --git a/cake/libs/view/helpers/ajax.php b/cake/libs/view/helpers/ajax.php new file mode 100755 index 0000000..ed0de97 --- /dev/null +++ b/cake/libs/view/helpers/ajax.php @@ -0,0 +1,984 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Helper for AJAX operations. + * + * Helps doing AJAX using the Prototype library. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.helpers + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * AjaxHelper helper library. + * + * Helps doing AJAX using the Prototype library. + * + * @package cake + * @subpackage cake.cake.libs.view.helpers + */ +class AjaxHelper extends AppHelper { +/** + * Included helpers. + * + * @var array + */ + var $helpers = array('Html', 'Javascript', 'Form'); +/** + * HtmlHelper instance + * + * @var HtmlHelper + * @access public + */ + var $Html = null; +/** + * JavaScriptHelper instance + * + * @var JavaScriptHelper + * @access public + */ + var $Javascript = null; +/** + * Names of Javascript callback functions. + * + * @var array + */ + var $callbacks = array( + 'complete', 'create', 'exception', 'failure', 'interactive', 'loading', + 'loaded', 'success', 'uninitialized' + ); +/** + * Names of AJAX options. + * + * @var array + */ + var $ajaxOptions = array( + 'after', 'asynchronous', 'before', 'confirm', 'condition', 'contentType', 'encoding', + 'evalScripts', 'failure', 'fallback', 'form', 'indicator', 'insertion', 'interactive', + 'loaded', 'loading', 'method', 'onCreate', 'onComplete', 'onException', 'onFailure', + 'onInteractive', 'onLoaded', 'onLoading', 'onSuccess', 'onUninitialized', 'parameters', + 'position', 'postBody', 'requestHeaders', 'success', 'type', 'update', 'with' + ); +/** + * Options for draggable. + * + * @var array + */ + var $dragOptions = array( + 'handle', 'revert', 'snap', 'zindex', 'constraint', 'change', 'ghosting', + 'starteffect', 'reverteffect', 'endeffect', 'scroll', 'scrollSensitivity', + 'onStart', 'onDrag', 'onEnd' + ); +/** + * Options for droppable. + * + * @var array + */ + var $dropOptions = array( + 'accept', 'containment', 'greedy', 'hoverclass', 'onHover', 'onDrop', 'overlap' + ); +/** + * Options for sortable. + * + * @var array + */ + var $sortOptions = array( + 'constraint', 'containment', 'dropOnEmpty', 'ghosting', 'handle', 'hoverclass', 'onUpdate', + 'onChange', 'only', 'overlap', 'scroll', 'scrollSensitivity', 'scrollSpeed', 'tag', 'tree', + 'treeTag', 'update' + ); +/** + * Options for slider. + * + * @var array + */ + var $sliderOptions = array( + 'alignX', 'alignY', 'axis', 'disabled', 'handleDisabled', 'handleImage', 'increment', + 'maximum', 'minimum', 'onChange', 'onSlide', 'range', 'sliderValue', 'values' + ); +/** + * Options for in-place editor. + * + * @var array + */ + var $editorOptions = array( + 'okText', 'cancelText', 'savingText', 'formId', 'externalControl', 'rows', 'cols', 'size', + 'highlightcolor', 'highlightendcolor', 'savingClassName', 'formClassName', 'loadTextURL', + 'loadingText', 'callback', 'ajaxOptions', 'clickToEditText', 'collection', 'okControl', + 'cancelControl', 'submitOnBlur' + ); +/** + * Options for auto-complete editor. + * + * @var array + */ + var $autoCompleteOptions = array( + 'afterUpdateElement', 'callback', 'frequency', 'indicator', 'minChars', 'onShow', 'onHide', + 'parameters', 'paramName', 'tokens', 'updateElement' + ); +/** + * Output buffer for Ajax update content + * + * @var array + */ + var $__ajaxBuffer = array(); +/** + * Returns link to remote action + * + * Returns a link to a remote action defined by <i>options[url]</i> + * (using the url() format) that's called in the background using + * XMLHttpRequest. The result of that request can then be inserted into a + * DOM object whose id can be specified with <i>options[update]</i>. + * + * Examples: + * <code> + * link("Delete this post", + * array("update" => "posts", "url" => "delete/{$postid->id}")); + * link(imageTag("refresh"), + * array("update" => "emails", "url" => "list_emails" )); + * </code> + * + * By default, these remote requests are processed asynchronous during + * which various callbacks can be triggered (for progress indicators and + * the likes). + * + * Example: + * <code> + * link (word, + * array("url" => "undo", "n" => word_counter), + * array("complete" => "undoRequestCompleted(request)")); + * </code> + * + * The callbacks that may be specified are: + * + * - <i>loading</i>:: Called when the remote document is being + * loaded with data by the browser. + * - <i>loaded</i>:: Called when the browser has finished loading + * the remote document. + * - <i>interactive</i>:: Called when the user can interact with the + * remote document, even though it has not + * finished loading. + * - <i>complete</i>:: Called when the XMLHttpRequest is complete. + * + * If you for some reason or another need synchronous processing (that'll + * block the browser while the request is happening), you can specify + * <i>options[type] = synchronous</i>. + * + * You can customize further browser side call logic by passing + * in Javascript code snippets via some optional parameters. In + * their order of use these are: + * + * - <i>confirm</i>:: Adds confirmation dialog. + * -<i>condition</i>:: Perform remote request conditionally + * by this expression. Use this to + * describe browser-side conditions when + * request should not be initiated. + * - <i>before</i>:: Called before request is initiated. + * - <i>after</i>:: Called immediately after request was + * initiated and before <i>loading</i>. + * + * @param string $title Title of link + * @param string $href Href string "/products/view/12" + * @param array $options Options for JavaScript function + * @param string $confirm Confirmation message. Calls up a JavaScript confirm() message. + * @param boolean $escapeTitle Escaping the title string to HTML entities + * + * @return string HTML code for link to remote action + */ + function link($title, $href = null, $options = array(), $confirm = null, $escapeTitle = true) { + if (!isset($href)) { + $href = $title; + } + if (!isset($options['url'])) { + $options['url'] = $href; + } + + if (isset($confirm)) { + $options['confirm'] = $confirm; + unset($confirm); + } + $htmlOptions = $this->__getHtmlOptions($options, array('url')); + + if (empty($options['fallback']) || !isset($options['fallback'])) { + $options['fallback'] = $href; + } + $htmlDefaults = array('id' => 'link' . intval(mt_rand()), 'onclick' => ''); + $htmlOptions = array_merge($htmlDefaults, $htmlOptions); + + $htmlOptions['onclick'] .= ' event.returnValue = false; return false;'; + $return = $this->Html->link($title, $href, $htmlOptions, null, $escapeTitle); + $callback = $this->remoteFunction($options); + $script = $this->Javascript->event("'{$htmlOptions['id']}'", "click", $callback); + + if (is_string($script)) { + $return .= $script; + } + return $return; + } +/** + * Creates JavaScript function for remote AJAX call + * + * This function creates the javascript needed to make a remote call + * it is primarily used as a helper for AjaxHelper::link. + * + * @param array $options options for javascript + * @return string html code for link to remote action + * @see AjaxHelper::link() for docs on options parameter. + */ + function remoteFunction($options) { + if (isset($options['update'])) { + if (!is_array($options['update'])) { + $func = "new Ajax.Updater('{$options['update']}',"; + } else { + $func = "new Ajax.Updater(document.createElement('div'),"; + } + if (!isset($options['requestHeaders'])) { + $options['requestHeaders'] = array(); + } + if (is_array($options['update'])) { + $options['update'] = join(' ', $options['update']); + } + $options['requestHeaders']['X-Update'] = $options['update']; + } else { + $func = "new Ajax.Request("; + } + + $func .= "'" . $this->url(isset($options['url']) ? $options['url'] : "") . "'"; + $func .= ", " . $this->__optionsForAjax($options) . ")"; + + if (isset($options['before'])) { + $func = "{$options['before']}; $func"; + } + if (isset($options['after'])) { + $func = "$func; {$options['after']};"; + } + if (isset($options['condition'])) { + $func = "if ({$options['condition']}) { $func; }"; + } + + if (isset($options['confirm'])) { + $func = "if (confirm('" . $this->Javascript->escapeString($options['confirm']) + . "')) { $func; } else { event.returnValue = false; return false; }"; + } + return $func; + } +/** + * Periodically call remote url via AJAX. + * + * Periodically calls the specified url (<i>options[url]</i>) every <i>options[frequency]</i> + * seconds (default is 10). Usually used to update a specified div (<i>options[update]</i>) with + * the results of the remote call. The options for specifying the target with url and defining + * callbacks is the same as AjaxHelper::link(). + * + * @param array $options Callback options + * @return string Javascript code + * @see AjaxHelper::link() + */ + function remoteTimer($options = null) { + $frequency = (isset($options['frequency'])) ? $options['frequency'] : 10; + $callback = $this->remoteFunction($options); + $code = "new PeriodicalExecuter(function() {{$callback}}, $frequency)"; + return $this->Javascript->codeBlock($code); + } +/** + * Returns form tag that will submit using Ajax. + * + * Returns a form tag that will submit using XMLHttpRequest in the background instead of the regular + * reloading POST arrangement. Even though it's using Javascript to serialize the form elements, + * the form submission will work just like a regular submission as viewed by the receiving side + * (all elements available in params). The options for defining callbacks is the same + * as AjaxHelper::link(). + * + * @param mixed $params Either a string identifying the form target, or an array of method parameters, including: + * - 'params' => Acts as the form target + * - 'type' => 'post' or 'get' + * - 'options' => An array containing all HTML and script options used to + * generate the form tag and Ajax request. + * @param array $type How form data is posted: 'get' or 'post' + * @param array $options Callback/HTML options + * @return string JavaScript/HTML code + * @see AjaxHelper::link() + */ + function form($params = null, $type = 'post', $options = array()) { + $model = false; + if (is_array($params)) { + extract($params, EXTR_OVERWRITE); + } + + if (empty($options['url'])) { + $options['url'] = array('action' => $params); + } + + $htmlDefaults = array( + 'id' => 'form' . intval(mt_rand()), + 'onsubmit' => "event.returnValue = false; return false;", + 'type' => $type + ); + $htmlOptions = $this->__getHtmlOptions($options, array('model', 'with')); + $htmlOptions = array_merge($htmlDefaults, $htmlOptions); + + $defaults = array('model' => $model, 'with' => "Form.serialize('{$htmlOptions['id']}')"); + $options = array_merge($defaults, $options); + $callback = $this->remoteFunction($options); + + $form = $this->Form->create($options['model'], $htmlOptions); + $script = $this->Javascript->event("'" . $htmlOptions['id']. "'", 'submit', $callback); + return $form . $script; + } +/** + * Returns a button input tag that will submit using Ajax + * + * Returns a button input tag that will submit form using XMLHttpRequest in the background instead + * of regular reloading POST arrangement. <i>options</i> argument is the same as + * in AjaxHelper::form(). + * + * @param string $title Input button title + * @param array $options Callback options + * @return string Ajaxed input button + * @see AjaxHelper::form() + */ + function submit($title = 'Submit', $options = array()) { + $htmlOptions = $this->__getHtmlOptions($options); + $htmlOptions['value'] = $title; + + if (!isset($options['with'])) { + $options['with'] = 'Form.serialize(Event.element(event).form)'; + } + if (!isset($htmlOptions['id'])) { + $htmlOptions['id'] = 'submit' . intval(mt_rand()); + } + + $htmlOptions['onclick'] = "event.returnValue = false; return false;"; + $callback = $this->remoteFunction($options); + + $form = $this->Form->submit($title, $htmlOptions); + $script = $this->Javascript->event('"' . $htmlOptions['id'] . '"', 'click', $callback); + return $form . $script; + } +/** + * Observe field and call ajax on change. + * + * Observes the field with the DOM ID specified by <i>field</i> and makes + * an Ajax when its contents have changed. + * + * Required +options+ are: + * - <i>frequency</i>:: The frequency (in seconds) at which changes to + * this field will be detected. + * - <i>url</i>:: @see url() -style options for the action to call + * when the field has changed. + * + * Additional options are: + * - <i>update</i>:: Specifies the DOM ID of the element whose + * innerHTML should be updated with the + * XMLHttpRequest response text. + * - <i>with</i>:: A Javascript expression specifying the + * parameters for the XMLHttpRequest. This defaults + * to Form.Element.serialize('$field'), which can be + * accessed from params['form']['field_id']. + * + * Additionally, you may specify any of the options documented in + * @see linkToRemote(). + * + * @param string $field DOM ID of field to observe + * @param array $options ajax options + * @return string ajax script + */ + function observeField($field, $options = array()) { + if (!isset($options['with'])) { + $options['with'] = 'Form.Element.serialize(\'' . $field . '\')'; + } + $observer = 'Observer'; + if (!isset($options['frequency']) || intval($options['frequency']) == 0) { + $observer = 'EventObserver'; + } + return $this->Javascript->codeBlock( + $this->_buildObserver('Form.Element.' . $observer, $field, $options) + ); + } +/** + * Observe entire form and call ajax on change. + * + * Like @see observeField(), but operates on an entire form identified by the + * DOM ID <b>form</b>. <b>options</b> are the same as <b>observeField</b>, except + * the default value of the <i>with</i> option evaluates to the + * serialized (request string) value of the form. + * + * @param string $form DOM ID of form to observe + * @param array $options ajax options + * @return string ajax script + */ + function observeForm($form, $options = array()) { + if (!isset($options['with'])) { + $options['with'] = 'Form.serialize(\'' . $form . '\')'; + } + $observer = 'Observer'; + if (!isset($options['frequency']) || intval($options['frequency']) == 0) { + $observer = 'EventObserver'; + } + return $this->Javascript->codeBlock( + $this->_buildObserver('Form.' . $observer, $form, $options) + ); + } +/** + * Create a text field with Autocomplete. + * + * Creates an autocomplete field with the given ID and options. + * + * options['with'] defaults to "Form.Element.serialize('$field')", + * but can be any valid javascript expression defining the additional fields. + * + * @param string $field DOM ID of field to observe + * @param string $url URL for the autocomplete action + * @param array $options Ajax options + * @return string Ajax script + */ + function autoComplete($field, $url = "", $options = array()) { + $var = ''; + if (isset($options['var'])) { + $var = 'var ' . $options['var'] . ' = '; + unset($options['var']); + } + + if (!isset($options['id'])) { + $options['id'] = Inflector::camelize(str_replace(".", "_", $field)); + } + + $divOptions = array( + 'id' => $options['id'] . "_autoComplete", + 'class' => isset($options['class']) ? $options['class'] : 'auto_complete' + ); + + if (isset($options['div_id'])) { + $divOptions['id'] = $options['div_id']; + unset($options['div_id']); + } + + $htmlOptions = $this->__getHtmlOptions($options); + $htmlOptions['autocomplete'] = "off"; + + foreach ($this->autoCompleteOptions as $opt) { + unset($htmlOptions[$opt]); + } + + if (isset($options['tokens'])) { + if (is_array($options['tokens'])) { + $options['tokens'] = $this->Javascript->object($options['tokens']); + } else { + $options['tokens'] = '"' . $options['tokens'] . '"'; + } + } + + $options = $this->_optionsToString($options, array('paramName', 'indicator')); + $options = $this->_buildOptions($options, $this->autoCompleteOptions); + + + $text = $this->Form->text($field, $htmlOptions); + $div = $this->Html->div(null, '', $divOptions); + $script = "{$var}new Ajax.Autocompleter('{$htmlOptions['id']}', '{$divOptions['id']}', '"; + $script .= $this->Html->url($url) . "', {$options});"; + + return "{$text}\n{$div}\n" . $this->Javascript->codeBlock($script); + } +/** + * Creates an Ajax-updateable DIV element + * + * @param string $id options for javascript + * @return string HTML code + */ + function div($id, $options = array()) { + if (env('HTTP_X_UPDATE') != null) { + $this->Javascript->enabled = false; + $divs = explode(' ', env('HTTP_X_UPDATE')); + + if (in_array($id, $divs)) { + @ob_end_clean(); + ob_start(); + return ''; + } + } + $attr = $this->_parseAttributes(array_merge($options, array('id' => $id))); + return $this->output(sprintf($this->Html->tags['blockstart'], $attr)); + } +/** + * Closes an Ajax-updateable DIV element + * + * @param string $id The DOM ID of the element + * @return string HTML code + */ + function divEnd($id) { + if (env('HTTP_X_UPDATE') != null) { + $divs = explode(' ', env('HTTP_X_UPDATE')); + if (in_array($id, $divs)) { + $this->__ajaxBuffer[$id] = ob_get_contents(); + ob_end_clean(); + ob_start(); + return ''; + } + } + return $this->output($this->Html->tags['blockend']); + } +/** + * Detects Ajax requests + * + * @return boolean True if the current request is a Prototype Ajax update call + */ + function isAjax() { + return (isset($this->params['isAjax']) && $this->params['isAjax'] === true); + } +/** + * Creates a draggable element. For a reference on the options for this function, + * check out http://github.com/madrobby/scriptaculous/wikis/draggable + * + * @param unknown_type $id + * @param array $options + * @return unknown + */ + function drag($id, $options = array()) { + $var = ''; + if (isset($options['var'])) { + $var = 'var ' . $options['var'] . ' = '; + unset($options['var']); + } + $options = $this->_buildOptions( + $this->_optionsToString($options, array('handle', 'constraint')), $this->dragOptions + ); + return $this->Javascript->codeBlock("{$var}new Draggable('$id', " .$options . ");"); + } +/** + * For a reference on the options for this function, check out + * http://github.com/madrobby/scriptaculous/wikis/droppables + * + * @param unknown_type $id + * @param array $options + * @return string + */ + function drop($id, $options = array()) { + $optionsString = array('overlap', 'hoverclass'); + if (!isset($options['accept']) || !is_array($options['accept'])) { + $optionsString[] = 'accept'; + } else if (isset($options['accept'])) { + $options['accept'] = $this->Javascript->object($options['accept']); + } + $options = $this->_buildOptions( + $this->_optionsToString($options, $optionsString), $this->dropOptions + ); + return $this->Javascript->codeBlock("Droppables.add('{$id}', {$options});"); + } +/** + * Make an element with the given $id droppable, and trigger an Ajax call when a draggable is + * dropped on it. + * + * For a reference on the options for this function, check out + * http://wiki.script.aculo.us/scriptaculous/show/Droppables.add + * + * @param string $id + * @param array $options + * @param array $ajaxOptions + * @return string JavaScript block to create a droppable element + */ + function dropRemote($id, $options = array(), $ajaxOptions = array()) { + $callback = $this->remoteFunction($ajaxOptions); + $options['onDrop'] = "function(element, droppable, event) {{$callback}}"; + $optionsString = array('overlap', 'hoverclass'); + + if (!isset($options['accept']) || !is_array($options['accept'])) { + $optionsString[] = 'accept'; + } else if (isset($options['accept'])) { + $options['accept'] = $this->Javascript->object($options['accept']); + } + + $options = $this->_buildOptions( + $this->_optionsToString($options, $optionsString), + $this->dropOptions + ); + return $this->Javascript->codeBlock("Droppables.add('{$id}', {$options});"); + } +/** + * Makes a slider control. + * + * @param string $id DOM ID of slider handle + * @param string $trackId DOM ID of slider track + * @param array $options Array of options to control the slider + * @link http://github.com/madrobby/scriptaculous/wikis/slider + */ + function slider($id, $trackId, $options = array()) { + if (isset($options['var'])) { + $var = 'var ' . $options['var'] . ' = '; + unset($options['var']); + } else { + $var = 'var ' . $id . ' = '; + } + + $options = $this->_optionsToString($options, array( + 'axis', 'handleImage', 'handleDisabled' + )); + $callbacks = array('change', 'slide'); + + foreach ($callbacks as $callback) { + if (isset($options[$callback])) { + $call = $options[$callback]; + $options['on' . ucfirst($callback)] = "function(value) {{$call}}"; + unset($options[$callback]); + } + } + + if (isset($options['values']) && is_array($options['values'])) { + $options['values'] = $this->Javascript->object($options['values']); + } + + $options = $this->_buildOptions($options, $this->sliderOptions); + $script = "{$var}new Control.Slider('$id', '$trackId', $options);"; + return $this->Javascript->codeBlock($script); + } +/** + * Makes an Ajax In Place editor control. + * + * @param string $id DOM ID of input element + * @param string $url Postback URL of saved data + * @param array $options Array of options to control the editor, including ajaxOptions (see link). + * @link http://github.com/madrobby/scriptaculous/wikis/ajax-inplaceeditor + */ + function editor($id, $url, $options = array()) { + $url = $this->url($url); + $options['ajaxOptions'] = $this->__optionsForAjax($options); + + foreach ($this->ajaxOptions as $opt) { + if (isset($options[$opt])) { + unset($options[$opt]); + } + } + + if (isset($options['callback'])) { + $options['callback'] = 'function(form, value) {' . $options['callback'] . '}'; + } + + $type = 'InPlaceEditor'; + if (isset($options['collection']) && is_array($options['collection'])) { + $options['collection'] = $this->Javascript->object($options['collection']); + $type = 'InPlaceCollectionEditor'; + } + + $var = ''; + if (isset($options['var'])) { + $var = 'var ' . $options['var'] . ' = '; + unset($options['var']); + } + + $options = $this->_optionsToString($options, array( + 'okText', 'cancelText', 'savingText', 'formId', 'externalControl', 'highlightcolor', + 'highlightendcolor', 'savingClassName', 'formClassName', 'loadTextURL', 'loadingText', + 'clickToEditText', 'okControl', 'cancelControl' + )); + $options = $this->_buildOptions($options, $this->editorOptions); + $script = "{$var}new Ajax.{$type}('{$id}', '{$url}', {$options});"; + return $this->Javascript->codeBlock($script); + } +/** + * Makes a list or group of floated objects sortable. + * + * @param string $id DOM ID of parent + * @param array $options Array of options to control sort. + * @link http://github.com/madrobby/scriptaculous/wikis/sortable + */ + function sortable($id, $options = array()) { + if (!empty($options['url'])) { + if (empty($options['with'])) { + $options['with'] = "Sortable.serialize('$id')"; + } + $options['onUpdate'] = 'function(sortable) {' . $this->remoteFunction($options) . '}'; + } + $block = true; + + if (isset($options['block'])) { + $block = $options['block']; + unset($options['block']); + } + $strings = array( + 'tag', 'constraint', 'only', 'handle', 'hoverclass', 'tree', + 'treeTag', 'update', 'overlap' + ); + $scrollIsObject = ( + isset($options['scroll']) && + $options['scroll'] != 'window' && + strpos($options['scroll'], '$(') !== 0 + ); + + if ($scrollIsObject) { + $strings[] = 'scroll'; + } + + $options = $this->_optionsToString($options, $strings); + $options = array_merge($options, $this->_buildCallbacks($options)); + $options = $this->_buildOptions($options, $this->sortOptions); + $result = "Sortable.create('$id', $options);"; + + if (!$block) { + return $result; + } + return $this->Javascript->codeBlock($result); + } +/** + * Private helper function for Javascript. + * + * @param array $options Set of options + * @access private + */ + function __optionsForAjax($options) { + if (isset($options['indicator'])) { + if (isset($options['loading'])) { + $loading = $options['loading']; + + if (!empty($loading) && substr(trim($loading), -1, 1) != ';') { + $options['loading'] .= '; '; + } + $options['loading'] .= "Element.show('{$options['indicator']}');"; + } else { + $options['loading'] = "Element.show('{$options['indicator']}');"; + } + if (isset($options['complete'])) { + $complete = $options['complete']; + + if (!empty($complete) && substr(trim($complete), -1, 1) != ';') { + $options['complete'] .= '; '; + } + $options['complete'] .= "Element.hide('{$options['indicator']}');"; + } else { + $options['complete'] = "Element.hide('{$options['indicator']}');"; + } + unset($options['indicator']); + } + + $jsOptions = array_merge( + array('asynchronous' => 'true', 'evalScripts' => 'true'), + $this->_buildCallbacks($options) + ); + + $options = $this->_optionsToString($options, array( + 'contentType', 'encoding', 'fallback', 'method', 'postBody', 'update', 'url' + )); + $jsOptions = array_merge($jsOptions, array_intersect_key($options, array_flip(array( + 'contentType', 'encoding', 'method', 'postBody' + )))); + + foreach ($options as $key => $value) { + switch ($key) { + case 'type': + $jsOptions['asynchronous'] = ($value == 'synchronous') ? 'false' : 'true'; + break; + case 'evalScripts': + $jsOptions['evalScripts'] = ($value) ? 'true' : 'false'; + break; + case 'position': + $pos = Inflector::camelize($options['position']); + $jsOptions['insertion'] = "Insertion.{$pos}"; + break; + case 'with': + $jsOptions['parameters'] = $options['with']; + break; + case 'form': + $jsOptions['parameters'] = 'Form.serialize(this)'; + break; + case 'requestHeaders': + $keys = array(); + foreach ($value as $key => $val) { + $keys[] = "'" . $key . "'"; + $keys[] = "'" . $val . "'"; + } + $jsOptions['requestHeaders'] = '[' . join(', ', $keys) . ']'; + break; + } + } + return $this->_buildOptions($jsOptions, $this->ajaxOptions); + } +/** + * Private Method to return a string of html options + * option data as a JavaScript options hash. + * + * @param array $options Options in the shape of keys and values + * @param array $extra Array of legal keys in this options context + * @return array Array of html options + * @access private + */ + function __getHtmlOptions($options, $extra = array()) { + foreach (array_merge($this->ajaxOptions, $this->callbacks, $extra) as $key) { + if (isset($options[$key])) { + unset($options[$key]); + } + } + return $options; + } +/** + * Returns a string of JavaScript with the given option data as a JavaScript options hash. + * + * @param array $options Options in the shape of keys and values + * @param array $acceptable Array of legal keys in this options context + * @return string String of Javascript array definition + */ + function _buildOptions($options, $acceptable) { + if (is_array($options)) { + $out = array(); + + foreach ($options as $k => $v) { + if (in_array($k, $acceptable)) { + if ($v === true) { + $v = 'true'; + } elseif ($v === false) { + $v = 'false'; + } + $out[] = "$k:$v"; + } elseif ($k === 'with' && in_array('parameters', $acceptable)) { + $out[] = "parameters:${v}"; + } + } + + $out = join(', ', $out); + $out = '{' . $out . '}'; + return $out; + } else { + return false; + } + } +/** + * Return JavaScript text for an observer... + * + * @param string $klass Name of JavaScript class + * @param string $name + * @param array $options Ajax options + * @return string Formatted JavaScript + */ + function _buildObserver($klass, $name, $options = null) { + if (!isset($options['with']) && isset($options['update'])) { + $options['with'] = 'value'; + } + + $callback = $this->remoteFunction($options); + $hasFrequency = !(!isset($options['frequency']) || intval($options['frequency']) == 0); + $frequency = $hasFrequency ? $options['frequency'] . ', ' : ''; + + return "new $klass('$name', {$frequency}function(element, value) {{$callback}})"; + } +/** + * Return Javascript text for callbacks. + * + * @param array $options Option array where a callback is specified + * @return array Options with their callbacks properly set + * @access protected + */ + function _buildCallbacks($options) { + $callbacks = array(); + + foreach ($this->callbacks as $callback) { + if (isset($options[$callback])) { + $name = 'on' . ucfirst($callback); + $code = $options[$callback]; + switch ($name) { + case 'onComplete': + $callbacks[$name] = "function(request, json) {" . $code . "}"; + break; + case 'onCreate': + $callbacks[$name] = "function(request, xhr) {" . $code . "}"; + break; + case 'onException': + $callbacks[$name] = "function(request, exception) {" . $code . "}"; + break; + default: + $callbacks[$name] = "function(request) {" . $code . "}"; + break; + } + if (isset($options['bind'])) { + $bind = $options['bind']; + + $hasBinding = ( + (is_array($bind) && in_array($callback, $bind)) || + (is_string($bind) && strpos($bind, $callback) !== false) + ); + + if ($hasBinding) { + $callbacks[$name] .= ".bind(this)"; + } + } + } + } + return $callbacks; + } +/** + * Returns a string of JavaScript with a string representation of given options array. + * + * @param array $options Ajax options array + * @param array $stringOpts Options as strings in an array + * @access private + * @return array + */ + function _optionsToString($options, $stringOpts = array()) { + foreach ($stringOpts as $option) { + $hasOption = ( + isset($options[$option]) && !empty($options[$option]) && + is_string($options[$option]) && $options[$option][0] != "'" + ); + + if ($hasOption) { + if ($options[$option] === true || $options[$option] === 'true') { + $options[$option] = 'true'; + } elseif ($options[$option] === false || $options[$option] === 'false') { + $options[$option] = 'false'; + } else { + $options[$option] = "'{$options[$option]}'"; + } + } + } + return $options; + } +/** + * Executed after a view has rendered, used to include bufferred code + * blocks. + * + * @access public + */ + function afterRender() { + if (env('HTTP_X_UPDATE') != null && !empty($this->__ajaxBuffer)) { + @ob_end_clean(); + + $data = array(); + $divs = explode(' ', env('HTTP_X_UPDATE')); + $keys = array_keys($this->__ajaxBuffer); + + if (count($divs) == 1 && in_array($divs[0], $keys)) { + echo $this->__ajaxBuffer[$divs[0]]; + } else { + foreach ($this->__ajaxBuffer as $key => $val) { + if (in_array($key, $divs)) { + $data[] = $key . ':"' . rawurlencode($val) . '"'; + } + } + $out = 'var __ajaxUpdater__ = {' . join(", \n", $data) . '};' . "\n"; + $out .= 'for (n in __ajaxUpdater__) { if (typeof __ajaxUpdater__[n] == "string"'; + $out .= ' && $(n)) Element.update($(n), unescape(decodeURIComponent('; + $out .= '__ajaxUpdater__[n]))); }'; + echo $this->Javascript->codeBlock($out, false); + } + $scripts = $this->Javascript->getCache(); + + if (!empty($scripts)) { + echo $this->Javascript->codeBlock($scripts, false); + } + $this->_stop(); + } + } +} + +?> \ No newline at end of file diff --git a/cake/libs/view/helpers/app_helper.php b/cake/libs/view/helpers/app_helper.php new file mode 100755 index 0000000..c57f438 --- /dev/null +++ b/cake/libs/view/helpers/app_helper.php @@ -0,0 +1,41 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Short description for file. + * + * 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://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake + * @since CakePHP(tm) v 0.2.9 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +App::import('Core', 'Helper'); +/** + * 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 { +} +?> \ No newline at end of file diff --git a/cake/libs/view/helpers/cache.php b/cake/libs/view/helpers/cache.php new file mode 100755 index 0000000..d8e60b1 --- /dev/null +++ b/cake/libs/view/helpers/cache.php @@ -0,0 +1,293 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Short description for file. + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.helpers + * @since CakePHP(tm) v 1.0.0.2277 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Short description for file. + * + * Long description for file + * + * @package cake + * @subpackage cake.cake.libs.view.helpers + */ +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(); +/** + * holds the View object passed in final call to CacheHelper::cache() + * + * @var View + * @access public + */ + var $view; +/** + * 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 + * @return view ouput + */ + function cache($file, $out, $cache = false) { + $cacheTime = 0; + $useCallbacks = false; + if (is_array($this->cacheAction)) { + $contoller = Inflector::underscore($this->controllerName); + $check = str_replace('/', '_', $this->here); + $replace = str_replace('/', '_', $this->base); + $match = str_replace($this->base, '', $this->here); + $match = str_replace('//', '/', $match); + $match = str_replace('/' . $contoller . '/', '', $match); + $match = str_replace('/' . $this->controllerName . '/', '', $match); + $check = str_replace($replace, '', $check); + $check = str_replace('_' . $contoller . '_', '', $check); + $check = str_replace('_' . $this->controllerName . '_', '', $check); + $check = Inflector::slug($check); + $check = preg_replace('/^_+/', '', $check); + $keys = str_replace('/', '_', array_keys($this->cacheAction)); + $found = array_keys($this->cacheAction); + $index = null; + $count = 0; + + foreach ($keys as $key => $value) { + if (strpos($check, $value) === 0) { + $index = $found[$count]; + break; + } + $count++; + } + + if (isset($index)) { + $pos1 = strrpos($match, '/'); + $char = strlen($match) - 1; + + if ($pos1 == $char) { + $match = substr($match, 0, $char); + } + + $key = $match; + } elseif ($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 (array_key_exists('duration', $options)) { + $cacheTime = $options['duration']; + } + if (array_key_exists('callbacks', $options)) { + $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 + * @param boolean $cache + * @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, $oresult, PREG_PATTERN_ORDER); + preg_match_all('/(?<=<cake:nocache>)([\\s\\S]*?)(?=<\/cake:nocache>)/i', $file, $result, PREG_PATTERN_ORDER); + + if (!empty($this->__replace)) { + foreach ($oresult['0'] as $k => $element) { + $index = array_search($element, $this->__match); + if ($index !== false) { + array_splice($oresult[0], $k, 1); + } + } + } + + if (!empty($result['0'])) { + $count = 0; + foreach ($result['0'] as $block) { + if (isset($oresult['0'][$count])) { + $this->__replace[] = $block; + $this->__match[] = $oresult['0'][$count]; + } + $count++; + } + } + } +/** + * Parse the output and replace cache tags + * + * @param sting $cache + * @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 $file + * @param sting $timestamp + * @return cached 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->namedArgs = $this->namedArgs = \'' . $this->namedArgs . '\'; + $controller->argSeparator = $this->argSeparator = \'' . $this->argSeparator . '\'; + $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->themeWeb = $this->themeWeb = \'' . $this->themeWeb . '\'; + 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}; + } + ?>'; + $content = preg_replace("/(<\\?xml)/", "<?php echo '$1';?>",$content); + $file .= $content; + return cache('views' . DS . $cache, $file, $timestamp); + } +} + +?> \ No newline at end of file diff --git a/cake/libs/view/helpers/form.php b/cake/libs/view/helpers/form.php new file mode 100755 index 0000000..0dcc1c1 --- /dev/null +++ b/cake/libs/view/helpers/form.php @@ -0,0 +1,1881 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Automatic generation of HTML FORMs from given data. + * + * Used for scaffolding. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.helpers + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Form helper library. + * + * Automatic generation of HTML FORMs from given data. + * + * @package cake + * @subpackage cake.cake.libs.view.helpers + */ +class FormHelper extends AppHelper { +/** + * Other helpers used by FormHelper + * + * @var array + * @access public + */ + var $helpers = array('Html'); +/** + * Holds the fields array('field_name' => array('type'=> 'string', 'length'=> 100), + * primaryKey and validates array('field_name') + * + * @access public + */ + var $fieldset = array('fields' => array(), 'key' => 'id', 'validates' => array()); +/** + * Options used by DateTime fields + * + * @var array + */ + var $__options = array( + 'day' => array(), 'minute' => array(), 'hour' => array(), + 'month' => array(), 'year' => array(), 'meridian' => array() + ); +/** + * List of fields created, used with secure forms. + * + * @var array + * @access public + */ + var $fields = array(); +/** + * Defines the type of form being created. Set by FormHelper::create(). + * + * @var string + * @access public + */ + var $requestType = null; +/** + * Returns an HTML FORM element. + * + * Options: + * + * - 'type' Form method defaults to POST + * - 'action' The Action the form submits to. Can be a string or array, + * - 'url' The url the form submits to. Can be a string or a url array, + * - 'default' Allows for the creation of Ajax forms. + * - 'onsubmit' Used in conjunction with 'default' to create ajax forms. + * + * @access public + * @param string $model The model object which the form is being defined for + * @param array $options An array of html attributes and options. + * @return string An formatted opening FORM tag. + */ + function create($model = null, $options = array()) { + $defaultModel = null; + $view =& ClassRegistry::getObject('view'); + + if (is_array($model) && empty($options)) { + $options = $model; + $model = null; + } + + if (empty($model) && $model !== false && !empty($this->params['models'])) { + $model = $this->params['models'][0]; + $defaultModel = $this->params['models'][0]; + } elseif (empty($model) && empty($this->params['models'])) { + $model = false; + } elseif (is_string($model) && strpos($model, '.') !== false) { + $path = explode('.', $model); + $model = $path[count($path) - 1]; + } + + if (ClassRegistry::isKeySet($model)) { + $object =& ClassRegistry::getObject($model); + } + + $models = ClassRegistry::keys(); + foreach ($models as $currentModel) { + if (ClassRegistry::isKeySet($currentModel)) { + $currentObject =& ClassRegistry::getObject($currentModel); + if (is_a($currentObject, 'Model') && !empty($currentObject->validationErrors)) { + $this->validationErrors[Inflector::camelize($currentModel)] =& $currentObject->validationErrors; + } + } + } + + $this->setEntity($model . '.', true); + $append = ''; + $created = $id = false; + + if (isset($object)) { + $fields = $object->schema(); + foreach ($fields as $key => $value) { + unset($fields[$key]); + $fields[$model . '.' . $key] = $value; + } + + if (!empty($object->hasAndBelongsToMany)) { + foreach ($object->hasAndBelongsToMany as $alias => $assocData) { + $fields[$alias] = array('type' => 'multiple'); + } + } + $validates = array(); + if (!empty($object->validate)) { + foreach ($object->validate as $validateField => $validateProperties) { + if (is_array($validateProperties)) { + $dims = Set::countDim($validateProperties); + if (($dims == 1 && !isset($validateProperties['required']) || (array_key_exists('required', $validateProperties) && $validateProperties['required'] !== false))) { + $validates[] = $validateField; + } elseif ($dims > 1) { + foreach ($validateProperties as $rule => $validateProp) { + if (is_array($validateProp) && (array_key_exists('required', $validateProp) && $validateProp['required'] !== false)) { + $validates[] = $validateField; + } + } + } + } + } + } + $key = $object->primaryKey; + $this->fieldset = compact('fields', 'key', 'validates'); + } + + $data = $this->fieldset; + $recordExists = ( + isset($this->data[$model]) && + isset($this->data[$model][$data['key']]) && + !empty($this->data[$model][$data['key']]) + ); + + if ($recordExists) { + $created = true; + $id = $this->data[$model][$data['key']]; + } + $options = array_merge(array( + 'type' => ($created && empty($options['action'])) ? 'put' : 'post', + 'action' => null, + 'url' => null, + 'default' => true), + $options); + + if (empty($options['url']) || is_array($options['url'])) { + if (empty($options['url']['controller'])) { + if (!empty($model) && $model != $defaultModel) { + $options['url']['controller'] = Inflector::underscore(Inflector::pluralize($model)); + } elseif (!empty($this->params['controller'])) { + $options['url']['controller'] = Inflector::underscore($this->params['controller']); + } + } + if (empty($options['action'])) { + $options['action'] = ($created) ? 'edit' : 'add'; + } + + $actionDefaults = array( + 'plugin' => $this->plugin, + 'controller' => $view->viewPath, + 'action' => $options['action'], + 'id' => $id + ); + if (!empty($options['action']) && !isset($options['id'])) { + $options['id'] = $model . Inflector::camelize($options['action']) . 'Form'; + } + $options['action'] = array_merge($actionDefaults, (array)$options['url']); + } elseif (is_string($options['url'])) { + $options['action'] = $options['url']; + } + unset($options['url']); + + switch (strtolower($options['type'])) { + case 'get': + $htmlAttributes['method'] = 'get'; + break; + case 'file': + $htmlAttributes['enctype'] = 'multipart/form-data'; + $options['type'] = ($created) ? 'put' : 'post'; + case 'post': + case 'put': + case 'delete': + $append .= $this->hidden('_method', array( + 'name' => '_method', 'value' => strtoupper($options['type']), 'id' => null + )); + default: + $htmlAttributes['method'] = 'post'; + break; + } + $this->requestType = strtolower($options['type']); + + $htmlAttributes['action'] = $this->url($options['action']); + unset($options['type'], $options['action']); + + if ($options['default'] == false) { + if (isset($htmlAttributes['onSubmit']) || isset($htmlAttributes['onsubmit'])) { + $htmlAttributes['onsubmit'] .= ' event.returnValue = false; return false;'; + } else { + $htmlAttributes['onsubmit'] = 'event.returnValue = false; return false;'; + } + } + unset($options['default']); + $htmlAttributes = array_merge($options, $htmlAttributes); + + if (isset($this->params['_Token']) && !empty($this->params['_Token'])) { + $append .= $this->hidden('_Token.key', array( + 'value' => $this->params['_Token']['key'], 'id' => 'Token' . mt_rand()) + ); + } + + if (!empty($append)) { + $append = sprintf($this->Html->tags['fieldset'], ' style="display:none;"', $append); + } + + $this->setEntity($model . '.', true); + $attributes = $this->_parseAttributes($htmlAttributes, null, ''); + return $this->output(sprintf($this->Html->tags['form'], $attributes)) . $append; + } +/** + * Closes an HTML form, cleans up values set by FormHelper::create(), and writes hidden + * input fields where appropriate. + * + * If $options is set a form submit button will be created. + * + * @param mixed $options as a string will use $options as the value of button, + * array usage: + * array('label' => 'save'); value="save" + * array('label' => 'save', 'name' => 'Whatever'); value="save" name="Whatever" + * array('name' => 'Whatever'); value="Submit" name="Whatever" + * array('label' => 'save', 'name' => 'Whatever', 'div' => 'good') <div class="good"> value="save" name="Whatever" + * array('label' => 'save', 'name' => 'Whatever', 'div' => array('class' => 'good')); <div class="good"> value="save" name="Whatever" + * + * @return string a closing FORM tag optional submit button. + * @access public + */ + function end($options = null) { + if (!empty($this->params['models'])) { + $models = $this->params['models'][0]; + } + $out = null; + $submit = null; + + if ($options !== null) { + $submitOptions = array(); + if (is_string($options)) { + $submit = $options; + } else { + if (isset($options['label'])) { + $submit = $options['label']; + unset($options['label']); + } + $submitOptions = $options; + + if (!$submit) { + $submit = __('Submit', true); + } + } + $out .= $this->submit($submit, $submitOptions); + } + if (isset($this->params['_Token']) && !empty($this->params['_Token'])) { + $out .= $this->secure($this->fields); + $this->fields = array(); + } + $this->setEntity(null); + $out .= $this->Html->tags['formend']; + + $view =& ClassRegistry::getObject('view'); + $view->modelScope = false; + return $this->output($out); + } +/** + * Generates a hidden field with a security hash based on the fields used in the form. + * + * @param array $fields The list of fields to use when generating the hash + * @return string A hidden input field with a security hash + * @access public + */ + function secure($fields = array()) { + if (!isset($this->params['_Token']) || empty($this->params['_Token'])) { + return; + } + $out = '<fieldset style="display:none;">'; + $locked = array(); + + foreach ($fields as $key => $value) { + if (!is_int($key)) { + $locked[$key] = $value; + unset($fields[$key]); + } + } + sort($fields, SORT_STRING); + ksort($locked, SORT_STRING); + $fields += $locked; + + $fields = Security::hash(serialize($fields) . Configure::read('Security.salt')); + $locked = str_rot13(serialize(array_keys($locked))); + + $out .= $this->hidden('_Token.fields', array( + 'value' => urlencode($fields . ':' . $locked), + 'id' => 'TokenFields' . mt_rand() + )); + return $out .= '</fieldset>'; + } +/** + * Determine which fields of a form should be used for hash + * + * @param mixed $field Reference to field to be secured + * @param mixed $value Field value, if value should not be tampered with + * @access private + */ + function __secure($field = null, $value = null) { + if (!$field) { + $view =& ClassRegistry::getObject('view'); + $field = $view->entity(); + } elseif (is_string($field)) { + $field = Set::filter(explode('.', $field), true); + } + + if (!empty($this->params['_Token']['disabledFields'])) { + foreach ((array)$this->params['_Token']['disabledFields'] as $disabled) { + $disabled = explode('.', $disabled); + if (array_values(array_intersect($field, $disabled)) === $disabled) { + return; + } + } + } + $field = join('.', $field); + if (!in_array($field, $this->fields)) { + if ($value !== null) { + return $this->fields[$field] = $value; + } + $this->fields[] = $field; + } + } +/** + * Returns true if there is an error for the given field, otherwise false + * + * @param string $field This should be "Modelname.fieldname" + * @return boolean If there are errors this method returns true, else false. + * @access public + */ + function isFieldError($field) { + $this->setEntity($field); + return (bool)$this->tagIsInvalid(); + } +/** + * Returns a formatted error message for given FORM field, NULL if no errors. + * + * Options: + * + * - 'escape' bool Whether or not to html escape the contents of the error. + * - 'wrap' mixed Whether or not the error message should be wrapped in a div. If a + * string, will be used as the HTML tag to use. + * - 'class' string The classname for the error message + * + * @param string $field A field name, like "Modelname.fieldname" + * @param mixed $text Error message or array of $options + * @param array $options Rendering options for <div /> wrapper tag + * @return string If there are errors this method returns an error message, otherwise null. + * @access public + */ + function error($field, $text = null, $options = array()) { + $defaults = array('wrap' => true, 'class' => 'error-message', 'escape' => true); + $options = array_merge($defaults, $options); + $this->setEntity($field); + + if ($error = $this->tagIsInvalid()) { + if (is_array($error)) { + list(,,$field) = explode('.', $field); + if (isset($error[$field])) { + $error = $error[$field]; + } else { + return null; + } + } + + if (is_array($text) && is_numeric($error) && $error > 0) { + $error--; + } + if (is_array($text) && isset($text[$error])) { + $text = $text[$error]; + } elseif (is_array($text)) { + $options = array_merge($options, $text); + $text = null; + } + + if ($text != null) { + $error = $text; + } elseif (is_numeric($error)) { + $error = sprintf(__('Error in field %s', true), Inflector::humanize($this->field())); + } + if ($options['escape']) { + $error = h($error); + unset($options['escape']); + } + if ($options['wrap']) { + $tag = is_string($options['wrap']) ? $options['wrap'] : 'div'; + unset($options['wrap']); + return $this->Html->tag($tag, $error, $options); + } else { + return $error; + } + } else { + return null; + } + } +/** + * Returns a formatted LABEL element for HTML FORMs. + * + * @param string $fieldName This should be "Modelname.fieldname" + * @param string $text Text that will appear in the label field. + * @param array $attributes Array of HTML attributes. + * @return string The formatted LABEL element + */ + function label($fieldName = null, $text = null, $attributes = array()) { + if (empty($fieldName)) { + $view = ClassRegistry::getObject('view'); + $fieldName = implode('.', $view->entity()); + } + + if ($text === null) { + if (strpos($fieldName, '.') !== false) { + $text = array_pop(explode('.', $fieldName)); + } else { + $text = $fieldName; + } + if (substr($text, -3) == '_id') { + $text = substr($text, 0, strlen($text) - 3); + } + $text = __(Inflector::humanize(Inflector::underscore($text)), true); + } + + if (isset($attributes['for'])) { + $labelFor = $attributes['for']; + unset($attributes['for']); + } else { + $labelFor = $this->domId($fieldName); + } + + return $this->output(sprintf( + $this->Html->tags['label'], + $labelFor, + $this->_parseAttributes($attributes), $text + )); + } +/** + * Will display all the fields passed in an array expects fieldName as an array key + * replaces generateFields + * + * @access public + * @param array $fields works well with Controller::generateFields() or on its own; + * @param array $blacklist a simple array of fields to skip + * @return output + */ + function inputs($fields = null, $blacklist = null) { + $fieldset = $legend = true; + + if (is_array($fields)) { + if (array_key_exists('legend', $fields)) { + $legend = $fields['legend']; + unset($fields['legend']); + } + + if (isset($fields['fieldset'])) { + $fieldset = $fields['fieldset']; + unset($fields['fieldset']); + } + } elseif ($fields !== null) { + $fieldset = $legend = $fields; + if (!is_bool($fieldset)) { + $fieldset = true; + } + $fields = array(); + } + + if (empty($fields)) { + $fields = array_keys($this->fieldset['fields']); + } + + if ($legend === true) { + $actionName = __('New', true); + $isEdit = ( + strpos($this->action, 'update') !== false || + strpos($this->action, 'edit') !== false + ); + if ($isEdit) { + $actionName = __('Edit', true); + } + $modelName = Inflector::humanize(Inflector::underscore($this->model())); + $legend = $actionName .' '. __($modelName, true); + } + + $out = null; + foreach ($fields as $name => $options) { + if (is_numeric($name) && !is_array($options)) { + $name = $options; + $options = array(); + } + $entity = explode('.', $name); + $blacklisted = ( + is_array($blacklist) && + (in_array($name, $blacklist) || in_array(end($entity), $blacklist)) + ); + if ($blacklisted) { + continue; + } + $out .= $this->input($name, $options); + } + + if (is_string($fieldset)) { + $fieldsetClass = sprintf(' class="%s"', $fieldset); + } else { + $fieldsetClass = ''; + } + + if ($fieldset && $legend) { + return sprintf( + $this->Html->tags['fieldset'], + $fieldsetClass, + sprintf($this->Html->tags['legend'], $legend) . $out + ); + } elseif ($fieldset) { + return sprintf($this->Html->tags['fieldset'], $fieldsetClass, $out); + } else { + return $out; + } + } +/** + * Generates a form input element complete with label and wrapper div + * + * Options - See each field type method for more information. Any options that are part of + * $attributes or $options for the different type methods can be included in $options for input(). + * + * - 'type' - Force the type of widget you want. e.g. ```type => 'select'``` + * - 'label' - control the label + * - 'div' - control the wrapping div element + * - 'options' - for widgets that take options e.g. radio, select + * - 'error' - control the error message that is produced + * + * @param string $fieldName This should be "Modelname.fieldname" + * @param array $options Each type of input takes different options. + * @return string Completed form widget + */ + function input($fieldName, $options = array()) { + $view =& ClassRegistry::getObject('view'); + $this->setEntity($fieldName); + $entity = join('.', $view->entity()); + + $defaults = array('before' => null, 'between' => null, 'after' => null); + $options = array_merge($defaults, $options); + + if (!isset($options['type'])) { + $options['type'] = 'text'; + + if (isset($options['options'])) { + $options['type'] = 'select'; + } elseif (in_array($this->field(), array('psword', 'passwd', 'password'))) { + $options['type'] = 'password'; + } elseif (isset($this->fieldset['fields'][$entity])) { + $fieldDef = $this->fieldset['fields'][$entity]; + $type = $fieldDef['type']; + $primaryKey = $this->fieldset['key']; + } elseif (ClassRegistry::isKeySet($this->model())) { + $model =& ClassRegistry::getObject($this->model()); + $type = $model->getColumnType($this->field()); + $fieldDef = $model->schema(); + + if (isset($fieldDef[$this->field()])) { + $fieldDef = $fieldDef[$this->field()]; + } else { + $fieldDef = array(); + } + $primaryKey = $model->primaryKey; + } + + if (isset($type)) { + $map = array( + 'string' => 'text', 'datetime' => 'datetime', + 'boolean' => 'checkbox', 'timestamp' => 'datetime', + 'text' => 'textarea', 'time' => 'time', + 'date' => 'date', 'float' => 'text' + ); + + if (isset($this->map[$type])) { + $options['type'] = $this->map[$type]; + } elseif (isset($map[$type])) { + $options['type'] = $map[$type]; + } + if ($this->field() == $primaryKey) { + $options['type'] = 'hidden'; + } + } + + if ($this->model() === $this->field()) { + $options['type'] = 'select'; + if (!isset($options['multiple'])) { + $options['multiple'] = 'multiple'; + } + } + } + $types = array('text', 'checkbox', 'radio', 'select'); + + if (!isset($options['options']) && in_array($options['type'], $types)) { + $view =& ClassRegistry::getObject('view'); + $varName = Inflector::variable( + Inflector::pluralize(preg_replace('/_id$/', '', $this->field())) + ); + $varOptions = $view->getVar($varName); + if (is_array($varOptions)) { + if ($options['type'] !== 'radio') { + $options['type'] = 'select'; + } + $options['options'] = $varOptions; + } + } + + $autoLength = (!array_key_exists('maxlength', $options) && isset($fieldDef['length'])); + if ($autoLength && $options['type'] == 'text') { + $options['maxlength'] = $fieldDef['length']; + } + if ($autoLength && $fieldDef['type'] == 'float') { + $options['maxlength'] = array_sum(explode(',', $fieldDef['length']))+1; + } + + $out = ''; + $div = true; + $divOptions = array(); + + if (array_key_exists('div', $options)) { + $div = $options['div']; + unset($options['div']); + } + + if (!empty($div)) { + $divOptions['class'] = 'input'; + $divOptions = $this->addClass($divOptions, $options['type']); + if (is_string($div)) { + $divOptions['class'] = $div; + } elseif (is_array($div)) { + $divOptions = array_merge($divOptions, $div); + } + if (in_array($this->field(), $this->fieldset['validates'])) { + $divOptions = $this->addClass($divOptions, 'required'); + } + if (!isset($divOptions['tag'])) { + $divOptions['tag'] = 'div'; + } + } + + $label = null; + if (isset($options['label']) && $options['type'] !== 'radio') { + $label = $options['label']; + unset($options['label']); + } + + if ($options['type'] === 'radio') { + $label = false; + if (isset($options['options'])) { + if (is_array($options['options'])) { + $radioOptions = $options['options']; + } else { + $radioOptions = array($options['options']); + } + unset($options['options']); + } + } + + if ($label !== false) { + $labelAttributes = $this->domId(array(), 'for'); + if (in_array($options['type'], array('date', 'datetime'))) { + $labelAttributes['for'] .= 'Month'; + } else if ($options['type'] === 'time') { + $labelAttributes['for'] .= 'Hour'; + } + + if (is_array($label)) { + $labelText = null; + if (isset($label['text'])) { + $labelText = $label['text']; + unset($label['text']); + } + $labelAttributes = array_merge($labelAttributes, $label); + } else { + $labelText = $label; + } + + if (isset($options['id'])) { + $labelAttributes = array_merge($labelAttributes, array('for' => $options['id'])); + } + $out = $this->label($fieldName, $labelText, $labelAttributes); + } + + $error = null; + if (isset($options['error'])) { + $error = $options['error']; + unset($options['error']); + } + + $selected = null; + if (array_key_exists('selected', $options)) { + $selected = $options['selected']; + unset($options['selected']); + } + if (isset($options['rows']) || isset($options['cols'])) { + $options['type'] = 'textarea'; + } + + $empty = false; + if (isset($options['empty'])) { + $empty = $options['empty']; + unset($options['empty']); + } + + $timeFormat = 12; + if (isset($options['timeFormat'])) { + $timeFormat = $options['timeFormat']; + unset($options['timeFormat']); + } + + $dateFormat = 'MDY'; + if (isset($options['dateFormat'])) { + $dateFormat = $options['dateFormat']; + unset($options['dateFormat']); + } + + $type = $options['type']; + $before = $options['before']; + $between = $options['between']; + $after = $options['after']; + unset($options['type'], $options['before'], $options['between'], $options['after']); + + switch ($type) { + case 'hidden': + $out = $this->hidden($fieldName, $options); + unset($divOptions); + break; + case 'checkbox': + $out = $before . $this->checkbox($fieldName, $options) . $between . $out; + break; + case 'radio': + $out = $before . $out . $this->radio($fieldName, $radioOptions, $options) . $between; + break; + case 'text': + case 'password': + $out = $before . $out . $between . $this->{$type}($fieldName, $options); + break; + case 'file': + $out = $before . $out . $between . $this->file($fieldName, $options); + break; + case 'select': + $options = array_merge(array('options' => array()), $options); + $list = $options['options']; + unset($options['options']); + $out = $before . $out . $between . $this->select( + $fieldName, $list, $selected, $options, $empty + ); + break; + case 'time': + $out = $before . $out . $between . $this->dateTime( + $fieldName, null, $timeFormat, $selected, $options, $empty + ); + break; + case 'date': + $out = $before . $out . $between . $this->dateTime( + $fieldName, $dateFormat, null, $selected, $options, $empty + ); + break; + case 'datetime': + $out = $before . $out . $between . $this->dateTime( + $fieldName, $dateFormat, $timeFormat, $selected, $options, $empty + ); + break; + case 'textarea': + default: + $out = $before . $out . $between . $this->textarea($fieldName, array_merge( + array('cols' => '30', 'rows' => '6'), $options + )); + break; + } + + if ($type != 'hidden') { + $out .= $after; + if ($error !== false) { + $errMsg = $this->error($fieldName, $error); + if ($errMsg) { + $out .= $errMsg; + $divOptions = $this->addClass($divOptions, 'error'); + } + } + } + if (isset($divOptions) && isset($divOptions['tag'])) { + $tag = $divOptions['tag']; + unset($divOptions['tag']); + $out = $this->Html->tag($tag, $out, $divOptions); + } + return $out; + } +/** + * Creates a checkbox input widget. + * + * Options: + * + * - 'value' - the value of the checkbox + * - checked' - boolean indicate that this checkbox is checked. + * + * @param string $fieldName Name of a field, like this "Modelname.fieldname" + * @param array $options Array of HTML attributes. + * @todo Right now, automatically setting the 'checked' value is dependent on whether or not the + * checkbox is bound to a model. This should probably be re-evaluated in future versions. + * @return string An HTML text input element + */ + function checkbox($fieldName, $options = array()) { + $options = $this->_initInputField($fieldName, $options); + $value = current($this->value()); + + if (!isset($options['value']) || empty($options['value'])) { + $options['value'] = 1; + } elseif (!empty($value) && $value === $options['value']) { + $options['checked'] = 'checked'; + } + $hiddenOptions = array( + 'id' => $options['id'] . '_', 'name' => $options['name'], + 'value' => '0', 'secure' => false + ); + if (isset($options['disabled']) && $options['disabled'] == true) { + $hiddenOptions['disabled'] = 'disabled'; + } + $output = $this->hidden($fieldName, $hiddenOptions); + + return $this->output($output . sprintf( + $this->Html->tags['checkbox'], + $options['name'], + $this->_parseAttributes($options, array('name'), null, ' ') + )); + } +/** + * Creates a set of radio widgets. + * + * Attributes: + * + * - 'separator' - define the string in between the radio buttons + * - 'legend' - control whether or not the widget set has a fieldset & legend + * - 'value' - indicate a value that is should be checked + * - 'label' - boolean to indicate whether or not labels for widgets show be displayed + * + * @param string $fieldName Name of a field, like this "Modelname.fieldname" + * @param array $options Radio button options array. + * @param array $attributes Array of HTML attributes. + * @return string + */ + function radio($fieldName, $options = array(), $attributes = array()) { + $attributes = $this->_initInputField($fieldName, $attributes); + $legend = false; + + if (isset($attributes['legend'])) { + $legend = $attributes['legend']; + unset($attributes['legend']); + } elseif (count($options) > 1) { + $legend = __(Inflector::humanize($this->field()), true); + } + $label = true; + + if (isset($attributes['label'])) { + $label = $attributes['label']; + unset($attributes['label']); + } + $inbetween = null; + + if (isset($attributes['separator'])) { + $inbetween = $attributes['separator']; + unset($attributes['separator']); + } + + if (isset($attributes['value'])) { + $value = $attributes['value']; + } else { + $value = $this->value($fieldName); + } + $out = array(); + + foreach ($options as $optValue => $optTitle) { + $optionsHere = array('value' => $optValue); + + if (isset($value) && $optValue == $value) { + $optionsHere['checked'] = 'checked'; + } + $parsedOptions = $this->_parseAttributes( + array_merge($attributes, $optionsHere), + array('name', 'type', 'id'), '', ' ' + ); + $tagName = Inflector::camelize( + $attributes['id'] . '_' . Inflector::underscore($optValue) + ); + + if ($label) { + $optTitle = sprintf($this->Html->tags['label'], $tagName, null, $optTitle); + } + $out[] = sprintf( + $this->Html->tags['radio'], $attributes['name'], + $tagName, $parsedOptions, $optTitle + ); + } + $hidden = null; + + if (!isset($value) || $value === '') { + $hidden = $this->hidden($fieldName, array( + 'id' => $attributes['id'] . '_', 'value' => '', 'name' => $attributes['name'] + )); + } + $out = $hidden . join($inbetween, $out); + + if ($legend) { + $out = sprintf( + $this->Html->tags['fieldset'], '', + sprintf($this->Html->tags['legend'], $legend) . $out + ); + } + return $this->output($out); + } +/** + * Creates a text input widget. + * + * @param string $fieldName Name of a field, in the form "Modelname.fieldname" + * @param array $options Array of HTML attributes. + * @return string An HTML text input element + */ + function text($fieldName, $options = array()) { + $options = $this->_initInputField($fieldName, array_merge( + array('type' => 'text'), $options + )); + return $this->output(sprintf( + $this->Html->tags['input'], + $options['name'], + $this->_parseAttributes($options, array('name'), null, ' ') + )); + } +/** + * Creates a password input widget. + * + * @param string $fieldName Name of a field, like in the form "Modelname.fieldname" + * @param array $options Array of HTML attributes. + * @return string + */ + function password($fieldName, $options = array()) { + $options = $this->_initInputField($fieldName, $options); + return $this->output(sprintf( + $this->Html->tags['password'], + $options['name'], + $this->_parseAttributes($options, array('name'), null, ' ') + )); + } +/** + * Creates a textarea widget. + * + * @param string $fieldName Name of a field, in the form "Modelname.fieldname" + * @param array $options Array of HTML attributes. + * @return string An HTML text input element + */ + function textarea($fieldName, $options = array()) { + $options = $this->_initInputField($fieldName, $options); + $value = null; + + if (array_key_exists('value', $options)) { + $value = $options['value']; + if (!array_key_exists('escape', $options) || $options['escape'] !== false) { + $value = h($value); + } + unset($options['value']); + } + return $this->output(sprintf( + $this->Html->tags['textarea'], + $options['name'], + $this->_parseAttributes($options, array('type', 'name'), null, ' '), + $value + )); + } +/** + * Creates a hidden input field. + * + * @param string $fieldName Name of a field, in the form"Modelname.fieldname" + * @param array $options Array of HTML attributes. + * @return string + * @access public + */ + function hidden($fieldName, $options = array()) { + $secure = true; + + if (isset($options['secure'])) { + $secure = $options['secure']; + unset($options['secure']); + } + $options = $this->_initInputField($fieldName, array_merge( + $options, array('secure' => false) + )); + $model = $this->model(); + + if ($fieldName !== '_method' && $model !== '_Token' && $secure) { + $this->__secure(null, '' . $options['value']); + } + + return $this->output(sprintf( + $this->Html->tags['hidden'], + $options['name'], + $this->_parseAttributes($options, array('name', 'class'), '', ' ') + )); + } +/** + * Creates file input widget. + * + * @param string $fieldName Name of a field, in the form "Modelname.fieldname" + * @param array $options Array of HTML attributes. + * @return string + * @access public + */ + function file($fieldName, $options = array()) { + $options = array_merge($options, array('secure' => false)); + $options = $this->_initInputField($fieldName, $options); + $view =& ClassRegistry::getObject('view'); + $field = $view->entity(); + + foreach (array('name', 'type', 'tmp_name', 'error', 'size') as $suffix) { + $this->__secure(array_merge($field, array($suffix))); + } + + $attributes = $this->_parseAttributes($options, array('name'), '', ' '); + return $this->output(sprintf($this->Html->tags['file'], $options['name'], $attributes)); + } +/** + * Creates a button tag. + * + * @param string $title The button's caption + * @param array $options Array of options. + * @return string A HTML button tag. + * @access public + */ + function button($title, $options = array()) { + $options = array_merge(array('type' => 'button', 'value' => $title), $options); + + if (isset($options['name']) && strpos($options['name'], '.') !== false) { + if ($this->value($options['name'])) { + $options['checked'] = 'checked'; + } + $name = $options['name']; + unset($options['name']); + $options = $this->_initInputField($name, $options); + } + return $this->output(sprintf( + $this->Html->tags['button'], + $options['type'], + $this->_parseAttributes($options, array('type'), '', ' ') + )); + } +/** + * Creates a submit button element. + * + * @param string $caption The label appearing on the button OR if string contains :// or the + * extension .jpg, .jpe, .jpeg, .gif, .png use an image if the extension + * exists, AND the first character is /, image is relative to webroot, + * OR if the first character is not /, image is relative to webroot/img. + * @param array $options + * @return string A HTML submit button + */ + function submit($caption = null, $options = array()) { + if (!$caption) { + $caption = __('Submit', true); + } + $out = null; + $div = true; + + if (isset($options['div'])) { + $div = $options['div']; + unset($options['div']); + } + $divOptions = array('tag' => 'div'); + + if ($div === true) { + $divOptions['class'] = 'submit'; + } elseif ($div === false) { + unset($divOptions); + } elseif (is_string($div)) { + $divOptions['class'] = $div; + } elseif (is_array($div)) { + $divOptions = array_merge(array('class' => 'submit', 'tag' => 'div'), $div); + } + + if (strpos($caption, '://') !== false) { + $out .= $this->output(sprintf( + $this->Html->tags['submitimage'], + $caption, + $this->_parseAttributes($options, null, '', ' ') + )); + } elseif (preg_match('/\.(jpg|jpe|jpeg|gif|png|ico)$/', $caption)) { + if ($caption{0} !== '/') { + $url = $this->webroot(IMAGES_URL . $caption); + } else { + $caption = trim($caption, '/'); + $url = $this->webroot($caption); + } + $out .= $this->output(sprintf( + $this->Html->tags['submitimage'], + $url, + $this->_parseAttributes($options, null, '', ' ') + )); + } else { + $options['value'] = $caption; + $out .= $this->output(sprintf( + $this->Html->tags['submit'], + $this->_parseAttributes($options, null, '', ' ') + )); + } + + if (isset($divOptions)) { + $tag = $divOptions['tag']; + unset($divOptions['tag']); + $out = $this->Html->tag($tag, $out, $divOptions); + } + return $out; + } +/** + * Returns a formatted SELECT element. + * + * Attributes: + * + * - 'showParents' - If included in the array and set to true, an additional option element + * will be added for the parent of each option group. + * - 'multiple' - show a multiple select box. If set to 'checkbox' multiple checkboxes will be + * created instead. + * + * @param string $fieldName Name attribute of the SELECT + * @param array $options Array of the OPTION elements (as 'value'=>'Text' pairs) to be used in the + * SELECT element + * @param mixed $selected The option selected by default. If null, the default value + * from POST data will be used when available. + * @param array $attributes The HTML attributes of the select element. + * @param mixed $showEmpty If true, the empty select option is shown. If a string, + * that string is displayed as the empty element. + * @return string Formatted SELECT element + */ + function select($fieldName, $options = array(), $selected = null, $attributes = array(), $showEmpty = '') { + $select = array(); + $showParents = false; + $escapeOptions = true; + $style = null; + $tag = null; + + if (isset($attributes['escape'])) { + $escapeOptions = $attributes['escape']; + unset($attributes['escape']); + } + $attributes = $this->_initInputField($fieldName, array_merge( + (array)$attributes, array('secure' => false) + )); + + if (is_string($options) && isset($this->__options[$options])) { + $options = $this->__generateOptions($options); + } elseif (!is_array($options)) { + $options = array(); + } + if (isset($attributes['type'])) { + unset($attributes['type']); + } + if (in_array('showParents', $attributes)) { + $showParents = true; + unset($attributes['showParents']); + } + + if (!isset($selected)) { + $selected = $attributes['value']; + } + + if (isset($attributes) && array_key_exists('multiple', $attributes)) { + $style = ($attributes['multiple'] === 'checkbox') ? 'checkbox' : null; + $template = ($style) ? 'checkboxmultiplestart' : 'selectmultiplestart'; + $tag = $this->Html->tags[$template]; + $select[] = $this->hidden(null, array('value' => '', 'id' => null, 'secure' => false)); + } else { + $tag = $this->Html->tags['selectstart']; + } + + if (!empty($tag) || isset($template)) { + $this->__secure(); + $select[] = sprintf($tag, $attributes['name'], $this->_parseAttributes( + $attributes, array('name', 'value')) + ); + } + $emptyMulti = ( + $showEmpty !== null && $showEmpty !== false && !( + empty($showEmpty) && (isset($attributes) && + array_key_exists('multiple', $attributes)) + ) + ); + + if ($emptyMulti) { + $showEmpty = ($showEmpty === true) ? '' : $showEmpty; + $options = array_reverse($options, true); + $options[''] = $showEmpty; + $options = array_reverse($options, true); + } + + $select = array_merge($select, $this->__selectOptions( + array_reverse($options, true), + $selected, + array(), + $showParents, + array('escape' => $escapeOptions, 'style' => $style) + )); + + $template = ($style == 'checkbox') ? 'checkboxmultipleend' : 'selectend'; + $select[] = $this->Html->tags[$template]; + return $this->output(implode("\n", $select)); + } +/** + * Returns a SELECT element for days. + * + * @param string $fieldName Prefix name for the SELECT element + * @param string $selected Option which is selected. + * @param array $attributes HTML attributes for the select element + * @param mixed $showEmpty Show/hide the empty select option + * @return string + */ + function day($fieldName, $selected = null, $attributes = array(), $showEmpty = true) { + if ((empty($selected) || $selected === true) && $value = $this->value($fieldName)) { + if (is_array($value)) { + extract($value); + $selected = $day; + } else { + if (empty($value)) { + if (!$showEmpty) { + $selected = 'now'; + } + } else { + $selected = $value; + } + } + } + + if (strlen($selected) > 2) { + $selected = date('d', strtotime($selected)); + } elseif ($selected === false) { + $selected = null; + } + return $this->select( + $fieldName . ".day", $this->__generateOptions('day'), $selected, $attributes, $showEmpty + ); + } +/** + * Returns a SELECT element for years + * + * @param string $fieldName Prefix name for the SELECT element + * @param integer $minYear First year in sequence + * @param integer $maxYear Last year in sequence + * @param string $selected Option which is selected. + * @param array $attributes Attribute array for the select elements. + * @param boolean $showEmpty Show/hide the empty select option + * @return string + */ + function year($fieldName, $minYear = null, $maxYear = null, $selected = null, $attributes = array(), $showEmpty = true) { + if ((empty($selected) || $selected === true) && $value = $this->value($fieldName)) { + if (is_array($value)) { + extract($value); + $selected = $year; + } else { + if (empty($value)) { + if (!$showEmpty && !$maxYear) { + $selected = 'now'; + + } elseif (!$showEmpty && $maxYear && !$selected) { + $selected = $maxYear; + } + } else { + $selected = $value; + } + } + } + + if (strlen($selected) > 4 || $selected === 'now') { + $selected = date('Y', strtotime($selected)); + } elseif ($selected === false) { + $selected = null; + } + $yearOptions = array('min' => $minYear, 'max' => $maxYear); + return $this->select( + $fieldName . ".year", $this->__generateOptions('year', $yearOptions), + $selected, $attributes, $showEmpty + ); + } +/** + * Returns a SELECT element for months. + * + * Attributes: + * + * - 'monthNames' is set and false 2 digit numbers will be used instead of text. + * + * @param string $fieldName Prefix name for the SELECT element + * @param string $selected Option which is selected. + * @param array $attributes Attributes for the select element + * @param boolean $showEmpty Show/hide the empty select option + * @return string + */ + function month($fieldName, $selected = null, $attributes = array(), $showEmpty = true) { + if ((empty($selected) || $selected === true) && $value = $this->value($fieldName)) { + if (is_array($value)) { + extract($value); + $selected = $month; + } else { + if (empty($value)) { + if (!$showEmpty) { + $selected = 'now'; + } + } else { + $selected = $value; + } + } + } + + if (strlen($selected) > 2) { + $selected = date('m', strtotime($selected)); + } elseif ($selected === false) { + $selected = null; + } + $defaults = array('monthNames' => true); + $attributes = array_merge($defaults, (array) $attributes); + $monthNames = $attributes['monthNames']; + unset($attributes['monthNames']); + + return $this->select( + $fieldName . ".month", + $this->__generateOptions('month', array('monthNames' => $monthNames)), + $selected, $attributes, $showEmpty + ); + } +/** + * Returns a SELECT element for hours. + * + * @param string $fieldName Prefix name for the SELECT element + * @param boolean $format24Hours True for 24 hours format + * @param string $selected Option which is selected. + * @param array $attributes List of HTML attributes + * @param mixed $showEmpty True to show an empty element, or a string to provide default empty element text + * @return string + */ + function hour($fieldName, $format24Hours = false, $selected = null, $attributes = array(), $showEmpty = true) { + if ((empty($selected) || $selected === true) && $value = $this->value($fieldName)) { + if (is_array($value)) { + extract($value); + $selected = $hour; + } else { + if (empty($value)) { + if (!$showEmpty) { + $selected = 'now'; + } + } else { + $selected = $value; + } + } + } else { + $value = $selected; + } + + if (strlen($selected) > 2) { + if ($format24Hours) { + $selected = date('H', strtotime($value)); + } else { + $selected = date('g', strtotime($value)); + } + } elseif ($selected === false) { + $selected = null; + } + return $this->select( + $fieldName . ".hour", + $this->__generateOptions($format24Hours ? 'hour24' : 'hour'), + $selected, $attributes, $showEmpty + ); + } +/** + * Returns a SELECT element for minutes. + * + * @param string $fieldName Prefix name for the SELECT element + * @param string $selected Option which is selected. + * @param string $attributes Array of Attributes + * @param bool $showEmpty True to show an empty element, or a string to provide default empty element text + * @return string + */ + function minute($fieldName, $selected = null, $attributes = array(), $showEmpty = true) { + if ((empty($selected) || $selected === true) && $value = $this->value($fieldName)) { + if (is_array($value)) { + extract($value); + $selected = $min; + } else { + if (empty($value)) { + if (!$showEmpty) { + $selected = 'now'; + } + } else { + $selected = $value; + } + } + } + + if (strlen($selected) > 2) { + $selected = date('i', strtotime($selected)); + } elseif ($selected === false) { + $selected = null; + } + $minuteOptions = array(); + + if (isset($attributes['interval'])) { + $minuteOptions['interval'] = $attributes['interval']; + unset($attributes['interval']); + } + return $this->select( + $fieldName . ".min", $this->__generateOptions('minute', $minuteOptions), + $selected, $attributes, $showEmpty + ); + } +/** + * Returns a SELECT element for AM or PM. + * + * @param string $fieldName Prefix name for the SELECT element + * @param string $selected Option which is selected. + * @param string $attributes Array of Attributes + * @param bool $showEmpty Show/Hide an empty option + * @return string + */ + function meridian($fieldName, $selected = null, $attributes = array(), $showEmpty = true) { + if ((empty($selected) || $selected === true) && $value = $this->value($fieldName)) { + if (is_array($value)) { + extract($value); + $selected = $meridian; + } else { + if (empty($value)) { + if (!$showEmpty) { + $selected = date('a'); + } + } else { + $selected = date('a', strtotime($value)); + } + } + } + + if ($selected === false) { + $selected = null; + } + return $this->select( + $fieldName . ".meridian", $this->__generateOptions('meridian'), + $selected, $attributes, $showEmpty + ); + } +/** + * Returns a set of SELECT elements for a full datetime setup: day, month and year, and then time. + * + * Attributes: + * + * - 'monthNames' If set and false numbers will be used for month select instead of text. + * - 'minYear' The lowest year to use in the year select + * - 'maxYear' The maximum year to use in the year select + * - 'interval' The interval for the minutes select. Defaults to 1 + * - 'separator' The contents of the string between select elements. Defaults to '-' + * + * @param string $fieldName Prefix name for the SELECT element + * @param string $dateFormat DMY, MDY, YMD or NONE. + * @param string $timeFormat 12, 24, NONE + * @param string $selected Option which is selected. + * @param string $attributes array of Attributes + * @param bool $showEmpty Whether or not to show an empty default value. + * @return string The HTML formatted OPTION element + */ + function dateTime($fieldName, $dateFormat = 'DMY', $timeFormat = '12', $selected = null, $attributes = array(), $showEmpty = true) { + $year = $month = $day = $hour = $min = $meridian = null; + + if (empty($selected)) { + $selected = $this->value($fieldName); + } + + if ($selected === null && $showEmpty != true) { + $selected = time(); + } + + if (!empty($selected)) { + if (is_array($selected)) { + extract($selected); + } else { + if (is_numeric($selected)) { + $selected = strftime('%Y-%m-%d %H:%M:%S', $selected); + } + $meridian = 'am'; + $pos = strpos($selected, '-'); + if ($pos !== false) { + $date = explode('-', $selected); + $days = explode(' ', $date[2]); + $day = $days[0]; + $month = $date[1]; + $year = $date[0]; + } else { + $days[1] = $selected; + } + + if ($timeFormat != 'NONE' && !empty($timeFormat)) { + $time = explode(':', $days[1]); + $check = str_replace(':', '', $days[1]); + + if (($check > 115959) && $timeFormat == '12') { + $time[0] = $time[0] - 12; + $meridian = 'pm'; + } elseif ($time[0] == '00' && $timeFormat == '12') { + $time[0] = 12; + } elseif ($time[0] > 12) { + $meridian = 'pm'; + } + if ($time[0] == 0 && $timeFormat == '12') { + $time[0] = 12; + } + $hour = $time[0]; + $min = $time[1]; + } + } + } + + $elements = array('Day','Month','Year','Hour','Minute','Meridian'); + $defaults = array( + 'minYear' => null, 'maxYear' => null, 'separator' => '-', + 'interval' => 1, 'monthNames' => true + ); + $attributes = array_merge($defaults, (array) $attributes); + if (isset($attributes['minuteInterval'])) { + $attributes['interval'] = $attributes['minuteInterval']; + unset($attributes['minuteInterval']); + } + $minYear = $attributes['minYear']; + $maxYear = $attributes['maxYear']; + $separator = $attributes['separator']; + $interval = $attributes['interval']; + $monthNames = $attributes['monthNames']; + $attributes = array_diff_key($attributes, $defaults); + + if (isset($attributes['id'])) { + if (is_string($attributes['id'])) { + // build out an array version + foreach ($elements as $element) { + $selectAttrName = 'select' . $element . 'Attr'; + ${$selectAttrName} = $attributes; + ${$selectAttrName}['id'] = $attributes['id'] . $element; + } + } elseif (is_array($attributes['id'])) { + // check for missing ones and build selectAttr for each element + foreach ($elements as $element) { + $selectAttrName = 'select' . $element . 'Attr'; + ${$selectAttrName} = $attributes; + ${$selectAttrName}['id'] = $attributes['id'][strtolower($element)]; + } + } + } else { + // build the selectAttrName with empty id's to pass + foreach ($elements as $element) { + $selectAttrName = 'select' . $element . 'Attr'; + ${$selectAttrName} = $attributes; + } + } + + $opt = ''; + + if ($dateFormat != 'NONE') { + $selects = array(); + foreach (preg_split('//', $dateFormat, -1, PREG_SPLIT_NO_EMPTY) as $char) { + switch ($char) { + case 'Y': + $selects[] = $this->year( + $fieldName, $minYear, $maxYear, $year, $selectYearAttr, $showEmpty + ); + break; + case 'M': + $selectMonthAttr['monthNames'] = $monthNames; + $selects[] = $this->month($fieldName, $month, $selectMonthAttr, $showEmpty); + break; + case 'D': + $selects[] = $this->day($fieldName, $day, $selectDayAttr, $showEmpty); + break; + } + } + $opt = implode($separator, $selects); + } + if (!empty($interval) && $interval > 1 && !empty($min)) { + $min = round($min * (1 / $interval)) * $interval; + } + $selectMinuteAttr['interval'] = $interval; + switch ($timeFormat) { + case '24': + $opt .= $this->hour($fieldName, true, $hour, $selectHourAttr, $showEmpty) . ':' . + $this->minute($fieldName, $min, $selectMinuteAttr, $showEmpty); + break; + case '12': + $opt .= $this->hour($fieldName, false, $hour, $selectHourAttr, $showEmpty) . ':' . + $this->minute($fieldName, $min, $selectMinuteAttr, $showEmpty) . ' ' . + $this->meridian($fieldName, $meridian, $selectMeridianAttr, $showEmpty); + break; + case 'NONE': + default: + $opt .= ''; + break; + } + return $opt; + } +/** + * Gets the input field name for the current tag + * + * @param array $options + * @param string $key + * @return array + */ + function __name($options = array(), $field = null, $key = 'name') { + if ($this->requestType == 'get') { + if ($options === null) { + $options = array(); + } elseif (is_string($options)) { + $field = $options; + $options = 0; + } + + if (!empty($field)) { + $this->setEntity($field); + } + + if (is_array($options) && isset($options[$key])) { + return $options; + } + $name = $this->field(); + + if (is_array($options)) { + $options[$key] = $name; + return $options; + } else { + return $name; + } + } + return parent::__name($options, $field, $key); + } +/** + * Returns an array of formatted OPTION/OPTGROUP elements + * @access private + * @return array + */ + function __selectOptions($elements = array(), $selected = null, $parents = array(), $showParents = null, $attributes = array()) { + $select = array(); + $attributes = array_merge(array('escape' => true, 'style' => null), $attributes); + $selectedIsEmpty = ($selected === '' || $selected === null); + $selectedIsArray = is_array($selected); + + foreach ($elements as $name => $title) { + $htmlOptions = array(); + if (is_array($title) && (!isset($title['name']) || !isset($title['value']))) { + if (!empty($name)) { + if ($attributes['style'] === 'checkbox') { + $select[] = $this->Html->tags['fieldsetend']; + } else { + $select[] = $this->Html->tags['optiongroupend']; + } + $parents[] = $name; + } + $select = array_merge($select, $this->__selectOptions( + $title, $selected, $parents, $showParents, $attributes + )); + + if (!empty($name)) { + if ($attributes['style'] === 'checkbox') { + $select[] = sprintf($this->Html->tags['fieldsetstart'], $name); + } else { + $select[] = sprintf($this->Html->tags['optiongroup'], $name, ''); + } + } + $name = null; + } elseif (is_array($title)) { + $htmlOptions = $title; + $name = $title['value']; + $title = $title['name']; + unset($htmlOptions['name'], $htmlOptions['value']); + } + + if ($name !== null) { + if ((!$selectedIsEmpty && $selected == $name) || ($selectedIsArray && in_array($name, $selected))) { + if ($attributes['style'] === 'checkbox') { + $htmlOptions['checked'] = true; + } else { + $htmlOptions['selected'] = 'selected'; + } + } + + if ($showParents || (!in_array($title, $parents))) { + $title = ($attributes['escape']) ? h($title) : $title; + + if ($attributes['style'] === 'checkbox') { + $htmlOptions['value'] = $name; + + $tagName = Inflector::camelize( + $this->model() . '_' . $this->field().'_'.Inflector::underscore($name) + ); + $htmlOptions['id'] = $tagName; + $label = array('for' => $tagName); + + if (isset($htmlOptions['checked']) && $htmlOptions['checked'] === true) { + $label['class'] = 'selected'; + } + + list($name) = array_values($this->__name()); + + if (empty($attributes['class'])) { + $attributes['class'] = 'checkbox'; + } + $label = $this->label(null, $title, $label); + $item = sprintf( + $this->Html->tags['checkboxmultiple'], $name, + $this->_parseAttributes($htmlOptions) + ); + $select[] = $this->Html->div($attributes['class'], $item . $label); + } else { + $select[] = sprintf( + $this->Html->tags['selectoption'], + $name, $this->_parseAttributes($htmlOptions), $title + ); + } + } + } + } + + return array_reverse($select, true); + } +/** + * Generates option lists for common <select /> menus + * @access private + */ + function __generateOptions($name, $options = array()) { + if (!empty($this->options[$name])) { + return $this->options[$name]; + } + $data = array(); + + switch ($name) { + case 'minute': + if (isset($options['interval'])) { + $interval = $options['interval']; + } else { + $interval = 1; + } + $i = 0; + while ($i < 60) { + $data[$i] = sprintf('%02d', $i); + $i += $interval; + } + break; + case 'hour': + for ($i = 1; $i <= 12; $i++) { + $data[sprintf('%02d', $i)] = $i; + } + break; + case 'hour24': + for ($i = 0; $i <= 23; $i++) { + $data[sprintf('%02d', $i)] = $i; + } + break; + case 'meridian': + $data = array('am' => 'am', 'pm' => 'pm'); + break; + case 'day': + $min = 1; + $max = 31; + + if (isset($options['min'])) { + $min = $options['min']; + } + if (isset($options['max'])) { + $max = $options['max']; + } + + for ($i = $min; $i <= $max; $i++) { + $data[sprintf('%02d', $i)] = $i; + } + break; + case 'month': + if ($options['monthNames']) { + $data['01'] = __('January', true); + $data['02'] = __('February', true); + $data['03'] = __('March', true); + $data['04'] = __('April', true); + $data['05'] = __('May', true); + $data['06'] = __('June', true); + $data['07'] = __('July', true); + $data['08'] = __('August', true); + $data['09'] = __('September', true); + $data['10'] = __('October', true); + $data['11'] = __('November', true); + $data['12'] = __('December', true); + } else { + for ($m = 1; $m <= 12; $m++) { + $data[sprintf("%02s", $m)] = strftime("%m", mktime(1, 1, 1, $m, 1, 1999)); + } + } + break; + case 'year': + $current = intval(date('Y')); + + if (!isset($options['min'])) { + $min = $current - 20; + } else { + $min = $options['min']; + } + + if (!isset($options['max'])) { + $max = $current + 20; + } else { + $max = $options['max']; + } + if ($min > $max) { + list($min, $max) = array($max, $min); + } + for ($i = $min; $i <= $max; $i++) { + $data[$i] = $i; + } + $data = array_reverse($data, true); + break; + } + $this->__options[$name] = $data; + return $this->__options[$name]; + } +/** + * Sets field defaults and adds field to form security input hash + * + * Options + * - secure - boolean whether or not the the field should be added to the security fields. + * + * @param string $field + * @param array $options + * @return array + * @access protected + */ + function _initInputField($field, $options = array()) { + if (isset($options['secure'])) { + $secure = $options['secure']; + unset($options['secure']); + } else { + $secure = (isset($this->params['_Token']) && !empty($this->params['_Token'])); + } + $result = parent::_initInputField($field, $options); + + if ($secure) { + $this->__secure(); + } + return $result; + } +} + +?> \ No newline at end of file diff --git a/cake/libs/view/helpers/html.php b/cake/libs/view/helpers/html.php new file mode 100755 index 0000000..cb94089 --- /dev/null +++ b/cake/libs/view/helpers/html.php @@ -0,0 +1,643 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Html Helper class file. + * + * Simplifies the construction of HTML elements. + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.helpers + * @since CakePHP(tm) v 0.9.1 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * 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 + */ +class HtmlHelper extends AppHelper { +/************************************************************************* + * Public variables + *************************************************************************/ +/**#@+ + * @access public + */ +/** + * html tags used by this helper. + * + * @var array + */ + 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 type="submit" %s/>', + 'submitimage' => '<input type="image" src="%s" %s/>', + 'button' => '<input type="%s" %s/>', + '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>' + ); +/** + * Base URL + * + * @var string + */ + var $base = null; +/** + * URL to current action. + * + * @var string + */ + var $here = null; +/** + * Parameter array. + * + * @var array + */ + var $params = array(); +/** + * Current action. + * + * @var string + */ + var $action = null; +/** + * Enter description here... + * + * @var array + */ + var $data = null; +/**#@-*/ +/************************************************************************* + * Private variables + *************************************************************************/ +/**#@+ + * @access private + */ +/** + * Breadcrumbs. + * + * @var array + * @access private + */ + var $_crumbs = 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') + */ + 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. + */ + function docType($type = 'xhtml-strict') { + if (isset($this->__docTypes[$type])) { + return $this->output($this->__docTypes[$type]); + } + return null; + } +/** + * Creates a link to an external resource and handles basic meta tags + * + * @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 $attributes Other attributes for the generated tag. If the type attribute is html, rss, atom, or icon, the mime-type is returned. + * @param boolean $inline If set to false, the generated tag appears in the head tag of the layout. + * @return string + */ + function meta($type, $url = null, $attributes = array(), $inline = true) { + 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($attributes['type']) && $url !== null) { + if (is_array($url) && isset($url['ext'])) { + $type = $types[$url['ext']]; + } else { + $type = $types['rss']; + } + } elseif (isset($attributes['type']) && isset($types[$attributes['type']])) { + $type = $types[$attributes['type']]; + unset($attributes['type']); + } else { + $type = array(); + } + } elseif ($url !== null) { + $inline = $url; + } + $attributes = array_merge($type, $attributes); + $out = null; + + if (isset($attributes['link'])) { + if (isset($attributes['rel']) && $attributes['rel'] === 'icon') { + $out = sprintf($this->tags['metalink'], $attributes['link'], $this->_parseAttributes($attributes, array('link'), ' ', ' ')); + $attributes['rel'] = 'shortcut icon'; + } else { + $attributes['link'] = $this->url($attributes['link'], true); + } + $out .= sprintf($this->tags['metalink'], $attributes['link'], $this->_parseAttributes($attributes, array('link'), ' ', ' ')); + } else { + $out = sprintf($this->tags['meta'], $this->_parseAttributes($attributes, array('type'))); + } + + if ($inline) { + return $this->output($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. Example: "utf-8". + * @return string A meta tag containing the specified character set. + */ + function charset($charset = null) { + if (empty($charset)) { + $charset = strtolower(Configure::read('App.encoding')); + } + return $this->output(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. + * + * @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 $htmlAttributes Array of HTML attributes. + * @param string $confirmMessage JavaScript confirmation message. + * @param boolean $escapeTitle Whether or not $title should be HTML escaped. + * @return string An <a /> element. + */ + function link($title, $url = null, $htmlAttributes = array(), $confirmMessage = false, $escapeTitle = true) { + if ($url !== null) { + $url = $this->url($url); + } else { + $url = $this->url($title); + $title = $url; + $escapeTitle = false; + } + + if (isset($htmlAttributes['escape']) && $escapeTitle == true) { + $escapeTitle = $htmlAttributes['escape']; + } + + if ($escapeTitle === true) { + $title = h($title); + } elseif (is_string($escapeTitle)) { + $title = htmlentities($title, ENT_QUOTES, $escapeTitle); + } + + if (!empty($htmlAttributes['confirm'])) { + $confirmMessage = $htmlAttributes['confirm']; + unset($htmlAttributes['confirm']); + } + if ($confirmMessage) { + $confirmMessage = str_replace("'", "\'", $confirmMessage); + $confirmMessage = str_replace('"', '\"', $confirmMessage); + $htmlAttributes['onclick'] = "return confirm('{$confirmMessage}');"; + } elseif (isset($htmlAttributes['default']) && $htmlAttributes['default'] == false) { + if (isset($htmlAttributes['onclick'])) { + $htmlAttributes['onclick'] .= ' event.returnValue = false; return false;'; + } else { + $htmlAttributes['onclick'] = 'event.returnValue = false; return false;'; + } + unset($htmlAttributes['default']); + } + return $this->output(sprintf($this->tags['link'], $url, $this->_parseAttributes($htmlAttributes), $title)); + } +/** + * Creates a link element for CSS stylesheets. + * + * @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 $htmlAttributes Array of HTML attributes. + * @param boolean $inline If set to false, the generated tag appears in the head tag of the layout. + * @return string CSS <link /> or <style /> tag, depending on the type of link. + */ + function css($path, $rel = null, $htmlAttributes = array(), $inline = true) { + if (is_array($path)) { + $out = ''; + foreach ($path as $i) { + $out .= "\n\t" . $this->css($i, $rel, $htmlAttributes, $inline); + } + if ($inline) { + return $out . "\n"; + } + return; + } + + if (strpos($path, '://') !== false) { + $url = $path; + } else { + if ($path[0] !== '/') { + $path = CSS_URL . $path; + } + + if (strpos($path, '?') === false) { + if (strpos($path, '.css') === false) { + $path .= '.css'; + } + } + + $path = $this->webroot($path); + + $url = $path; + if (strpos($path, '?') === false && ((Configure::read('Asset.timestamp') === true && Configure::read() > 0) || Configure::read('Asset.timestamp') === 'force')) { + $url .= '?' . @filemtime(WWW_ROOT . str_replace('/', DS, $path)); + } + + if (Configure::read('Asset.filter.css')) { + $url = str_replace(CSS_URL, 'ccss/', $url); + } + } + + if ($rel == 'import') { + $out = sprintf($this->tags['style'], $this->_parseAttributes($htmlAttributes, null, '', ' '), '@import url(' . $url . ');'); + } else { + if ($rel == null) { + $rel = 'stylesheet'; + } + $out = sprintf($this->tags['css'], $rel, $url, $this->_parseAttributes($htmlAttributes, null, '', ' ')); + } + $out = $this->output($out); + + if ($inline) { + return $out; + } else { + $view =& ClassRegistry::getObject('view'); + $view->addScript($out); + } + } +/** + * Builds CSS style data from an array of CSS properties + * + * @param array $data Style data array + * @param boolean $inline Whether or not the style block should be displayed inline + * @return string CSS styling data + */ + function style($data, $inline = true) { + if (!is_array($data)) { + return $data; + } + $out = array(); + foreach ($data as $key=> $value) { + $out[] = $key.':'.$value.';'; + } + if ($inline) { + return join(' ', $out); + } + return join("\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 + */ + function getCrumbs($separator = '&raquo;', $startText = false) { + if (count($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 $this->output(join($separator, $out)); + } else { + return null; + } + } +/** + * Creates a formatted IMG element. + * + * @param string $path Path to the image file, relative to the app/webroot/img/ directory. + * @param array $options Array of HTML attributes. + * @return string + */ + function image($path, $options = array()) { + if (is_array($path)) { + $path = $this->url($path); + } elseif ($path[0] === '/') { + $path = $this->webroot($path); + } elseif (strpos($path, '://') === false) { + $path = $this->webroot(IMAGES_URL . $path); + + if ((Configure::read('Asset.timestamp') == true && Configure::read() > 0) || Configure::read('Asset.timestamp') === 'force') { + $path .= '?' . @filemtime(str_replace('/', DS, WWW_ROOT . $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 $this->output(sprintf($this->tags['link'], $this->url($url), null, $image)); + } + + return $this->output($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 + */ + function tableHeaders($names, $trOptions = null, $thOptions = null) { + $out = array(); + foreach ($names as $arg) { + $out[] = sprintf($this->tags['tableheader'], $this->_parseAttributes($thOptions), $arg); + } + $data = sprintf($this->tags['tablerow'], $this->_parseAttributes($trOptions), join(' ', $out)); + return $this->output($data); + } +/** + * 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 + */ + 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, join(' ', $cellsOut)); + } + return $this->output(join("\n", $out)); + } +/** + * Returns a formatted block tag, i.e DIV, SPAN, P. + * + * @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 $attributes Additional HTML attributes of the DIV tag + * @param boolean $escape If true, $text will be HTML-escaped + * @return string The formatted tag element + */ + function tag($name, $text = null, $attributes = array(), $escape = false) { + if ($escape) { + $text = h($text); + } + if (!is_array($attributes)) { + $attributes = array('class' => $attributes); + } + if ($text === null) { + $tag = 'tagstart'; + } else { + $tag = 'tag'; + } + return $this->output(sprintf($this->tags[$tag], $name, $this->_parseAttributes($attributes, null, ' ', ''), $text, $name)); + } +/** + * Returns a formatted DIV tag for HTML FORMs. + * + * @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 $attributes Additional HTML attributes of the DIV tag + * @param boolean $escape If true, $text will be HTML-escaped + * @return string The formatted DIV element + */ + function div($class = null, $text = null, $attributes = array(), $escape = false) { + if ($class != null && !empty($class)) { + $attributes['class'] = $class; + } + return $this->tag('div', $text, $attributes, $escape); + } +/** + * Returns a formatted P tag. + * + * @param string $class CSS class name of the p element. + * @param string $text String content that will appear inside the p element. + * @param array $attributes Additional HTML attributes of the P tag + * @param boolean $escape If true, $text will be HTML-escaped + * @return string The formatted P element + */ + function para($class, $text, $attributes = array(), $escape = false) { + if ($escape) { + $text = h($text); + } + if ($class != null && !empty($class)) { + $attributes['class'] = $class; + } + if ($text === null) { + $tag = 'parastart'; + } else { + $tag = 'para'; + } + return $this->output(sprintf($this->tags[$tag], $this->_parseAttributes($attributes, null, ' ', ''), $text)); + } +/** + * Build a nested list (UL/OL) out of an associative array. + * + * @param array $list Set of elements to list + * @param array $attributes Additional HTML attributes of the list (ol/ul) tag or if ul/ol use that as tag + * @param array $itemAttributes 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, $attributes = array(), $itemAttributes = array(), $tag = 'ul') { + if (is_string($attributes)) { + $tag = $attributes; + $attributes = array(); + } + $items = $this->__nestedListItem($list, $attributes, $itemAttributes, $tag); + return sprintf($this->tags[$tag], $this->_parseAttributes($attributes, 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 $attributes Additional HTML attributes of the list (ol/ul) tag + * @param array $itemAttributes 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 nestedList() + */ + function __nestedListItem($items, $attributes, $itemAttributes, $tag) { + $out = ''; + + $index = 1; + foreach ($items as $key => $item) { + if (is_array($item)) { + $item = $key . $this->nestedList($item, $attributes, $itemAttributes, $tag); + } + if (isset($itemAttributes['even']) && $index % 2 == 0) { + $itemAttributes['class'] = $itemAttributes['even']; + } else if (isset($itemAttributes['odd']) && $index % 2 != 0) { + $itemAttributes['class'] = $itemAttributes['odd']; + } + $out .= sprintf($this->tags['li'], $this->_parseAttributes(array_diff_key($itemAttributes, array_flip(array('even', 'odd'))), null, ' ', ''), $item); + $index++; + } + return $out; + } +} +?> \ No newline at end of file diff --git a/cake/libs/view/helpers/javascript.php b/cake/libs/view/helpers/javascript.php new file mode 100755 index 0000000..9756d62 --- /dev/null +++ b/cake/libs/view/helpers/javascript.php @@ -0,0 +1,708 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Javascript Helper class file. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.helpers + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * 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 + */ +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 (strpos($url, '.js') === false) { + $url .= '.js'; + } + } + + $url = $this->webroot($url); + $timestampEnabled = ( + (Configure::read('Asset.timestamp') === true && Configure::read() > 0) || + Configure::read('Asset.timestamp') === 'force' + ); + + if (strpos($url, '?') === false && $timestampEnabled) { + $url .= '?' . @filemtime(WWW_ROOT . str_replace('/', DS, $url)); + } + + if (Configure::read('Asset.filter.js')) { + $url = str_replace(JS_URL, 'cjs/', $url); + } + } + $out = $this->output(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 "'" + * + * @param array $data Data to be converted + * @param array $options Set of options: block, prefix, postfix, stringKeys, quoteKeys, q + * @param string $prefix DEPRECATED, use $options['prefix'] instead. Prepends the string to the returned data + * @param string $postfix DEPRECATED, use $options['postfix'] instead. Appends the string to the returned data + * @param array $stringKeys DEPRECATED, use $options['stringKeys'] instead. A list of array keys to be treated as a string + * @param boolean $quoteKeys DEPRECATED, use $options['quoteKeys'] instead. If false, treats $stringKey as a list of keys *not* to be quoted + * @param string $q DEPRECATED, use $options['q'] instead. The type of quote to use + * @return string A JSON code block + */ + function object($data = array(), $options = array(), $prefix = null, $postfix = null, $stringKeys = null, $quoteKeys = null, $q = null) { + 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))); + } 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 = '{' . join(',', $out) . '}'; + } else { + $rt = '[' . join(',', $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 = ife($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); + } +} + +?> \ No newline at end of file diff --git a/cake/libs/view/helpers/js.php b/cake/libs/view/helpers/js.php new file mode 100755 index 0000000..15e2509 --- /dev/null +++ b/cake/libs/view/helpers/js.php @@ -0,0 +1,451 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Javascript Generator class file. + * + * PHP versions 4 and 5 + * + * CakePHP : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2006-2008, Cake Software Foundation, Inc. + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2006-2008, Cake Software Foundation, Inc. + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP Project + * @package cake + * @subpackage cake.cake.libs.view.helpers + * @since CakePHP v 1.2 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Javascript Generator helper class for easy use of JavaScript. + * + * JsHelper provides an abstract interface for authoring JavaScript with a + * given client-side library. + * + * @package cake + * @subpackage cake.cake.libs.view.helpers + */ +class JsHelper extends Overloadable2 { + var $base = null; + var $webroot = null; + var $here = null; + var $params = null; + var $action = null; + var $data = null; + var $themeWeb = null; + var $plugin = null; + + var $helpers = array(); + + var $hook = null; + + var $__objects = array(); + + var $effectMap = array( + 'Appear', 'Fade', 'Puff', 'BlindDown', 'BlindUp', 'SwitchOff', 'SlideDown', 'SlideUp', + 'DropOut', 'Shake', 'Pulsate', 'Squish', 'Fold', 'Grow', 'Shrink', 'Highlight', 'toggle' + ); + + var $output = false; + + function __construct() { + $this->effectMap = array_combine( + array_map('strtolower', $this->effectMap), + $this->effectMap + ); + parent::__construct(); + } + + function call__($method, $params) { + if (is_object($this->hook) && method_exists($this->hook, $method)) { + $this->hook->dispatchMethod($method . '_', $params); + } + if (method_exists($this, $method . '_')) { + return $this->dispatchMethod($method . '_', $params); + } + } + + function alert_($message) { + return 'alert("' . $this->escape($message) . '");'; + } + + function if_($if, $then, $else = null, $elseIf = array()) { + $len = strlen($if) - 1; + if ($if{$len} == ';') { + $if{$len} = null; + } + + $out = 'if (' . $if . ') { ' . $then . ' }'; + + foreach ($elseIf as $cond => $exec) { + //$out .= + } + + if (!empty($else)) { + $out .= ' else { ' . $else . ' }'; + } + + return $out; + } + + function confirm_($message) { + return 'confirm("' . $this->escape($message) . '");'; + } + + function prompt_($message, $default = '') { + return 'prompt("' . $this->escape($message) . '", "' . $this->escape($default) . '");'; + } +/* + * Tries a series of expressions, and executes after first successful completion. + * (See Prototype's Try.these). + * + * @return string + */ + function tryThese_($expr1, $expr2, $expr3) { + } +/** + * Loads a remote URL + * + * @param string $url + * @param array $options + * @return string + */ + function load_($url = null, $options = array()) { + + if (isset($options['update'])) { + if (!is_array($options['update'])) { + $func = "new Ajax.Updater('{$options['update']}',"; + } else { + $func = "new Ajax.Updater(document.createElement('div'),"; + } + if (!isset($options['requestHeaders'])) { + $options['requestHeaders'] = array(); + } + if (is_array($options['update'])) { + $options['update'] = join(' ', $options['update']); + } + $options['requestHeaders']['X-Update'] = $options['update']; + } else { + $func = "new Ajax.Request("; + } + + $func .= "'" . Router::url($url) . "'"; + $ajax =& new AjaxHelper(); + $func .= ", " . $ajax->__optionsForAjax($options) . ")"; + + if (isset($options['before'])) { + $func = "{$options['before']}; $func"; + } + if (isset($options['after'])) { + $func = "$func; {$options['after']};"; + } + if (isset($options['condition'])) { + $func = "if ({$options['condition']}) { $func; }"; + } + if (isset($options['confirm'])) { + $func = "if (confirm('" . $this->Javascript->escapeString($options['confirm']) + . "')) { $func; } else { return false; }"; + } + return $func; + } +/** + * Redirects to a URL + * + * @param mixed $url + * @param array $options + * @return string + */ + function redirect_($url = null) { + return 'window.location = "' . Router::url($url) . '";'; + } +/** + * 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 escape($string) { + $escape = array("\r\n" => '\n', "\r" => '\n', "\n" => '\n', '"' => '\"', "'" => "\\'"); + return str_replace(array_keys($escape), array_values($escape), $string); + } + + function get__($name) { + return $this->__object($name, 'id'); + } + + function select($pattern) { + return $this->__object($pattern, 'pattern'); + } + + function real($var) { + return $this->__object($var, 'real'); + } + + function __object($name, $var) { + if (!isset($this->__objects[$name])) { + $this->__objects[$name] = new JsHelperObject($this); + $this->__objects[$name]->{$var} = $name; + } + return $this->__objects[$name]; + } +/** + * Generates a JavaScript object in JavaScript Object Notation (JSON) + * from an array + * + * @param array $data Data to be converted + * @param boolean $block Wraps return value in a <script/> block if true + * @param string $prefix Prepends the string to the returned data + * @param string $postfix Appends the string to the returned data + * @param array $stringKeys A list of array keys to be treated as a string + * @param boolean $quoteKeys If false, treats $stringKey as a list of keys *not* to be quoted + * @param string $q The type of quote to use + * @return string A JSON code block + */ + function object($data = array(), $block = false, $prefix = '', $postfix = '', $stringKeys = array(), $quoteKeys = true, $q = "\"") { + if (is_object($data)) { + $data = get_object_vars($data); + } + + $out = array(); + $key = array(); + + if (is_array($data)) { + $keys = array_keys($data); + } + + $numeric = true; + + if (!empty($keys)) { + foreach ($keys as $key) { + if (!is_numeric($key)) { + $numeric = false; + break; + } + } + } + + foreach ($data as $key => $val) { + if (is_array($val) || is_object($val)) { + $val = $this->object($val, false, '', '', $stringKeys, $quoteKeys, $q); + } else { + if ((!count($stringKeys) && !is_numeric($val) && !is_bool($val)) || ($quoteKeys && in_array($key, $stringKeys)) || (!$quoteKeys && !in_array($key, $stringKeys)) && $val !== null) { + $val = $q . $this->escapeString($val) . $q; + } + if ($val == null) { + $val = 'null'; + } + } + + if (!$numeric) { + $val = $q . $key . $q . ':' . $val; + } + + $out[] = $val; + } + + if (!$numeric) { + $rt = '{' . join(', ', $out) . '}'; + } else { + $rt = '[' . join(', ', $out) . ']'; + } + $rt = $prefix . $rt . $postfix; + + if ($block) { + $rt = $this->codeBlock($rt); + } + + return $rt; + } +} + +class JsHelperObject { + var $__parent = null; + + var $id = null; + + var $pattern = null; + + var $real = null; + + function __construct(&$parent) { + if (is_object($parent)) { + $this->setParent($parent); + } + } + + function toString() { + return $this->__toString(); + } + + function __toString() { + return $this->literal; + } + + function ref($ref = null) { + if ($ref == null) { + foreach (array('id', 'pattern', 'real') as $ref) { + if ($this->{$ref} !== null) { + return $this->{$ref}; + } + } + } else { + return ($this->{$ref} !== null); + } + return null; + } + + function literal($append = null) { + if (!empty($this->id)) { + $data = '$("' . $this->id . '")'; + } + if (!empty($this->pattern)) { + $data = '$$("' . $this->pattern . '")'; + } + if (!empty($this->real)) { + $data = $this->real; + } + if (!empty($append)) { + $data .= '.' . $append; + } + return $data; + } + + function __call($name, $args) { + $data = ''; + + if (isset($this->__parent->effectMap[strtolower($name)])) { + array_unshift($args, $this->__parent->effectMap[strtolower($name)]); + $name = 'effect'; + } + + switch ($name) { + case 'effect': + case 'visualEffect': + + if (strpos($args[0], '_') || $args[0]{0} != strtoupper($args[0]{0})) { + $args[0] = Inflector::camelize($args[0]); + } + + if (strtolower($args[0]) == 'highlight') { + $data .= 'new '; + } + if ($this->pattern == null) { + $data .= 'Effect.' . $args[0] . '(' . $this->literal(); + } else { + $data .= 'Effect.' . $args[0] . '(item'; + } + + if (isset($args[1]) && is_array($args[1])) { + $data .= ', {' . $this->__options($args[1]) . '}'; + } + $data .= ');'; + + if ($this->pattern !== null) { + $data = $this->each($data); + } + break; + case 'remove': + case 'toggle': + case 'show': + case 'hide': + if (empty($args)) { + $obj = 'Element'; + $params = ''; + } else { + $obj = 'Effect'; + $params = ', "' . $args[0] . '"'; + } + + if ($this->pattern != null) { + $data = $this->each($obj . ".{$name}(item);"); + } else { + $data = $obj . ".{$name}(" . $this->literal() . ');'; + } + break; + case 'visible': + $data = $this->literal() . '.visible();'; + break; + case 'update': + $data = $this->literal() . ".update({$args[0]});"; + break; + case 'load': + $data = 'new Ajax.Updater("' . $this->id . '", "' . $args[0] . '"'; + if (isset($args[1]) && is_array($args[1])) { + $data .= ', {' . $this->__options($args[1]) . '}'; + } + $data .= ');'; + break; + case 'each': + case 'all': + case 'any': + case 'detect': + case 'findAll': + if ($this->pattern != null) { + $data = $this->__iterate($name, $args[0]); + } + break; + case 'addClass': + case 'removeClass': + case 'hasClass': + case 'toggleClass': + $data = $this->literal() . ".{$name}Name(\"{$args[0]}\");"; + break; + case 'clone': + case 'inspect': + case 'keys': + case 'values': + $data = "Object.{$name}(" . $this->literal() . ");"; + break; + case 'extend': + $data = "Object.extend(" . $this->literal() . ", {$args[0]});"; + break; + case '...': + // Handle other methods here + // including interfaces to load other files on-the-fly + // that add support for additional methods/replacing existing methods + break; + default: + $data = $this->literal() . '.' . $name . '();'; + break; + } + + if ($this->__parent->output) { + echo $data; + } else { + return $data; + } + } + + function __iterate($method, $data) { + return '$$("' . $this->pattern . '").' . $method . '(function(item) {' . $data . '});'; + } + + function setParent(&$parent) { + $this->__parent =& $parent; + } + + function __options($opts) { + $options = array(); + foreach ($opts as $key => $val) { + if (!is_int($val)) { + $val = '"' . $val . '"'; + } + $options[] = $key . ':' . $val; + } + return join(', ', $options); + } +} +?> \ No newline at end of file diff --git a/cake/libs/view/helpers/number.php b/cake/libs/view/helpers/number.php new file mode 100755 index 0000000..6c814e0 --- /dev/null +++ b/cake/libs/view/helpers/number.php @@ -0,0 +1,190 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Number Helper. + * + * Methods to make numbers more readable. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.helpers + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Number helper library. + * + * Methods to make numbers more readable. + * + * @package cake + * @subpackage cake.cake.libs.view.helpers + */ +class NumberHelper extends AppHelper { +/** + * 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 Enter description here... + * @static + */ + 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 + * @static + */ + 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 + * @static + */ + 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 + * @static + */ + 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. + * + * @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. + */ + function currency($number, $currency = 'USD', $options = array()) { + $default = array( + 'before'=>'', 'after' => '', 'zero' => '0', 'places' => 2, 'thousands' => ',', + 'decimals' => '.','negative' => '()', 'escape' => true + ); + $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' => 'c', 'zero' => 0, 'places' => 2, 'thousands' => '.', + 'decimals' => ',', 'negative' => '()', 'escape' => false + ) + ); + + if (isset($currencies[$currency])) { + $default = $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 ) { + $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; + } +} +?> \ No newline at end of file diff --git a/cake/libs/view/helpers/paginator.php b/cake/libs/view/helpers/paginator.php new file mode 100755 index 0000000..7b5dd09 --- /dev/null +++ b/cake/libs/view/helpers/paginator.php @@ -0,0 +1,644 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Pagination Helper class file. + * + * Generates pagination links + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.helpers + * @since CakePHP(tm) v 1.2.0 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * 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 + */ +class PaginatorHelper extends AppHelper { +/** + * Helper dependencies + * + * @var array + */ + var $helpers = array('Html', 'Ajax'); +/** + * Holds the default model for paged recordsets + * + * @var string + */ + var $__defaultModel = null; +/** + * Holds the default options for pagination links + * + * The values that may be specified are: + * + * - <i>$options['format']</i> 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% . + * - <i>$options['separator']</i> The separator of the actual page and number of pages (default: ' of '). + * - <i>$options['url']</i> Url of the action. See Router::url() + * - <i>$options['url']['sort']</i> the key that the recordset is sorted. + * - <i>$options['url']['direction']</i> Direction of the sorting (default: 'asc'). + * - <i>$options['url']['page']</i> Page # to display. + * - <i>$options['model']</i> The name of the model. + * - <i>$options['escape']</i> Defines if the title field for the link should be escaped (default: true). + * - <i>$options['update']</i> 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. + * - <i>$options['indicator']</i> DOM id of the element that will be shown when doing AJAX requests. + * + * @var array + */ + var $options = array(); +/** + * 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. + */ + 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 #options for list of keys. + */ + 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. + */ + 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. + */ + 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'])) { + if (preg_match('/(?:\w+\.)?(\w+)/', $options['sort'], $result) && isset($result[1])) { + if ($result[0] == $this->defaultModel()) { + return $result[1]; + } + } + return $options['sort']; + } elseif (isset($options['order']) && is_array($options['order'])) { + return key($options['order']); + } elseif (isset($options['order']) && is_string($options['order'])) { + if (preg_match('/(?:\w+\.)?(\w+)/', $options['order'], $result) && isset($result[1])) { + return $result[1]; + } + 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. + */ + 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 + * + * @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. + */ + 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 + * + * @param string $title Title for the link. Defaults to 'Next >>'. + * @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 "next" link or or $disabledTitle text if the link is disabled. + */ + function next($title = 'Next >>', $options = array(), $disabledTitle = null, $disabledOptions = array()) { + return $this->__pagingLink('Next', $title, $options, $disabledTitle, $disabledOptions); + } +/** + * Generates a sorting link + * + * @param string $title Title for the link. + * @param string $key The name of the key that the recordset should be sorted. + * @param array $options Options for sorting link. See #options 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'. + */ + 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 = 'asc'; + $sortKey = $this->sortKey($options['model']); + $isSorted = ($sortKey === $key || $sortKey === $this->defaultModel() . '.' . $key); + + if ($isSorted && $this->sortDir($options['model']) === 'asc') { + $dir = 'desc'; + } + + 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 + * + * @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. + */ + 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']) ? 'Ajax' : '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 + * @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) + */ + 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 + * + */ + 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' => 'div'); + $options = array_merge($_defaults, (array)$options); + $paging = $this->params($options['model']); + + 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->link($title, $url, array_merge($options, array('escape' => $escape))); + } else { + return $this->Html->tag($tag, $title, $options, $escape); + } + } +/** + * 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. + */ + 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. + */ + 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. + */ + function hasPage($model = null, $page = 1) { + if (is_numeric($model)) { + $page = $model; + $model = null; + } + $paging = $this->params($model); + return $page <= $paging['pageCount']; + } +/** + * Protected method + * + */ + 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. + */ + 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 + * + * @param mixed $options Options for the counter string. See #options for list of keys. + * @return string Counter string. + */ + function counter($options = array()) { + if (is_string($options)) { + $options = array('format' => $options); + } + + $options = array_merge( + array( + 'model' => $this->defaultModel(), + 'format' => 'pages', + 'separator' => ' of ' + ), + $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] . $paging['count']; + break; + case 'pages': + $out = $paging['page'] . $options['separator'] . $paging['pageCount']; + break; + default: + $replace = array( + '%page%' => $paging['page'], + '%pages%' => $paging['pageCount'], + '%current%' => $paging['current'], + '%count%' => $paging['count'], + '%start%' => $start, + '%end%' => $end + ); + $out = str_replace(array_keys($replace), array_values($replace), $options['format']); + break; + } + return $this->output($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) + * + * @param mixed $options Options for the numbers, (before, after, model, modulus, separator) + * @return string numbers string. + */ + function numbers($options = array()) { + if ($options === true) { + $options = array( + 'before' => ' | ', 'after' => ' | ', + 'first' => 'first', 'last' => 'last', + ); + } + + $options = array_merge( + array( + 'tag' => 'span', + 'before'=> null, 'after'=> null, + 'model' => $this->defaultModel(), + 'modulus' => '8', 'separator' => ' | ', + 'first' => null, 'last' => null, + ), + (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['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 $this->output($out); + } +/** + * Returns a first or set of numbers for the first pages + * + * @param mixed $first if string use as label for the link, if numeric print page numbers + * @param mixed $options + * @return string numbers string. + */ + 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 + * + * @param mixed $last if string use as label for the link, if numeric print page numbers + * @param mixed $options + * @return string numbers string. + */ + 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; + } +} +?> diff --git a/cake/libs/view/helpers/rss.php b/cake/libs/view/helpers/rss.php new file mode 100755 index 0000000..a7889a2 --- /dev/null +++ b/cake/libs/view/helpers/rss.php @@ -0,0 +1,277 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * RSS Helper class file. + * + * Simplifies the output of RSS feeds. + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.helpers + * @since CakePHP(tm) v 1.2 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +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 + */ +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 + */ + 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 /> + */ + 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 + */ + 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 + */ + 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(); + 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] = join('', $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; + } + $escape = true; + if (is_array($val) && isset($val['convertEntities'])) { + $escape = $val['convertEntities']; + unset($val['convertEntities']); + } + if (!is_null($val) && $escape) { + $val = h($val); + } + $elements[$key] = $this->elem($key, $attrib, $val); + } + if (!empty($elements)) { + $content = join('', $elements); + } + return $this->output($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); + } +} +?> \ No newline at end of file diff --git a/cake/libs/view/helpers/session.php b/cake/libs/view/helpers/session.php new file mode 100755 index 0000000..d78170a --- /dev/null +++ b/cake/libs/view/helpers/session.php @@ -0,0 +1,202 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Short description for file. + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.helpers + * @since CakePHP(tm) v 1.1.7.3328 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +if (!class_exists('cakesession')) { + uses('session'); +} + +/** + * Session Helper. + * + * Session reading from the view. + * + * @package cake + * @subpackage cake.cake.libs.view.helpers + * + */ +class SessionHelper extends CakeSession { +/** + * List of helpers used by this helper + * + * @var array + */ + var $helpers = null; +/** + * 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); + } else { + $this->__active = false; + } + } +/** + * Turn sessions on if 'Session.start' is set to false in core.php + * + * @param string $base + */ + 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 + */ + 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 + */ + 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 + */ + 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 string Will echo the value if $key is set, or false if not set. + * @access public + */ + function flash($key = 'flash') { + if ($this->__active === true && $this->__start()) { + if (parent::check('Message.' . $key)) { + $flash = parent::read('Message.' . $key); + + if ($flash['layout'] == '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['layout'] == '' || $flash['layout'] == null) { + $out = $flash['message']; + } else { + $view =& ClassRegistry::getObject('view'); + list($tmpVars, $tmpTitle) = array($view->viewVars, $view->pageTitle); + list($view->viewVars, $view->pageTitle) = array($flash['params'], ''); + $out = $view->renderLayout($flash['message'], $flash['layout']); + list($view->viewVars, $view->pageTitle) = array($tmpVars, $tmpTitle); + } + echo($out); + parent::del('Message.' . $key); + return true; + } + } + return false; + } +/** + * 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); + } +/** + * Session id + * + * @return string Session id + * @access public + */ + function id() { + return parent::id(); + } +/** + * 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 public + */ + function __start() { + if (!parent::started()) { + parent::start(); + } + return true; + } +} +?> \ No newline at end of file diff --git a/cake/libs/view/helpers/text.php b/cake/libs/view/helpers/text.php new file mode 100755 index 0000000..a3ae2be --- /dev/null +++ b/cake/libs/view/helpers/text.php @@ -0,0 +1,344 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * 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://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.helpers + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * 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 + */ +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. + * + * @param string $text Text to search the phrase in + * @param string $phrase The phrase that will be searched + * @param string $highlighter The piece of html with that the phrase will be highlighted + * @param boolean $considerHtml If true, will ignore any HTML tags, ensuring that only the correct text is highlighted + * @return string The highlighted text + * @access public + */ + function highlight($text, $phrase, $highlighter = '<span class="highlight">\1</span>', $considerHtml = false) { + if (empty($phrase)) { + return $text; + } + + if (is_array($phrase)) { + $replace = array(); + $with = array(); + + foreach ($phrase as $key => $value) { + $key = $value; + $value = $highlighter; + $key = '(' . $key . ')'; + if ($considerHtml) { + $key = '(?![^<]+>)' . $key . '(?![^<]+>)'; + } + $replace[] = '|' . $key . '|iu'; + $with[] = empty($value) ? $highlighter : $value; + } + + return preg_replace($replace, $with, $text); + } else { + $phrase = '(' . $phrase . ')'; + if ($considerHtml) { + $phrase = '(?![^<]+>)' . $phrase . '(?![^<]+>)'; + } + + return preg_replace('|'.$phrase.'|iu', $highlighter, $text); + } + } +/** + * Strips given text of all links (<a href=....) + * + * @param string $text Text + * @return string The text without links + * @access public + */ + 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 $htmlOptions Array of HTML options. + * @return string The text with links + * @access public + */ + function autoLinkUrls($text, $htmlOptions = array()) { + $options = 'array('; + foreach ($htmlOptions as $option => $value) { + $value = var_export($value, true); + $options .= "'$option' => $value, "; + } + $options .= ')'; + + $text = preg_replace_callback('#(?<!href="|">)((?:http|https|ftp|nntp)://[^ <]+)#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://" . strtolower($matches[0]),' . $options . ');'), $text); + } +/** + * Adds email links (<a href="mailto:....) to a given text. + * + * @param string $text Text + * @param array $htmlOptions Array of HTML options. + * @return string The text with links + * @access public + */ + function autoLinkEmails($text, $htmlOptions = array()) { + $options = 'array('; + + foreach ($htmlOptions as $option => $value) { + $options .= "'$option' => '$value', "; + } + $options .= ')'; + + 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],' . $options . ');'), $text); + } +/** + * Convert all links and email adresses to HTML links. + * + * @param string $text Text + * @param array $htmlOptions Array of HTML options. + * @return string The text with links + * @access public + */ + function autoLink($text, $htmlOptions = array()) { + return $this->autoLinkEmails($this->autoLinkUrls($text, $htmlOptions), $htmlOptions); + } +/** + * 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. + * + * @param string $text String to truncate. + * @param integer $length Length of returned string, including ellipsis. + * @param mixed $ending If string, will be used as Ending and appended to the trimmed string. Can also be an associative array that can contain the last three params of this method. + * @param boolean $exact If false, $text will not be cut mid-word + * @param boolean $considerHtml If true, HTML tags would be handled correctly + * @return string Trimmed string. + */ + function truncate($text, $length = 100, $ending = '...', $exact = true, $considerHtml = false) { + if (is_array($ending)) { + extract($ending); + } + if ($considerHtml) { + if (mb_strlen(preg_replace('/<.*?>/', '', $text)) <= $length) { + return $text; + } + $totalLength = mb_strlen($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 - strlen($ending)); + } + } + if (!$exact) { + $spacepos = mb_strrpos($truncate, ' '); + if (isset($spacepos)) { + if ($considerHtml) { + $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 ($considerHtml) { + foreach ($openTags as $tag) { + $truncate .= '</'.$tag.'>'; + } + } + + return $truncate; + } +/** + * Alias for truncate(). + * + * @see TextHelper::truncate() + * @access public + */ + function trim() { + $args = func_get_args(); + return call_user_func_array(array(&$this, 'truncate'), $args); + } +/** + * 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 + */ + function excerpt($text, $phrase, $radius = 100, $ending = "...") { + if (empty($text) or empty($phrase)) { + return $this->truncate($text, $radius * 2, $ending); + } + + $phraseLen = strlen($phrase); + if ($radius < $phraseLen) { + $radius = $phraseLen; + } + + $pos = strpos(strtolower($text), strtolower($phrase)); + + $startPos = 0; + if ($pos > $radius) { + $startPos = $pos - $radius; + } + + $textLen = strlen($text); + + $endPos = $pos + $phraseLen + $radius; + if ($endPos >= $textLen) { + $endPos = $textLen; + } + + $excerpt = 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 + * @return string + * @access public + */ + function toList($list, $and = 'and') { + $r = ''; + $c = count($list) - 1; + foreach ($list as $i => $item) { + $r .= $item; + if ($c > 0 && $i < $c) + { + $r .= ($i < $c - 1 ? ', ' : " {$and} "); + } + } + return $r; + } +/** + * Text-to-html parser, similar to Textile or RedCloth, only with a little different syntax. + * + * @param string $text String to "flay" + * @param boolean $allowHtml Set to true if if html is allowed + * @return string "Flayed" text + * @access public + * @todo Change this. We need a real Textile parser. + * @codeCoverageIgnoreStart + */ + function flay($text, $allowHtml = false) { + trigger_error(__('(TextHelper::flay) Deprecated: the Flay library is no longer supported and will be removed in a future version.', true), E_USER_WARNING); + if (!class_exists('Flay')) { + uses('flay'); + } + return Flay::toHtml($text, false, $allowHtml); + } +/** + * @codeCoverageIgnoreEnd + */ +} +?> \ No newline at end of file diff --git a/cake/libs/view/helpers/time.php b/cake/libs/view/helpers/time.php new file mode 100755 index 0000000..62792d9 --- /dev/null +++ b/cake/libs/view/helpers/time.php @@ -0,0 +1,538 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Time Helper class file. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.helpers + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Time Helper class for easy use of time data. + * + * Manipulation of time data. + * + * @package cake + * @subpackage cake.cake.libs.view.helpers + */ +class TimeHelper extends AppHelper { +/** + * 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 + */ + 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 + */ + 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 + */ + 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); + } + 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 + */ + function nice($dateString = null, $userOffset = null) { + if ($dateString != null) { + $date = $this->fromString($dateString, $userOffset); + } else { + $date = time(); + } + + $ret = date("D, M jS Y, H:i", $date); + return $this->output($ret); + } +/** + * 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 + */ + 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), date("H:i", $date)); + } elseif ($this->wasYesterday($date)) { + $ret = sprintf(__('Yesterday, %s',true), date("H:i", $date)); + } else { + $ret = date("M jS{$y}, H:i", $date); + } + + return $this->output($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. + */ + 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'; + + $ret ="($fieldName >= '$begin') AND ($fieldName <= '$end')"; + return $this->output($ret); + } +/** + * 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. + */ + function dayAsSql($dateString, $fieldName, $userOffset = null) { + $date = $this->fromString($dateString, $userOffset); + $ret = $this->daysAsSql($dateString, $dateString, $fieldName); + return $this->output($ret); + } +/** + * 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 + */ + 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 + */ + 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 + */ + 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 + */ + 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 + */ + 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 + */ + function isTomorrow($dateString, $userOffset = null) { + $date = $this->fromString($dateString, $userOffset); + return date('Y-m-d', $date) == date('Y-m-d', strtotime('tomorrow')); + } +/** + * Returns the quart + * @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 + */ + 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 $this->output($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 + */ + function toUnix($dateString, $userOffset = null) { + $ret = $this->fromString($dateString, $userOffset); + return $this->output($ret); + } +/** + * 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 + */ + function toAtom($dateString, $userOffset = null) { + $date = $this->fromString($dateString, $userOffset); + $ret = date('Y-m-d\TH:i:s\Z', $date); + return $this->output($ret); + } +/** + * 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 + */ + function toRSS($dateString, $userOffset = null) { + $date = $this->fromString($dateString, $userOffset); + $ret = date("r", $date); + return $this->output($ret); + } +/** + * 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. + */ + 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 $this->output($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 + */ + 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 + */ + 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 + */ + 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); + return $return; + } +/** + * Returns a UNIX timestamp, given either a UNIX timestamp or a valid strtotime() date string. + * + * @param string $format date format string. defaults to 'd-m-Y' + * @param string $dateString Datetime 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 + */ + function format($format = 'd-m-Y', $date, $invalid = false, $userOffset = null) { + $date = $this->fromString($date, $userOffset); + if ($date === false && $invalid !== false) { + return $invalid; + } + return date($format, $date); + } +} +?> \ No newline at end of file diff --git a/cake/libs/view/helpers/xml.php b/cake/libs/view/helpers/xml.php new file mode 100755 index 0000000..6377a52 --- /dev/null +++ b/cake/libs/view/helpers/xml.php @@ -0,0 +1,163 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * XML Helper class file. + * + * Simplifies the output of XML documents. + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.helpers + * @since CakePHP(tm) v 1.2 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +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 + */ +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 + */ + 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->output($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() + */ + 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 + */ + 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) && isset($content['value'])) { + $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 $this->output($out); + } +/** + * Create closing tag for current element + * + * @return string + */ + function closeElem() { + $name = $this->Xml->name(); + if ($parent =& $this->Xml->parent()) { + $this->Xml =& $parent; + } + return $this->output('</' . $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 + * XmlNode::__construct(). + * @return string A copy of $data in XML format + * @see XmlNode + */ + function serialize($data, $options = array()) { + $options += array('attributes' => false, 'format' => 'attributes'); + $data =& new Xml($data, $options); + return $data->toString($options + array('header' => false)); + } +} + +?> \ No newline at end of file diff --git a/cake/libs/view/layouts/ajax.ctp b/cake/libs/view/layouts/ajax.ctp new file mode 100755 index 0000000..ca3459a --- /dev/null +++ b/cake/libs/view/layouts/ajax.ctp @@ -0,0 +1,25 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.layouts + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<?php echo $content_for_layout; ?> \ No newline at end of file diff --git a/cake/libs/view/layouts/default.ctp b/cake/libs/view/layouts/default.ctp new file mode 100755 index 0000000..3b6fac0 --- /dev/null +++ b/cake/libs/view/layouts/default.ctp @@ -0,0 +1,64 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.layouts + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <?php echo $html->charset(); ?> + <title> + <?php __('CakePHP: the rapid development php framework:'); ?> + <?php echo $title_for_layout; ?> + </title> + <?php + echo $html->meta('icon'); + + echo $html->css('cake.generic'); + + echo $scripts_for_layout; + ?> +</head> +<body> + <div id="container"> + <div id="header"> + <h1><?php echo $html->link(__('CakePHP: the rapid development php framework', true), 'http://cakephp.org'); ?></h1> + </div> + <div id="content"> + + <?php $session->flash(); ?> + + <?php echo $content_for_layout; ?> + + </div> + <div id="footer"> + <?php echo $html->link( + $html->image('cake.power.gif', array('alt'=> __("CakePHP: the rapid development php framework", true), 'border'=>"0")), + 'http://www.cakephp.org/', + array('target'=>'_blank'), null, false + ); + ?> + </div> + </div> + <?php echo $cakeDebug; ?> +</body> +</html> \ No newline at end of file diff --git a/cake/libs/view/layouts/email/html/default.ctp b/cake/libs/view/layouts/email/html/default.ctp new file mode 100755 index 0000000..a41315a --- /dev/null +++ b/cake/libs/view/layouts/email/html/default.ctp @@ -0,0 +1,35 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.layouts.email.html + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"> +<html> +<head> + <title><?php echo $title_for_layout;?></title> +</head> +<body> + <?php echo $content_for_layout;?> + + <p>This email was sent using the <a href="http://cakephp.org">CakePHP Framework</a></p> +</body> +</html> \ No newline at end of file diff --git a/cake/libs/view/layouts/email/text/default.ctp b/cake/libs/view/layouts/email/text/default.ctp new file mode 100755 index 0000000..4b0b62c --- /dev/null +++ b/cake/libs/view/layouts/email/text/default.ctp @@ -0,0 +1,28 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.layouts.email.text + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<?php echo $content_for_layout;?> + +This email was sent using the CakePHP Framework, http://cakephp.org. + diff --git a/cake/libs/view/layouts/flash.ctp b/cake/libs/view/layouts/flash.ctp new file mode 100755 index 0000000..d896896 --- /dev/null +++ b/cake/libs/view/layouts/flash.ctp @@ -0,0 +1,43 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.layouts + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<?php echo $html->charset(); ?> +<title><?php echo $page_title; ?></title> + +<?php if (Configure::read() == 0) { ?> +<meta http-equiv="Refresh" content="<?php echo $pause; ?>;url=<?php echo $url; ?>"/> +<?php } ?> +<style><!-- +P { text-align:center; font:bold 1.1em sans-serif } +A { color:#444; text-decoration:none } +A:HOVER { text-decoration: underline; color:#44E } +--></style> +</head> +<body> +<p><a href="<?php echo $url; ?>"><?php echo $message; ?></a></p> +</body> +</html> \ No newline at end of file diff --git a/cake/libs/view/layouts/js/default.ctp b/cake/libs/view/layouts/js/default.ctp new file mode 100755 index 0000000..d94dc90 --- /dev/null +++ b/cake/libs/view/layouts/js/default.ctp @@ -0,0 +1,2 @@ +<?php echo $scripts_for_layout; ?> +<script type="text/javascript"><?php echo $content_for_layout; ?></script> \ No newline at end of file diff --git a/cake/libs/view/layouts/rss/default.ctp b/cake/libs/view/layouts/rss/default.ctp new file mode 100755 index 0000000..518d975 --- /dev/null +++ b/cake/libs/view/layouts/rss/default.ctp @@ -0,0 +1,16 @@ +<?php +echo $rss->header(); + +if (!isset($channel)) { + $channel = array(); +} +if (!isset($channel['title'])) { + $channel['title'] = $title_for_layout; +} + +echo $rss->document( + $rss->channel( + array(), $channel, $content_for_layout + ) +); +?> \ No newline at end of file diff --git a/cake/libs/view/layouts/xml/default.ctp b/cake/libs/view/layouts/xml/default.ctp new file mode 100755 index 0000000..566ca21 --- /dev/null +++ b/cake/libs/view/layouts/xml/default.ctp @@ -0,0 +1,2 @@ +<?php echo $xml->header(); ?> +<?php echo $content_for_layout; ?> \ No newline at end of file diff --git a/cake/libs/view/media.php b/cake/libs/view/media.php new file mode 100755 index 0000000..5be8378 --- /dev/null +++ b/cake/libs/view/media.php @@ -0,0 +1,249 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Methods to display or download any type of file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view + * @since CakePHP(tm) v 1.2.0.5714 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +class MediaView extends View { +/** + * Holds known mime type mappings + * + * @var array + * @access public + */ + var $mimeType = array('ai' => 'application/postscript', 'bcpio' => 'application/x-bcpio', 'bin' => 'application/octet-stream', + 'ccad' => 'application/clariscad', 'cdf' => 'application/x-netcdf', 'class' => 'application/octet-stream', + 'cpio' => 'application/x-cpio', 'cpt' => 'application/mac-compactpro', 'csh' => 'application/x-csh', + 'csv' => 'application/csv', 'dcr' => 'application/x-director', 'dir' => 'application/x-director', + 'dms' => 'application/octet-stream', 'doc' => 'application/msword', 'drw' => 'application/drafting', + 'dvi' => 'application/x-dvi', 'dwg' => 'application/acad', 'dxf' => 'application/dxf', 'dxr' => 'application/x-director', + 'eps' => 'application/postscript', 'exe' => 'application/octet-stream', 'ez' => 'application/andrew-inset', + 'flv' => 'video/x-flv', 'gtar' => 'application/x-gtar', 'gz' => 'application/x-gzip', + 'bz2' => 'application/x-bzip', '7z' => 'application/x-7z-compressed', 'hdf' => 'application/x-hdf', + 'hqx' => 'application/mac-binhex40', 'ips' => 'application/x-ipscript', 'ipx' => 'application/x-ipix', + 'js' => 'application/x-javascript', 'latex' => 'application/x-latex', 'lha' => 'application/octet-stream', + 'lsp' => 'application/x-lisp', 'lzh' => 'application/octet-stream', 'man' => 'application/x-troff-man', + 'me' => 'application/x-troff-me', 'mif' => 'application/vnd.mif', 'ms' => 'application/x-troff-ms', + 'nc' => 'application/x-netcdf', 'oda' => 'application/oda', 'pdf' => 'application/pdf', + 'pgn' => 'application/x-chess-pgn', 'pot' => 'application/mspowerpoint', 'pps' => 'application/mspowerpoint', + 'ppt' => 'application/mspowerpoint', 'ppz' => 'application/mspowerpoint', 'pre' => 'application/x-freelance', + 'prt' => 'application/pro_eng', 'ps' => 'application/postscript', 'roff' => 'application/x-troff', + 'scm' => 'application/x-lotusscreencam', 'set' => 'application/set', 'sh' => 'application/x-sh', + 'shar' => 'application/x-shar', 'sit' => 'application/x-stuffit', 'skd' => 'application/x-koan', + 'skm' => 'application/x-koan', 'skp' => 'application/x-koan', 'skt' => 'application/x-koan', + 'smi' => 'application/smil', 'smil' => 'application/smil', 'sol' => 'application/solids', + 'spl' => 'application/x-futuresplash', 'src' => 'application/x-wais-source', 'step' => 'application/STEP', + 'stl' => 'application/SLA', 'stp' => 'application/STEP', 'sv4cpio' => 'application/x-sv4cpio', + 'sv4crc' => 'application/x-sv4crc', 'svg' => 'image/svg+xml', 'svgz' => 'image/svg+xml', + 'swf' => 'application/x-shockwave-flash', 't' => 'application/x-troff', + 'tar' => 'application/x-tar', 'tcl' => 'application/x-tcl', 'tex' => 'application/x-tex', + 'texi' => 'application/x-texinfo', 'texinfo' => 'application/x-texinfo', 'tr' => 'application/x-troff', + 'tsp' => 'application/dsptype', 'unv' => 'application/i-deas', 'ustar' => 'application/x-ustar', + 'vcd' => 'application/x-cdlink', 'vda' => 'application/vda', 'xlc' => 'application/vnd.ms-excel', + 'xll' => 'application/vnd.ms-excel', 'xlm' => 'application/vnd.ms-excel', 'xls' => 'application/vnd.ms-excel', + 'xlw' => 'application/vnd.ms-excel', 'zip' => 'application/zip', 'aif' => 'audio/x-aiff', 'aifc' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', 'au' => 'audio/basic', 'kar' => 'audio/midi', 'mid' => 'audio/midi', + 'midi' => 'audio/midi', 'mp2' => 'audio/mpeg', 'mp3' => 'audio/mpeg', 'mpga' => 'audio/mpeg', + 'ra' => 'audio/x-realaudio', 'ram' => 'audio/x-pn-realaudio', 'rm' => 'audio/x-pn-realaudio', + 'rpm' => 'audio/x-pn-realaudio-plugin', 'snd' => 'audio/basic', 'tsi' => 'audio/TSP-audio', 'wav' => 'audio/x-wav', + 'asc' => 'text/plain', 'c' => 'text/plain', 'cc' => 'text/plain', 'css' => 'text/css', 'etx' => 'text/x-setext', + 'f' => 'text/plain', 'f90' => 'text/plain', 'h' => 'text/plain', 'hh' => 'text/plain', 'htm' => 'text/html', + 'html' => 'text/html', 'm' => 'text/plain', 'rtf' => 'text/rtf', 'rtx' => 'text/richtext', 'sgm' => 'text/sgml', + 'sgml' => 'text/sgml', 'tsv' => 'text/tab-separated-values', 'tpl' => 'text/template', 'txt' => 'text/plain', + 'xml' => 'text/xml', 'avi' => 'video/x-msvideo', 'fli' => 'video/x-fli', 'mov' => 'video/quicktime', + 'movie' => 'video/x-sgi-movie', 'mpe' => 'video/mpeg', 'mpeg' => 'video/mpeg', 'mpg' => 'video/mpeg', + 'qt' => 'video/quicktime', 'viv' => 'video/vnd.vivo', 'vivo' => 'video/vnd.vivo', 'gif' => 'image/gif', + 'ief' => 'image/ief', 'jpe' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'jpg' => 'image/jpeg', + 'pbm' => 'image/x-portable-bitmap', 'pgm' => 'image/x-portable-graymap', 'png' => 'image/png', + 'pnm' => 'image/x-portable-anymap', 'ppm' => 'image/x-portable-pixmap', 'ras' => 'image/cmu-raster', + 'rgb' => 'image/x-rgb', 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'xbm' => 'image/x-xbitmap', + 'xpm' => 'image/x-xpixmap', 'xwd' => 'image/x-xwindowdump', 'ice' => 'x-conference/x-cooltalk', + 'iges' => 'model/iges', 'igs' => 'model/iges', 'mesh' => 'model/mesh', 'msh' => 'model/mesh', + 'silo' => 'model/mesh', 'vrml' => 'model/vrml', 'wrl' => 'model/vrml', + 'mime' => 'www/mime', 'pdb' => 'chemical/x-pdb', 'xyz' => 'chemical/x-pdb'); +/** + * Holds headers sent to browser before rendering media + * + * @var array + * @access protected + */ + var $_headers = array(); +/** + * Constructor + * + * @param object $controller + */ + function __construct(&$controller) { + parent::__construct($controller); + } +/** + * Display or download the given file + * + * @return unknown + */ + function render() { + $name = $download = $extension = $id = $modified = $path = $size = $cache = $mimeType = null; + extract($this->viewVars, EXTR_OVERWRITE); + + if ($size) { + $id = $id . '_' . $size; + } + + if (is_dir($path)) { + $path = $path . $id; + } else { + $path = APP . $path . $id; + } + + if (!file_exists($path)) { + header('Content-Type: text/html'); + $this->cakeError('error404'); + } + + if (is_null($name)) { + $name = $id; + } + + if (is_array($mimeType)) { + $this->mimeType = array_merge($this->mimeType, $mimeType); + } + + if (isset($extension) && isset($this->mimeType[$extension]) && connection_status() == 0) { + $chunkSize = 8192; + $buffer = ''; + $fileSize = @filesize($path); + $handle = fopen($path, 'rb'); + + if ($handle === false) { + return false; + } + if (!empty($modified)) { + $modified = gmdate('D, d M Y H:i:s', strtotime($modified, time())) . ' GMT'; + } else { + $modified = gmdate('D, d M Y H:i:s') . ' GMT'; + } + + if ($download) { + $contentTypes = array('application/octet-stream'); + $agent = env('HTTP_USER_AGENT'); + + if (preg_match('%Opera(/| )([0-9].[0-9]{1,2})%', $agent)) { + $contentTypes[0] = 'application/octetstream'; + } else if (preg_match('/MSIE ([0-9].[0-9]{1,2})/', $agent)) { + $contentTypes[0] = 'application/force-download'; + array_push($contentTypes, array( + 'application/octet-stream', + 'application/download' + )); + } + foreach($contentTypes as $contentType) { + $this->_header('Content-Type: ' . $contentType); + } + $this->_header(array( + 'Content-Disposition: attachment; filename="' . $name . '.' . $extension . '";', + 'Expires: 0', + 'Accept-Ranges: bytes', + 'Cache-Control: private' => false, + 'Pragma: private')); + + $httpRange = env('HTTP_RANGE'); + if (isset($httpRange)) { + list($toss, $range) = explode('=', $httpRange); + + $size = $fileSize - 1; + $length = $fileSize - $range; + + $this->_header(array( + 'HTTP/1.1 206 Partial Content', + 'Content-Length: ' . $length, + 'Content-Range: bytes ' . $range . $size . '/' . $fileSize)); + + fseek($handle, $range); + } else { + $this->_header('Content-Length: ' . $fileSize); + } + } else { + $this->_header('Date: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT'); + if ($cache) { + if (!is_numeric($cache)) { + $cache = strtotime($cache) - time(); + } + $this->_header(array( + 'Cache-Control: max-age=' . $cache, + 'Expires: ' . gmdate('D, d M Y H:i:s', time() + $cache) . ' GMT', + 'Pragma: cache')); + } else { + $this->_header(array( + 'Cache-Control: must-revalidate, post-check=0, pre-check=0', + 'Pragma: no-cache')); + } + $this->_header(array( + 'Last-Modified: ' . $modified, + 'Content-Type: ' . $this->mimeType[$extension], + 'Content-Length: ' . $fileSize)); + } + $this->_output(); + @ob_end_clean(); + + while (!feof($handle) && connection_status() == 0 && !connection_aborted()) { + set_time_limit(0); + $buffer = fread($handle, $chunkSize); + echo $buffer; + @flush(); + @ob_flush(); + } + fclose($handle); + exit(0); + } + return false; + } +/** + * Method to set headers + * @param mixed $header + * @param boolean $boolean + * @access protected + */ + function _header($header, $boolean = true) { + if (is_array($header)) { + foreach ($header as $string => $boolean) { + if (is_numeric($string)) { + $this->_headers[] = array($boolean => true); + } else { + $this->_headers[] = array($string => $boolean); + } + } + return; + } + $this->_headers[] = array($header => $boolean); + return; + } +/** + * Method to output headers + * @access protected + */ + function _output() { + foreach ($this->_headers as $key => $value) { + $header = key($value); + header($header, $value[$header]); + } + } +} +?> \ No newline at end of file diff --git a/cake/libs/view/pages/home.ctp b/cake/libs/view/pages/home.ctp new file mode 100755 index 0000000..f0e3d9c --- /dev/null +++ b/cake/libs/view/pages/home.ctp @@ -0,0 +1,147 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.pages + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +if (Configure::read() == 0): + $this->cakeError('error404'); +endif; +?> +<h2><?php echo sprintf(__('Release Notes for CakePHP %s.', true), Configure::version()); ?></h2> +<a href="https://trac.cakephp.org/wiki/changelog/1.2.x.x"><?php __('Read the changelog'); ?> </a> +<?php +if (Configure::read() > 0): + Debugger::checkSessionKey(); +endif; +?> +<p> + <?php + if (is_writable(TMP)): + echo '<span class="notice success">'; + __('Your tmp directory is writable.'); + echo '</span>'; + else: + echo '<span class="notice">'; + __('Your tmp directory is NOT writable.'); + echo '</span>'; + endif; + ?> +</p> +<p> + <?php + $settings = Cache::settings(); + if (!empty($settings)): + echo '<span class="notice success">'; + echo sprintf(__('The %s is being used for caching. To change the config edit APP/config/core.php ', true), '<em>'. $settings['engine'] . 'Engine</em>'); + echo '</span>'; + else: + echo '<span class="notice">'; + __('Your cache is NOT working. Please check the settings in APP/config/core.php'); + echo '</span>'; + endif; + ?> +</p> +<p> + <?php + $filePresent = null; + if (file_exists(CONFIGS.'database.php')): + echo '<span class="notice success">'; + __('Your database configuration file is present.'); + $filePresent = true; + echo '</span>'; + else: + echo '<span class="notice">'; + __('Your database configuration file is NOT present.'); + echo '<br/>'; + __('Rename config/database.php.default to config/database.php'); + echo '</span>'; + endif; + ?> +</p> +<?php +if (isset($filePresent)): + uses('model' . DS . 'connection_manager'); + $db = ConnectionManager::getInstance(); + @$connected = $db->getDataSource('default'); +?> +<p> + <?php + if ($connected->isConnected()): + echo '<span class="notice success">'; + __('Cake is able to connect to the database.'); + echo '</span>'; + else: + echo '<span class="notice">'; + __('Cake is NOT able to connect to the database.'); + echo '</span>'; + endif; + ?> +</p> +<?php endif;?> +<h3><?php __('Editing this Page'); ?></h3> +<p> +<?php +__('To change the content of this page, create: APP/views/pages/home.ctp.<br /> +To change its layout, create: APP/views/layouts/default.ctp.<br /> +You can also add some CSS styles for your pages at: APP/webroot/css.'); +?> +</p> +<h3><?php __('Getting Started'); ?></h3> +<p> + <a href="http://book.cakephp.org"><strong>new</strong> CakePHP 1.2 Docs</a> +</p> +<p> + <a href="http://book.cakephp.org/view/219/the-cakephp-blog-tutorial"><?php __('The 15 min Blog Tutorial'); ?></a><br /> +</p> +<h3><?php __('More about Cake'); ?></h3> +<p> +<?php __('CakePHP is a rapid development framework for PHP which uses commonly known design patterns like Active Record, Association Data Mapping, Front Controller and MVC.'); ?> +</p> +<p> +<?php __('Our primary goal is to provide a structured framework that enables PHP users at all levels to rapidly develop robust web applications, without any loss to flexibility.'); ?> +</p> +<br /> +<ul> + <li><a href="http://www.cakefoundation.org/"><?php __('Cake Software Foundation'); ?> </a> + <ul><li><?php __('Promoting development related to CakePHP'); ?></li></ul></li> + <li><a href="http://www.cakephp.org"><?php __('CakePHP'); ?> </a> + <ul><li><?php __('The Rapid Development Framework'); ?></li></ul></li> + <li><a href="http://book.cakephp.org"><?php __('CakePHP Documentation'); ?> </a> + <ul><li><?php __('Your Rapid Development Cookbook'); ?></li></ul></li> + <li><a href="http://api.cakephp.org"><?php __('CakePHP API'); ?> </a> + <ul><li><?php __('Quick Reference'); ?></li></ul></li> + <li><a href="http://bakery.cakephp.org"><?php __('The Bakery'); ?> </a> + <ul><li><?php __('Everything CakePHP'); ?></li></ul></li> + <li><a href="http://live.cakephp.org"><?php __('The Show'); ?> </a> + <ul><li><?php __('The Show is a live and archived internet radio broadcast CakePHP-related topics and answer questions live via IRC, Skype, and telephone.'); ?></li></ul></li> + <li><a href="http://groups.google.com/group/cake-php"><?php __('CakePHP Google Group'); ?> </a> + <ul><li><?php __('Community mailing list'); ?></li></ul></li> + <li><a href="irc://irc.freenode.net/cakephp">irc.freenode.net #cakephp</a> + <ul><li><?php __('Live chat about CakePHP'); ?></li></ul></li> + <li><a href="https://trac.cakephp.org/"><?php __('CakePHP Trac'); ?> </a> + <ul><li><?php __('For the Development of CakePHP (Tickets, SVN browser, Roadmap, Changelogs)'); ?></li></ul></li> + <li><a href="http://www.cakeforge.org"><?php __('CakeForge'); ?> </a> + <ul><li><?php __('Open Development for CakePHP'); ?></li></ul></li> + <li><a href="http://astore.amazon.com/cakesoftwaref-20/"><?php __('Book Store'); ?> </a> + <ul><li><?php __('Recommended Software Books'); ?></li></ul></li> + <li><a href="http://www.cafepress.com/cakefoundation"><?php __('CakePHP gear'); ?> </a> + <ul><li><?php __('Get your own CakePHP gear - Doughnate to Cake'); ?></li></ul></li> +</ul> \ No newline at end of file diff --git a/cake/libs/view/scaffolds/edit.ctp b/cake/libs/view/scaffolds/edit.ctp new file mode 100755 index 0000000..6705b4b --- /dev/null +++ b/cake/libs/view/scaffolds/edit.ctp @@ -0,0 +1,51 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.scaffolds + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<div class="<?php echo $pluralVar;?> form"> +<?php + echo $form->create(); + echo $form->inputs($scaffoldFields, array('created', 'modified', 'updated')); + echo $form->end(__('Submit', true)); +?> +</div> +<div class="actions"> + <ul> +<?php if ($this->action != 'add'):?> + <li><?php echo $html->link(__('Delete', true), array('action' => 'delete', $form->value($modelClass.'.'.$primaryKey)), null, __('Are you sure you want to delete', true).' #' . $form->value($modelClass.'.'.$primaryKey)); ?></li> +<?php endif;?> + <li><?php echo $html->link(__('List', true).' '.$pluralHumanName, array('action' => 'index'));?></li> +<?php + $done = array(); + foreach ($associations as $_type => $_data) { + foreach ($_data as $_alias => $_details) { + if ($_details['controller'] != $this->name && !in_array($_details['controller'], $done)) { + echo "\t\t<li>" . $html->link(sprintf(__('List %s', true), Inflector::humanize($_details['controller'])), array('controller' => $_details['controller'], 'action' =>'index')) . "</li>\n"; + echo "\t\t<li>" . $html->link(sprintf(__('New %s', true), Inflector::humanize(Inflector::underscore($_alias))), array('controller' => $_details['controller'], 'action' =>'add')) . "</li>\n"; + $done[] = $_details['controller']; + } + } + } +?> + </ul> +</div> \ No newline at end of file diff --git a/cake/libs/view/scaffolds/index.ctp b/cake/libs/view/scaffolds/index.ctp new file mode 100755 index 0000000..c5b9e0e --- /dev/null +++ b/cake/libs/view/scaffolds/index.ctp @@ -0,0 +1,97 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.console.libs.templates.views + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<div class="<?php echo $pluralVar;?> index"> +<h2><?php echo $pluralHumanName;?></h2> +<p><?php +echo $paginator->counter(array( + 'format' => 'Page %page% of %pages%, showing %current% records out of %count% total, starting on record %start%, ending on %end%' +)); +?></p> +<table cellpadding="0" cellspacing="0"> +<tr> +<?php foreach ($scaffoldFields as $_field):?> + <th><?php echo $paginator->sort($_field);?></th> +<?php endforeach;?> + <th><?php __('Actions');?></th> +</tr> +<?php +$i = 0; +foreach (${$pluralVar} as ${$singularVar}): + $class = null; + if ($i++ % 2 == 0) { + $class = ' class="altrow"'; + } +echo "\n"; + echo "\t<tr{$class}>\n"; + foreach ($scaffoldFields as $_field) { + $isKey = false; + if (!empty($associations['belongsTo'])) { + foreach ($associations['belongsTo'] as $_alias => $_details) { + if ($_field === $_details['foreignKey']) { + $isKey = true; + echo "\t\t<td>\n\t\t\t" . $html->link(${$singularVar}[$_alias][$_details['displayField']], array('controller' => $_details['controller'], 'action' => 'view', ${$singularVar}[$_alias][$_details['primaryKey']])) . "\n\t\t</td>\n"; + break; + } + } + } + if ($isKey !== true) { + echo "\t\t<td>\n\t\t\t" . ${$singularVar}[$modelClass][$_field] . " \n\t\t</td>\n"; + } + } + + echo "\t\t<td class=\"actions\">\n"; + echo "\t\t\t" . $html->link(__('View', true), array('action' => 'view', ${$singularVar}[$modelClass][$primaryKey])) . "\n"; + echo "\t\t\t" . $html->link(__('Edit', true), array('action' => 'edit', ${$singularVar}[$modelClass][$primaryKey])) . "\n"; + echo "\t\t\t" . $html->link(__('Delete', true), array('action' => 'delete', ${$singularVar}[$modelClass][$primaryKey]), null, __('Are you sure you want to delete', true).' #' . ${$singularVar}[$modelClass][$primaryKey]) . "\n"; + echo "\t\t</td>\n"; + echo "\t</tr>\n"; + +endforeach; +echo "\n"; +?> +</table> +</div> +<div class="paging"> +<?php echo "\t" . $paginator->prev('<< ' . __('previous', true), array(), null, array('class' => 'disabled')) . "\n";?> + | <?php echo $paginator->numbers() . "\n"?> +<?php echo "\t ". $paginator->next(__('next', true) .' >>', array(), null, array('class' => 'disabled')) . "\n";?> +</div> +<div class="actions"> + <ul> + <li><?php echo $html->link('New '.$singularHumanName, array('action' => 'add')); ?></li> +<?php + $done = array(); + foreach ($associations as $_type => $_data) { + foreach ($_data as $_alias => $_details) { + if ($_details['controller'] != $this->name && !in_array($_details['controller'], $done)) { + echo "\t\t<li>" . $html->link(sprintf(__('List %s', true), Inflector::humanize($_details['controller'])), array('controller' => $_details['controller'], 'action' => 'index')) . "</li>\n"; + echo "\t\t<li>" . $html->link(sprintf(__('New %s', true), Inflector::humanize(Inflector::underscore($_alias))), array('controller' => $_details['controller'], 'action' => 'add')) . "</li>\n"; + $done[] = $_details['controller']; + } + } + } +?> + </ul> +</div> \ No newline at end of file diff --git a/cake/libs/view/scaffolds/view.ctp b/cake/libs/view/scaffolds/view.ctp new file mode 100755 index 0000000..a497ca6 --- /dev/null +++ b/cake/libs/view/scaffolds/view.ctp @@ -0,0 +1,159 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view.templates.scaffolds + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +?> +<div class="<?php echo $pluralVar;?> view"> +<h2><?php echo sprintf(__("View %s", true), $singularHumanName);?></h2> + <dl> +<?php +$i = 0; +foreach ($scaffoldFields as $_field) { + $class = null; + if ($i++ % 2 == 0) { + $class = ' class="altrow"'; + } + $isKey = false; + if (!empty($associations['belongsTo'])) { + foreach ($associations['belongsTo'] as $_alias => $_details) { + if ($_field === $_details['foreignKey']) { + $isKey = true; + echo "\t\t<dt{$class}>" . Inflector::humanize($_alias) . "</dt>\n"; + echo "\t\t<dd{$class}>\n\t\t\t" . $html->link(${$singularVar}[$_alias][$_details['displayField']], array('controller' => $_details['controller'], 'action' => 'view', ${$singularVar}[$_alias][$_details['primaryKey']])) . "\n\t\t&nbsp;</dd>\n"; + break; + } + } + } + if ($isKey !== true) { + echo "\t\t<dt{$class}>" . Inflector::humanize($_field) . "</dt>\n"; + echo "\t\t<dd{$class}>\n\t\t\t{${$singularVar}[$modelClass][$_field]}\n&nbsp;\t\t</dd>\n"; + } +} +?> + </dl> +</div> +<div class="actions"> + <ul> +<?php + echo "\t\t<li>" .$html->link(sprintf(__('Edit %s', true), $singularHumanName), array('action' => 'edit', ${$singularVar}[$modelClass][$primaryKey])). " </li>\n"; + echo "\t\t<li>" .$html->link(sprintf(__('Delete %s', true), $singularHumanName), array('action' => 'delete', ${$singularVar}[$modelClass][$primaryKey]), null, __('Are you sure you want to delete', true).' #' . ${$singularVar}[$modelClass][$primaryKey] . '?'). " </li>\n"; + echo "\t\t<li>" .$html->link(sprintf(__('List %s', true), $pluralHumanName), array('action' => 'index')). " </li>\n"; + echo "\t\t<li>" .$html->link(sprintf(__('New %s', true), $singularHumanName), array('action' => 'add')). " </li>\n"; + + $done = array(); + foreach ($associations as $_type => $_data) { + foreach ($_data as $_alias => $_details) { + if ($_details['controller'] != $this->name && !in_array($_details['controller'], $done)) { + echo "\t\t<li>" . $html->link(sprintf(__('List %s', true), Inflector::humanize($_details['controller'])), array('controller' => $_details['controller'], 'action' => 'index')) . "</li>\n"; + echo "\t\t<li>" . $html->link(sprintf(__('New %s', true), Inflector::humanize(Inflector::underscore($_alias))), array('controller' => $_details['controller'], 'action' => 'add')) . "</li>\n"; + $done[] = $_details['controller']; + } + } + } +?> + </ul> +</div> +<?php +if (!empty($associations['hasOne'])) : +foreach ($associations['hasOne'] as $_alias => $_details): ?> +<div class="related"> + <h3><?php echo sprintf(__("Related %s", true), Inflector::humanize($_details['controller']));?></h3> +<?php if (!empty(${$singularVar}[$_alias])):?> + <dl> +<?php + $i = 0; + $otherFields = array_keys(${$singularVar}[$_alias]); + foreach ($otherFields as $_field) { + $class = null; + if ($i++ % 2 == 0) { + $class = ' class="altrow"'; + } + echo "\t\t<dt{$class}>" . Inflector::humanize($_field) . "</dt>\n"; + echo "\t\t<dd{$class}>\n\t" . ${$singularVar}[$_alias][$_field] . "\n&nbsp;</dd>\n"; + } +?> + </dl> +<?php endif; ?> + <div class="actions"> + <ul> + <li><?php echo $html->link(sprintf(__('Edit %s', true), Inflector::humanize(Inflector::underscore($_alias))), array('controller' => $_details['controller'], 'action' => 'edit', ${$singularVar}[$_alias][$_details['primaryKey']]))."</li>\n";?> + </ul> + </div> +</div> +<?php +endforeach; +endif; + +if (empty($associations['hasMany'])) { + $associations['hasMany'] = array(); +} +if (empty($associations['hasAndBelongsToMany'])) { + $associations['hasAndBelongsToMany'] = array(); +} +$relations = array_merge($associations['hasMany'], $associations['hasAndBelongsToMany']); +$i = 0; +foreach ($relations as $_alias => $_details): +$otherSingularVar = Inflector::variable($_alias); +?> +<div class="related"> + <h3><?php echo sprintf(__("Related %s", true), Inflector::humanize($_details['controller']));?></h3> +<?php if (!empty(${$singularVar}[$_alias])):?> + <table cellpadding="0" cellspacing="0"> + <tr> +<?php + $otherFields = array_keys(${$singularVar}[$_alias][0]); + foreach ($otherFields as $_field) { + echo "\t\t<th>" . Inflector::humanize($_field) . "</th>\n"; + } +?> + <th class="actions">Actions</th> + </tr> +<?php + $i = 0; + foreach (${$singularVar}[$_alias] as ${$otherSingularVar}): + $class = null; + if ($i++ % 2 == 0) { + $class = ' class="altrow"'; + } + echo "\t\t<tr{$class}>\n"; + + foreach ($otherFields as $_field) { + echo "\t\t\t<td>" . ${$otherSingularVar}[$_field] . "</td>\n"; + } + + echo "\t\t\t<td class=\"actions\">\n"; + echo "\t\t\t\t" . $html->link(__('View', true), array('controller' => $_details['controller'], 'action' => 'view', ${$otherSingularVar}[$_details['primaryKey']])). "\n"; + echo "\t\t\t\t" . $html->link(__('Edit', true), array('controller' => $_details['controller'], 'action' => 'edit', ${$otherSingularVar}[$_details['primaryKey']])). "\n"; + echo "\t\t\t\t" . $html->link(__('Delete', true), array('controller' => $_details['controller'], 'action' => 'delete', ${$otherSingularVar}[$_details['primaryKey']]), null, __('Are you sure you want to delete', true).' #' . ${$otherSingularVar}[$_details['primaryKey']] . '?'). "\n"; + echo "\t\t\t</td>\n"; + echo "\t\t</tr>\n"; + endforeach; +?> + </table> +<?php endif; ?> + <div class="actions"> + <ul> + <li><?php echo $html->link(sprintf(__("New %s", true), Inflector::humanize(Inflector::underscore($_alias))), array('controller' => $_details['controller'], 'action' => 'add'));?> </li> + </ul> + </div> +</div> +<?php endforeach;?> \ No newline at end of file diff --git a/cake/libs/view/theme.php b/cake/libs/view/theme.php new file mode 100755 index 0000000..97e2569 --- /dev/null +++ b/cake/libs/view/theme.php @@ -0,0 +1,95 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * A custom view class that is used for themeing + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Theme view class + * + * @package cake + * @subpackage cake.cake.libs.view + */ +class ThemeView extends View { +/** + * System path to themed element: themed . DS . theme . DS . elements . DS + * + * @var string + */ + var $themeElement = null; +/** + * System path to themed layout: themed . DS . theme . DS . layouts . DS + * + * @var string + */ + var $themeLayout = null; +/** + * System path to themed: themed . DS . theme . DS + * + * @var string + */ + var $themePath = null; +/** + * Enter description here... + * + * @param unknown_type $controller + */ + function __construct (&$controller) { + parent::__construct($controller); + $this->theme =& $controller->theme; + + if (!empty($this->theme)) { + if (is_dir(WWW_ROOT . 'themed' . DS . $this->theme)) { + $this->themeWeb = 'themed/'. $this->theme .'/'; + } + /* deprecated: as of 6128 the following properties are no longer needed */ + $this->themeElement = 'themed'. DS . $this->theme . DS .'elements'. DS; + $this->themeLayout = 'themed'. DS . $this->theme . DS .'layouts'. DS; + $this->themePath = 'themed'. DS . $this->theme . DS; + } + } + +/** + * Return all possible paths to find view files in order + * + * @param string $plugin + * @return array paths + * @access private + */ + function _paths($plugin = null, $cached = true) { + $paths = parent::_paths($plugin, $cached); + + if (!empty($this->theme)) { + $count = count($paths); + for ($i = 0; $i < $count; $i++) { + $themePaths[] = $paths[$i] . 'themed'. DS . $this->theme . DS; + } + $paths = array_merge($themePaths, $paths); + } + + if (empty($this->__paths)) { + $this->__paths = $paths; + } + + return $paths; + } +} +?> \ No newline at end of file diff --git a/cake/libs/view/view.php b/cake/libs/view/view.php new file mode 100755 index 0000000..1ed8c01 --- /dev/null +++ b/cake/libs/view/view.php @@ -0,0 +1,929 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Methods for displaying presentation data in the view. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs.view + * @since CakePHP(tm) v 0.10.0.1076 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +/** + * Included libraries. + */ +App::import('Core', array('Helper', 'ClassRegistry')); +/** + * View, the V in the MVC triad. + * + * Class holding methods for displaying presentation data. + * + * @package cake + * @subpackage cake.cake.libs.view + */ +class View extends Object { +/** + * Path parts for creating links in views. + * + * @var string Base URL + * @access public + */ + var $base = null; +/** + * Stores the current URL (for links etc.) + * + * @var string Current URL + */ + var $here = null; +/** + * Name of the plugin. + * + * @link http://manual.cakephp.org/chapter/plugins + * @var string + */ + var $plugin = null; +/** + * Name of the controller. + * + * @var string Name of controller + * @access public + */ + var $name = null; +/** + * Action to be performed. + * + * @var string Name of action + * @access public + */ + var $action = null; +/** + * Array of parameter data + * + * @var array Parameter data + */ + var $params = array(); +/** + * Current passed params + * + * @var mixed + */ + var $passedArgs = array(); +/** + * Array of data + * + * @var array Parameter data + */ + var $data = array(); +/** + * An array of names of built-in helpers to include. + * + * @var mixed A single name as a string or a list of names as an array. + * @access public + */ + var $helpers = array('Html'); +/** + * Path to View. + * + * @var string Path to View + */ + var $viewPath = null; +/** + * Variables for the view + * + * @var array + * @access public + */ + var $viewVars = array(); +/** + * Name of layout to use with this View. + * + * @var string + * @access public + */ + var $layout = 'default'; +/** + * Path to Layout. + * + * @var string Path to Layout + */ + var $layoutPath = null; +/** + * Title HTML element of this View. + * + * @var string + * @access public + */ + var $pageTitle = false; +/** + * Turns on or off Cake's conventional mode of rendering views. On by default. + * + * @var boolean + * @access public + */ + var $autoRender = true; +/** + * Turns on or off Cake's conventional mode of finding layout files. On by default. + * + * @var boolean + * @access public + */ + var $autoLayout = true; +/** + * File extension. Defaults to Cake's template ".ctp". + * + * @var string + */ + var $ext = '.ctp'; +/** + * Sub-directory for this view file. + * + * @var string + */ + var $subDir = null; +/** + * Theme name. + * + * @var string + */ + var $themeWeb = null; +/** + * Used to define methods a controller that will be cached. + * + * @see Controller::$cacheAction + * @var mixed + * @access public + */ + var $cacheAction = false; +/** + * holds current errors for the model validation + * + * @var array + */ + var $validationErrors = array(); +/** + * True when the view has been rendered. + * + * @var boolean + */ + var $hasRendered = false; +/** + * Array of loaded view helpers. + * + * @var array + */ + var $loaded = array(); +/** + * True if in scope of model-specific region + * + * @var boolean + */ + var $modelScope = false; +/** + * Name of current model this view context is attached to + * + * @var string + */ + var $model = null; +/** + * Name of association model this view context is attached to + * + * @var string + */ + var $association = null; +/** + * Name of current model field this view context is attached to + * + * @var string + */ + var $field = null; +/** + * Suffix of current field this view context is attached to + * + * @var string + */ + var $fieldSuffix = null; +/** + * The current model ID this view context is attached to + * + * @var mixed + */ + var $modelId = null; +/** + * List of generated DOM UUIDs + * + * @var array + */ + var $uuids = array(); +/** + * Holds View output. + * + * @var string + **/ + var $output = false; +/** + * List of variables to collect from the associated controller + * + * @var array + * @access protected + */ + var $__passedVars = array( + 'viewVars', 'action', 'autoLayout', 'autoRender', 'ext', 'base', 'webroot', + 'helpers', 'here', 'layout', 'name', 'pageTitle', 'layoutPath', 'viewPath', + 'params', 'data', 'plugin', 'passedArgs', 'cacheAction' + ); +/** + * Scripts (and/or other <head /> tags) for the layout + * + * @var array + * @access private + */ + var $__scripts = array(); +/** + * Holds an array of paths. + * + * @var array + */ + var $__paths = array(); +/** + * Constructor + * + * @return View + */ + function __construct(&$controller, $register = true) { + if (is_object($controller)) { + $count = count($this->__passedVars); + for ($j = 0; $j < $count; $j++) { + $var = $this->__passedVars[$j]; + $this->{$var} = $controller->{$var}; + } + } + parent::__construct(); + + if ($register) { + ClassRegistry::addObject('view', $this); + } + } +/** + * Renders a piece of PHP with provided parameters and returns HTML, XML, or any other string. + * + * This realizes the concept of Elements, (or "partial layouts") + * and the $params array is used to send data to be used in the + * Element. Elements can be cached through use of the cache key. + * + * @param string $name Name of template file in the/app/views/elements/ folder + * @param array $params Array of data to be made available to the for rendered + * view (i.e. the Element) + * Special params: + * cache - enable caching for this element accepts boolean or strtotime compatible string. + * Can also be an array + * if an array,'time' is used to specify duration of cache. 'key' can be used to + * create unique cache files. + * + * @return string Rendered Element + * @access public + */ + function element($name, $params = array(), $loadHelpers = false) { + $file = $plugin = $key = null; + + if (isset($params['plugin'])) { + $plugin = $params['plugin']; + } + + if (isset($this->plugin) && !$plugin) { + $plugin = $this->plugin; + } + + if (isset($params['cache'])) { + $expires = '+1 day'; + + if (is_array($params['cache'])) { + $expires = $params['cache']['time']; + $key = Inflector::slug($params['cache']['key']); + } elseif ($params['cache'] !== true) { + $expires = $params['cache']; + $key = implode('_', array_keys($params)); + } + + if ($expires) { + $cacheFile = 'element_' . $key . '_' . $plugin . Inflector::slug($name); + $cache = cache('views' . DS . $cacheFile, null, $expires); + + if (is_string($cache)) { + return $cache; + } + } + } + $paths = $this->_paths($plugin); + + foreach ($paths as $path) { + if (file_exists($path . 'elements' . DS . $name . $this->ext)) { + $file = $path . 'elements' . DS . $name . $this->ext; + break; + } elseif (file_exists($path . 'elements' . DS . $name . '.thtml')) { + $file = $path . 'elements' . DS . $name . '.thtml'; + break; + } + } + + if (is_file($file)) { + $params = array_merge_recursive($params, $this->loaded); + $element = $this->_render($file, array_merge($this->viewVars, $params), $loadHelpers); + if (isset($params['cache']) && isset($cacheFile) && isset($expires)) { + cache('views' . DS . $cacheFile, $element, $expires); + } + return $element; + } + $file = $paths[0] . 'elements' . DS . $name . $this->ext; + + if (Configure::read() > 0) { + return "Not Found: " . $file; + } + } +/** + * Renders view for given action and layout. If $file is given, that is used + * for a view filename (e.g. customFunkyView.ctp). + * + * @param string $action Name of action to render for + * @param string $layout Layout to use + * @param string $file Custom filename for view + * @return string Rendered Element + */ + function render($action = null, $layout = null, $file = null) { + if ($this->hasRendered) { + return true; + } + $out = null; + + if ($file != null) { + $action = $file; + } + + if ($action !== false && $viewFileName = $this->_getViewFileName($action)) { + if (substr($viewFileName, -3) === 'ctp' || substr($viewFileName, -5) === 'thtml') { + $out = View::_render($viewFileName, $this->viewVars); + } else { + $out = $this->_render($viewFileName, $this->viewVars); + } + } + + if ($layout === null) { + $layout = $this->layout; + } + + if ($out !== false) { + if ($layout && $this->autoLayout) { + $out = $this->renderLayout($out, $layout); + $isCached = ( + isset($this->loaded['cache']) && + (($this->cacheAction != false)) && (Configure::read('Cache.check') === true) + ); + + if ($isCached) { + $replace = array('<cake:nocache>', '</cake:nocache>'); + $out = str_replace($replace, '', $out); + } + } + $this->hasRendered = true; + } else { + $out = $this->_render($viewFileName, $this->viewVars); + $msg = __("Error in view %s, got: <blockquote>%s</blockquote>", true); + trigger_error(sprintf($msg, $viewFileName, $out), E_USER_ERROR); + } + return $out; + } +/** + * Renders a layout. Returns output from _render(). Returns false on error. + * Several variables are created for use in layout. + * title_for_layout - contains page title + * content_for_layout - contains rendered view file + * scripts_for_layout - contains scripts added to header + * cakeDebug - if debug is on, cake debug information is added. + * + * @param string $content_for_layout Content to render in a view, wrapped by the surrounding layout. + * @return mixed Rendered output, or false on error + */ + function renderLayout($content_for_layout, $layout = null) { + $layoutFileName = $this->_getLayoutFileName($layout); + if (empty($layoutFileName)) { + return $this->output; + } + + $debug = ''; + + if (isset($this->viewVars['cakeDebug']) && Configure::read() > 2) { + $params = array('controller' => $this->viewVars['cakeDebug']); + $debug = View::element('dump', $params, false); + unset($this->viewVars['cakeDebug']); + } + + if ($this->pageTitle !== false) { + $pageTitle = $this->pageTitle; + } else { + $pageTitle = Inflector::humanize($this->viewPath); + } + $data_for_layout = array_merge($this->viewVars, array( + 'title_for_layout' => $pageTitle, + 'content_for_layout' => $content_for_layout, + 'scripts_for_layout' => join("\n\t", $this->__scripts), + 'cakeDebug' => $debug + )); + + if (empty($this->loaded) && !empty($this->helpers)) { + $loadHelpers = true; + } else { + $loadHelpers = false; + $data_for_layout = array_merge($data_for_layout, $this->loaded); + } + + $this->_triggerHelpers('beforeLayout'); + + if (substr($layoutFileName, -3) === 'ctp' || substr($layoutFileName, -5) === 'thtml') { + $this->output = View::_render($layoutFileName, $data_for_layout, $loadHelpers, true); + } else { + $this->output = $this->_render($layoutFileName, $data_for_layout, $loadHelpers); + } + + if ($this->output === false) { + $this->output = $this->_render($layoutFileName, $data_for_layout); + $msg = __("Error in layout %s, got: <blockquote>%s</blockquote>", true); + trigger_error(sprintf($msg, $layoutFileName, $this->output), E_USER_ERROR); + return false; + } + + $this->_triggerHelpers('afterLayout'); + + return $this->output; + } +/** + * Fire a callback on all loaded Helpers + * + * @param string $callback name of callback fire. + * @access protected + * @return void + */ + function _triggerHelpers($callback) { + if (empty($this->loaded)) { + return false; + } + $helpers = array_keys($this->loaded); + foreach ($helpers as $helperName) { + $helper =& $this->loaded[$helperName]; + if (is_object($helper)) { + if (is_subclass_of($helper, 'Helper')) { + $helper->{$callback}(); + } + } + } + } +/** + * Render cached view + * + * @param string $filename the cache file to include + * @param string $timeStart the page render start time + */ + function renderCache($filename, $timeStart) { + ob_start(); + include ($filename); + + if (Configure::read() > 0 && $this->layout != 'xml') { + echo "<!-- Cached Render Time: " . round(getMicrotime() - $timeStart, 4) . "s -->"; + } + $out = ob_get_clean(); + + if (preg_match('/^<!--cachetime:(\\d+)-->/', $out, $match)) { + if (time() >= $match['1']) { + @unlink($filename); + unset ($out); + return false; + } else { + if ($this->layout === 'xml') { + header('Content-type: text/xml'); + } + echo str_replace('<!--cachetime:' . $match['1'] . '-->', '', $out); + return true; + } + } + } +/** + * Returns a list of variables available in the current View context + * + * @return array + * @access public + */ + function getVars() { + return array_keys($this->viewVars); + } +/** + * Returns the contents of the given View variable(s) + * + * @return array + * @access public + */ + function getVar($var) { + if (!isset($this->viewVars[$var])) { + return null; + } else { + return $this->viewVars[$var]; + } + } +/** + * Adds a script block or other element to be inserted in $scripts_for_layout in + * the <head /> of a document layout + * + * @param string $name + * @param string $content + * @return void + * @access public + */ + function addScript($name, $content = null) { + if (empty($content)) { + if (!in_array($name, array_values($this->__scripts))) { + $this->__scripts[] = $name; + } + } else { + $this->__scripts[$name] = $content; + } + } +/** + * Generates a unique, non-random DOM ID for an object, based on the object type and the target URL. + * + * @param string $object Type of object, i.e. 'form' or 'link' + * @param string $url The object's target URL + * @return string + * @access public + */ + function uuid($object, $url) { + $c = 1; + $url = Router::url($url); + $hash = $object . substr(md5($object . $url), 0, 10); + while (in_array($hash, $this->uuids)) { + $hash = $object . substr(md5($object . $url . $c), 0, 10); + $c++; + } + $this->uuids[] = $hash; + return $hash; + } +/** + * Returns the entity reference of the current context as an array of identity parts + * + * @return array An array containing the identity elements of an entity + */ + function entity() { + $assoc = ($this->association) ? $this->association : $this->model; + return array_values(Set::filter( + array($assoc, $this->modelId, $this->field, $this->fieldSuffix) + )); + } +/** + * Allows a template or element to set a variable that will be available in + * a layout or other element. Analagous to Controller::set. + * + * @param mixed $one A string or an array of data. + * @param mixed $two Value in case $one is a string (which then works as the key). + * Unused if $one is an associative array, otherwise serves as the + * values to $one's keys. + * @return unknown + */ + function set($one, $two = null) { + $data = null; + if (is_array($one)) { + if (is_array($two)) { + $data = array_combine($one, $two); + } else { + $data = $one; + } + } else { + $data = array($one => $two); + } + + if ($data == null) { + return false; + } + + foreach ($data as $name => $value) { + if ($name == 'title') { + $this->pageTitle = $value; + } else { + $this->viewVars[$name] = $value; + } + } + } +/** + * Displays an error page to the user. Uses layouts/error.ctp to render the page. + * + * @param integer $code HTTP Error code (for instance: 404) + * @param string $name Name of the error (for instance: Not Found) + * @param string $message Error message as a web page + */ + function error($code, $name, $message) { + header ("HTTP/1.1 {$code} {$name}"); + print ($this->_render( + $this->_getLayoutFileName('error'), + array('code' => $code, 'name' => $name, 'message' => $message) + )); + } +/** + * Renders and returns output for given view filename with its + * array of data. + * + * @param string $___viewFn Filename of the view + * @param array $___dataForView Data to include in rendered view + * @return string Rendered output + * @access protected + */ + function _render($___viewFn, $___dataForView, $loadHelpers = true, $cached = false) { + $loadedHelpers = array(); + + if ($this->helpers != false && $loadHelpers === true) { + $loadedHelpers = $this->_loadHelpers($loadedHelpers, $this->helpers); + + foreach (array_keys($loadedHelpers) as $helper) { + $camelBackedHelper = Inflector::variable($helper); + ${$camelBackedHelper} =& $loadedHelpers[$helper]; + $this->loaded[$camelBackedHelper] =& ${$camelBackedHelper}; + } + + $this->_triggerHelpers('beforeRender'); + } + + extract($___dataForView, EXTR_SKIP); + ob_start(); + + if (Configure::read() > 0) { + include ($___viewFn); + } else { + @include ($___viewFn); + } + + if ($loadHelpers === true) { + $this->_triggerHelpers('afterRender'); + } + + $out = ob_get_clean(); + $caching = ( + isset($this->loaded['cache']) && + (($this->cacheAction != false)) && (Configure::read('Cache.check') === true) + ); + + if ($caching) { + if (is_a($this->loaded['cache'], 'CacheHelper')) { + $cache =& $this->loaded['cache']; + $cache->base = $this->base; + $cache->here = $this->here; + $cache->helpers = $this->helpers; + $cache->action = $this->action; + $cache->controllerName = $this->name; + $cache->layout = $this->layout; + $cache->cacheAction = $this->cacheAction; + $cache->cache($___viewFn, $out, $cached); + } + } + return $out; + } +/** + * Loads helpers, with their dependencies. + * + * @param array $loaded List of helpers that are already loaded. + * @param array $helpers List of helpers to load. + * @param string $parent holds name of helper, if loaded helper has helpers + * @return array + */ + function &_loadHelpers(&$loaded, $helpers, $parent = null) { + if (empty($loaded)) { + $helpers[] = 'Session'; + } + + foreach ($helpers as $i => $helper) { + $options = array(); + + if (!is_int($i)) { + $options = $helper; + $helper = $i; + } + $plugin = $this->plugin; + + if (strpos($helper, '.') !== false) { + list($plugin, $helper) = explode('.', $helper); + } + $helperCn = $helper . 'Helper'; + + if (!isset($loaded[$helper])) { + if (!class_exists($helperCn)) { + $isLoaded = false; + if (!is_null($plugin)) { + $isLoaded = App::import('Helper', $plugin . '.' . $helper); + } + if (!$isLoaded) { + if (!App::import('Helper', $helper)) { + $this->cakeError('missingHelperFile', array(array( + 'helper' => $helper, + 'file' => Inflector::underscore($helper) . '.php', + 'base' => $this->base + ))); + return false; + } + } + if (!class_exists($helperCn)) { + $this->cakeError('missingHelperClass', array(array( + 'helper' => $helper, + 'file' => Inflector::underscore($helper) . '.php', + 'base' => $this->base + ))); + return false; + } + } + $loaded[$helper] =& new $helperCn($options); + $vars = array( + 'base', 'webroot', 'here', 'params', 'action', 'data', 'themeWeb', 'plugin' + ); + $c = count($vars); + + for ($j = 0; $j < $c; $j++) { + $loaded[$helper]->{$vars[$j]} = $this->{$vars[$j]}; + } + + if (!empty($this->validationErrors)) { + $loaded[$helper]->validationErrors = $this->validationErrors; + } + if (is_array($loaded[$helper]->helpers) && !empty($loaded[$helper]->helpers)) { + $loaded =& $this->_loadHelpers($loaded, $loaded[$helper]->helpers, $helper); + } + } + if (isset($loaded[$parent])) { + $loaded[$parent]->{$helper} =& $loaded[$helper]; + } + } + return $loaded; + } +/** + * Returns filename of given action's template file (.ctp) as a string. + * CamelCased action names will be under_scored! This means that you can have + * LongActionNames that refer to long_action_names.ctp views. + * + * @param string $action Controller action to find template filename for + * @return string Template filename + * @access protected + */ + function _getViewFileName($name = null) { + $subDir = null; + + if (!is_null($this->subDir)) { + $subDir = $this->subDir . DS; + } + + if ($name === null) { + $name = $this->action; + } + $name = str_replace('/', DS, $name); + + if (strpos($name, DS) === false && $name[0] !== '.') { + $name = $this->viewPath . DS . $subDir . Inflector::underscore($name); + } elseif (strpos($name, DS) !== false) { + if ($name{0} === DS || $name{1} === ':') { + if (is_file($name)) { + return $name; + } + $name = trim($name, DS); + } else if ($name[0] === '.') { + $name = substr($name, 3); + } else { + $name = $this->viewPath . DS . $subDir . $name; + } + } + + $paths = $this->_paths(Inflector::underscore($this->plugin)); + + $exts = array($this->ext, '.ctp', '.thtml'); + foreach ($exts as $ext) { + foreach ($paths as $path) { + if (file_exists($path . $name . $ext)) { + return $path . $name . $ext; + } + } + } + $defaultPath = $paths[0]; + + if ($this->plugin) { + $pluginPaths = Configure::read('pluginPaths'); + foreach ($paths as $path) { + if (strpos($path, $pluginPaths[0]) === 0) { + $defaultPath = $path; + break; + } + } + } + return $this->_missingView($defaultPath . $name . $this->ext, 'missingView'); + } + +/** + * Returns layout filename for this template as a string. + * + * @return string Filename for layout file (.ctp). + * @access protected + */ + function _getLayoutFileName($name = null) { + if ($name === null) { + $name = $this->layout; + } + $subDir = null; + + if (!is_null($this->layoutPath)) { + $subDir = $this->layoutPath . DS; + } + $paths = $this->_paths(Inflector::underscore($this->plugin)); + $file = 'layouts' . DS . $subDir . $name; + + $exts = array($this->ext, '.ctp', '.thtml'); + foreach ($exts as $ext) { + foreach ($paths as $path) { + if (file_exists($path . $file . $ext)) { + return $path . $file . $ext; + } + } + } + return $this->_missingView($paths[0] . $file . $this->ext, 'missingLayout'); + } +/** + * Return a misssing view error message + * + * @param string $viewFileName the filename that should exist + * @return cakeError + */ + function _missingView($file, $error = 'missingView') { + + if ($error === 'missingView') { + $this->cakeError('missingView', array( + 'className' => $this->name, + 'action' => $this->action, + 'file' => $file, + 'base' => $this->base + )); + return false; + } elseif ($error === 'missingLayout') { + $this->cakeError('missingLayout', array( + 'layout' => $this->layout, + 'file' => $file, + 'base' => $this->base + )); + return false; + } + } +/** + * Return all possible paths to find view files in order + * + * @param string $plugin + * @return array paths + * @access protected + */ + function _paths($plugin = null, $cached = true) { + if ($plugin === null && $cached === true && !empty($this->__paths)) { + return $this->__paths; + } + $paths = array(); + $viewPaths = Configure::read('viewPaths'); + $corePaths = array_flip(Configure::corePaths('view')); + + if (!empty($plugin)) { + $count = count($viewPaths); + for ($i = 0; $i < $count; $i++) { + if (!isset($corePaths[$viewPaths[$i]])) { + $paths[] = $viewPaths[$i] . 'plugins' . DS . $plugin . DS; + } + } + $pluginPaths = Configure::read('pluginPaths'); + $count = count($pluginPaths); + + for ($i = 0; $i < $count; $i++) { + $paths[] = $pluginPaths[$i] . $plugin . DS . 'views' . DS; + } + } + $paths = array_merge($paths, $viewPaths); + + if (empty($this->__paths)) { + $this->__paths = $paths; + } + return $paths; + } +/** + * @deprecated + * @see View::element + */ + function renderElement($name, $params = array(), $loadHelpers = false) { + return $this->element($name, $params, $loadHelpers); + } +} + +?> \ No newline at end of file diff --git a/cake/libs/xml.php b/cake/libs/xml.php new file mode 100755 index 0000000..0d91825 --- /dev/null +++ b/cake/libs/xml.php @@ -0,0 +1,1406 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * XML handling for Cake. + * + * The methods in these classes enable the datasources that use XML to work. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP v .0.10.3.1400 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +App::import('Core', 'Set'); +/** + * XML node. + * + * Single XML node in an XML tree. + * + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP v .0.10.3.1400 + */ +class XmlNode extends Object { +/** + * Name of node + * + * @var string + * @access public + */ + var $name = null; +/** + * Node namespace + * + * @var string + * @access public + */ + var $namespace = null; +/** + * Namespaces defined for this node and all child nodes + * + * @var array + * @access public + */ + var $namespaces = array(); +/** + * Value of node + * + * @var string + * @access public + */ + var $value; +/** + * Attributes on this node + * + * @var array + * @access public + */ + var $attributes = array(); +/** + * This node's children + * + * @var array + * @access public + */ + var $children = array(); +/** + * Reference to parent node. + * + * @var XmlNode + * @access private + */ + var $__parent = null; +/** + * Constructor. + * + * @param string $name Node name + * @param array $attributes Node attributes + * @param mixed $value Node contents (text) + * @param array $children Node children + */ + function __construct($name = null, $value = null, $namespace = null) { + if (strpos($name, ':') !== false) { + list($prefix, $name) = explode(':', $name); + if (!$namespace) { + $namespace = $prefix; + } + } + $this->name = $name; + if ($namespace) { + $this->namespace = $namespace; + } + + if (is_array($value) || is_object($value)) { + $this->normalize($value); + } elseif (!empty($value) || $value === 0 || $value === '0') { + $this->createTextNode($value); + } + } + +/** + * Adds a namespace to the current node + * + * @param string $prefix The namespace prefix + * @param string $url The namespace DTD URL + * @return void + */ + function addNamespace($prefix, $url) { + if ($ns = Xml::addGlobalNs($prefix, $url)) { + $this->namespaces = array_merge($this->namespaces, $ns); + return true; + } + return false; + } +/** + * Adds a namespace to the current node + * + * @param string $prefix The namespace prefix + * @param string $url The namespace DTD URL + * @return void + */ + function removeNamespace($prefix) { + if (Xml::removeGlobalNs($prefix)) { + return true; + } + return false; + } +/** + * Creates an XmlNode object that can be appended to this document or a node in it + * + * @param string $name Node name + * @param string $value Node value + * @param string $namespace Node namespace + * @return object XmlNode + */ + function &createNode($name = null, $value = null, $namespace = false) { + $node =& new XmlNode($name, $value, $namespace); + $node->setParent($this); + return $node; + } +/** + * Creates an XmlElement object that can be appended to this document or a node in it + * + * @param string $name Element name + * @param string $value Element value + * @param array $attributes Element attributes + * @param string $namespace Node namespace + * @return object XmlElement + */ + function &createElement($name = null, $value = null, $attributes = array(), $namespace = false) { + $element =& new XmlElement($name, $value, $attributes, $namespace); + $element->setParent($this); + return $element; + } +/** + * Creates an XmlTextNode object that can be appended to this document or a node in it + * + * @param string $value Node value + * @return object XmlTextNode + */ + function &createTextNode($value = null) { + $node = new XmlTextNode($value); + $node->setParent($this); + return $node; + } +/** + * Gets the XML element properties from an object. + * + * @param object $object Object to get properties from + * @return array Properties from object + * @access public + */ + function normalize($object, $keyName = null, $options = array()) { + if (is_a($object, 'XmlNode')) { + return $object; + } + $name = null; + $options += array('format' => 'attributes'); + + if ($keyName !== null && !is_numeric($keyName)) { + $name = $keyName; + } elseif (!empty($object->_name_)) { + $name = $object->_name_; + } elseif (isset($object->name)) { + $name = $object->name; + } elseif ($options['format'] == 'attributes') { + $name = get_class($object); + } + + $tagOpts = $this->__tagOptions($name); + + if ($tagOpts === false) { + return; + } + + if (isset($tagOpts['name'])) { + $name = $tagOpts['name']; + } elseif ($name != strtolower($name)) { + $name = Inflector::slug(Inflector::underscore($name)); + } + + if (!empty($name)) { + $node =& $this->createElement($name); + } else { + $node =& $this; + } + + $namespace = array(); + $attributes = array(); + $children = array(); + $chldObjs = array(); + + if (is_object($object)) { + $chldObjs = get_object_vars($object); + } elseif (is_array($object)) { + $chldObjs = $object; + } elseif (!empty($object) || $object === 0) { + $node->createTextNode($object); + } + $attr = array(); + + if (isset($tagOpts['attributes'])) { + $attr = $tagOpts['attributes']; + } + if (isset($tagOpts['value']) && isset($chldObjs[$tagOpts['value']])) { + $node->createTextNode($chldObjs[$tagOpts['value']]); + unset($chldObjs[$tagOpts['value']]); + } + + $n = $name; + if (!empty($chldObjs['_name_'])) { + $n = null; + unset($chldObjs['_name_']); + } + $c = 0; + + foreach ($chldObjs as $key => $val) { + if (in_array($key, $attr) && !is_object($val) && !is_array($val)) { + $attributes[$key] = $val; + } else { + if (!isset($tagOpts['children']) || $tagOpts['children'] === array() || (is_array($tagOpts['children']) && in_array($key, $tagOpts['children']))) { + if (!is_numeric($key)) { + $n = $key; + } + if (is_array($val)) { + foreach ($val as $n2 => $obj2) { + if (is_numeric($n2)) { + $n2 = $n; + } + $node->normalize($obj2, $n2, $options); + } + } else { + if (is_object($val)) { + + $node->normalize($val, $n, $options); + } elseif ($options['format'] == 'tags' && $this->__tagOptions($key) !== false) { + $tmp =& $node->createElement($key); + if (!empty($val) || $val === 0) { + $tmp->createTextNode($val); + } + } elseif ($options['format'] == 'attributes') { + $node->addAttribute($key, $val); + } + } + } + } + $c++; + } + if (!empty($name)) { + return $node; + } + return $children; + } +/** + * Gets the tag-specific options for the given node name + * + * @param string $name XML tag name + * @param string $option The specific option to query. Omit for all options + * @return mixed A specific option value if $option is specified, otherwise an array of all options + * @access private + */ + function __tagOptions($name, $option = null) { + if (isset($this->__tags[$name])) { + $tagOpts = $this->__tags[$name]; + } elseif (isset($this->__tags[strtolower($name)])) { + $tagOpts = $this->__tags[strtolower($name)]; + } else { + return null; + } + if ($tagOpts === false) { + return false; + } + if (empty($option)) { + return $tagOpts; + } + if (isset($tagOpts[$option])) { + return $tagOpts[$option]; + } + return null; + } +/** + * Returns the fully-qualified XML node name, with namespace + * + * @access public + */ + function name() { + if (!empty($this->namespace)) { + $_this =& XmlManager::getInstance(); + if (!isset($_this->options['verifyNs']) || !$_this->options['verifyNs'] || in_array($this->namespace, array_keys($_this->namespaces))) { + return $this->namespace . ':' . $this->name; + } + } + return $this->name; + } +/** + * Sets the parent node of this XmlNode. + * + * @access public + */ + function setParent(&$parent) { + if (strtolower(get_class($this)) == 'xml') { + return; + } + if (isset($this->__parent) && is_object($this->__parent)) { + if ($this->__parent->compare($parent)) { + return; + } + foreach ($this->__parent->children as $i => $child) { + if ($this->compare($child)) { + array_splice($this->__parent->children, $i, 1); + break; + } + } + } + if ($parent == null) { + unset($this->__parent); + } else { + $parent->children[] =& $this; + $this->__parent =& $parent; + } + } +/** + * Returns a copy of self. + * + * @return object Cloned instance + * @access public + */ + function cloneNode() { + return clone($this); + } +/** + * Compares $node to this XmlNode object + * + * @param object An XmlNode or subclass instance + * @return boolean True if the nodes match, false otherwise + * @access public + */ + function compare($node) { + $keys = array(get_object_vars($this), get_object_vars($node)); + return ($keys[0] === $keys[1]); + } +/** + * Append given node as a child. + * + * @param object $child XmlNode with appended child + * @param array $options XML generator options for objects and arrays + * @return object A reference to the appended child node + * @access public + */ + function &append(&$child, $options = array()) { + if (empty($child)) { + $return = false; + return $return; + } + + if (is_object($child)) { + if ($this->compare($child)) { + trigger_error('Cannot append a node to itself.'); + $return = false; + return $return; + } + } else if (is_array($child)) { + $child = Set::map($child); + if (is_array($child)) { + if (!is_a(current($child), 'XmlNode')) { + foreach ($child as $i => $childNode) { + $child[$i] = $this->normalize($childNode, null, $options); + } + } else { + foreach ($child as $childNode) { + $this->append($childNode, $options); + } + } + return $child; + } + } else { + $attributes = array(); + if (func_num_args() >= 2) { + $attributes = func_get_arg(1); + } + $child =& $this->createNode($child, null, $attributes); + } + + $child = $this->normalize($child, null, $options); + + if (empty($child->namespace) && !empty($this->namespace)) { + $child->namespace = $this->namespace; + } + + if (is_a($child, 'XmlNode')) { + $child->setParent($this); + } + + return $child; + } +/** + * Returns first child node, or null if empty. + * + * @return object First XmlNode + * @access public + */ + function &first() { + if (isset($this->children[0])) { + return $this->children[0]; + } else { + $return = null; + return $return; + } + } +/** + * Returns last child node, or null if empty. + * + * @return object Last XmlNode + * @access public + */ + function &last() { + if (count($this->children) > 0) { + return $this->children[count($this->children) - 1]; + } else { + $return = null; + return $return; + } + } +/** + * Returns child node with given ID. + * + * @param string $id Name of child node + * @return object Child XmlNode + * @access public + */ + function &child($id) { + $null = null; + + if (is_int($id)) { + if (isset($this->children[$id])) { + return $this->children[$id]; + } else { + return null; + } + } elseif (is_string($id)) { + for ($i = 0; $i < count($this->children); $i++) { + if ($this->children[$i]->name == $id) { + return $this->children[$i]; + } + } + } + return $null; + } +/** + * Gets a list of childnodes with the given tag name. + * + * @param string $name Tag name of child nodes + * @return array An array of XmlNodes with the given tag name + * @access public + */ + function children($name) { + $nodes = array(); + $count = count($this->children); + for ($i = 0; $i < $count; $i++) { + if ($this->children[$i]->name == $name) { + $nodes[] =& $this->children[$i]; + } + } + return $nodes; + } +/** + * Gets a reference to the next child node in the list of this node's parent. + * + * @return object A reference to the XmlNode object + * @access public + */ + function &nextSibling() { + $null = null; + $count = count($this->__parent->children); + for ($i = 0; $i < $count; $i++) { + if ($this->__parent->children[$i] == $this) { + if ($i >= $count - 1 || !isset($this->__parent->children[$i + 1])) { + return $null; + } + return $this->__parent->children[$i + 1]; + } + } + return $null; + } +/** + * Gets a reference to the previous child node in the list of this node's parent. + * + * @return object A reference to the XmlNode object + * @access public + */ + function &previousSibling() { + $null = null; + $count = count($this->__parent->children); + for ($i = 0; $i < $count; $i++) { + if ($this->__parent->children[$i] == $this) { + if ($i == 0 || !isset($this->__parent->children[$i - 1])) { + return $null; + } + return $this->__parent->children[$i - 1]; + } + } + return $null; + } +/** + * Returns parent node. + * + * @return object Parent XmlNode + * @access public + */ + function &parent() { + return $this->__parent; + } +/** + * Returns the XML document to which this node belongs + * + * @return object Parent XML object + * @access public + */ + function &document() { + $document =& $this; + while (true) { + if (get_class($document) == 'Xml' || $document == null) { + break; + } + $document =& $document->parent(); + } + return $document; + } +/** + * Returns true if this structure has child nodes. + * + * @return bool + * @access public + */ + function hasChildren() { + if (is_array($this->children) && count($this->children) > 0) { + return true; + } + return false; + } +/** + * Returns this XML structure as a string. + * + * @return string String representation of the XML structure. + * @access public + */ + function toString($options = array(), $depth = 0) { + if (is_int($options)) { + $depth = $options; + $options = array(); + } + $defaults = array('cdata' => true, 'whitespace' => false, 'convertEntities' => false, 'showEmpty' => true, 'leaveOpen' => false); + $options = array_merge($defaults, Xml::options(), $options); + $tag = !(strpos($this->name, '#') === 0); + $d = ''; + + if ($tag) { + if ($options['whitespace']) { + $d .= str_repeat("\t", $depth); + } + + $d .= '<' . $this->name(); + if (count($this->namespaces) > 0) { + foreach ($this->namespaces as $key => $val) { + $val = str_replace('"', '\"', $val); + $d .= ' xmlns:' . $key . '="' . $val . '"'; + } + } + + $parent =& $this->parent(); + if ($parent->name === '#document' && count($parent->namespaces) > 0) { + foreach ($parent->namespaces as $key => $val) { + $val = str_replace('"', '\"', $val); + $d .= ' xmlns:' . $key . '="' . $val . '"'; + } + } + + if (is_array($this->attributes) && count($this->attributes) > 0) { + foreach ($this->attributes as $key => $val) { + if (is_bool($val) && $val === false) { + $val = 0; + } + $d .= ' ' . $key . '="' . htmlspecialchars($val, ENT_QUOTES, Configure::read('App.encoding')) . '"'; + } + } + } + + if (!$this->hasChildren() && empty($this->value) && $this->value !== 0 && $tag) { + if (!$options['leaveOpen']) { + $d .= ' />'; + } + if ($options['whitespace']) { + $d .= "\n"; + } + } elseif ($tag || $this->hasChildren()) { + if ($tag) { + $d .= '>'; + } + if ($this->hasChildren()) { + if ($options['whitespace']) { + $d .= "\n"; + } + $count = count($this->children); + $cDepth = $depth + 1; + for ($i = 0; $i < $count; $i++) { + $d .= $this->children[$i]->toString($options, $cDepth); + } + if ($tag) { + if ($options['whitespace'] && $tag) { + $d .= str_repeat("\t", $depth); + } + if (!$options['leaveOpen']) { + $d .= '</' . $this->name() . '>'; + } + if ($options['whitespace']) { + $d .= "\n"; + } + } + } + } + return $d; + } +/** + * Return array representation of current object. + * + * @param boolean $camelize true will camelize child nodes, false will not alter node names + * @return array Array representation + * @access public + */ + function toArray($camelize = true) { + $out = $this->attributes; + $multi = null; + + foreach ($this->children as $child) { + $key = $camelize ? Inflector::camelize($child->name) : $child->name; + + if (is_a($child, 'XmlTextNode')) { + $out['value'] = $child->value; + continue; + } elseif (isset($child->children[0]) && is_a($child->children[0], 'XmlTextNode')) { + $value = $child->children[0]->value; + + if ($child->attributes) { + $value = array_merge(array('value' => $value), $child->attributes); + } + if (isset($out[$child->name]) || isset($multi[$key])) { + if (!isset($multi[$key])) { + $multi[$key] = array($out[$child->name]); + unset($out[$child->name]); + } + $multi[$key][] = $value; + } else { + $out[$child->name] = $value; + } + continue; + } elseif (count($child->children) === 0 && $child->value == '') { + $value = $child->attributes; + if (isset($out[$child->name]) || isset($multi[$key])) { + if (!isset($multi[$key])) { + $multi[$key] = array($out[$child->name]); + unset($out[$child->name]); + } + $multi[$key][] = $value; + } elseif (!empty($value)) { + $out[$key] = $value; + } else { + $out[$child->name] = $value; + } + continue; + } else { + $value = $child->toArray($camelize); + } + + if (!isset($out[$key])) { + $out[$key] = $value; + } else { + if (!is_array($out[$key]) || !isset($out[$key][0])) { + $out[$key] = array($out[$key]); + } + $out[$key][] = $value; + } + } + + if (isset($multi)) { + $out = array_merge($out, $multi); + } + return $out; + } +/** + * Returns data from toString when this object is converted to a string. + * + * @return string String representation of this structure. + * @access private + */ + function __toString() { + return $this->toString(); + } +/** + * Debug method. Deletes the parent. Also deletes this node's children, + * if given the $recursive parameter. + * + * @param boolean $recursive Recursively delete elements. + * @access private + */ + function __killParent($recursive = true) { + unset($this->__parent, $this->_log); + if ($recursive && $this->hasChildren()) { + for ($i = 0; $i < count($this->children); $i++) { + $this->children[$i]->__killParent(true); + } + } + } +} + +/** + * Main XML class. + * + * Parses and stores XML data, representing the root of an XML document + * + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP v .0.10.3.1400 + */ +class Xml extends XmlNode { + +/** + * Resource handle to XML parser. + * + * @var resource + * @access private + */ + var $__parser; +/** + * File handle to XML indata file. + * + * @var resource + * @access private + */ + var $__file; +/** + * Raw XML string data (for loading purposes) + * + * @var string + * @access private + */ + var $__rawData = null; + +/** + * XML document header + * + * @var string + * @access private + */ + var $__header = null; + +/** + * Default array keys/object properties to use as tag names when converting objects or array + * structures to XML. Set by passing $options['tags'] to this object's constructor. + * + * @var array + * @access private + */ + var $__tags = array(); + +/** + * XML document version + * + * @var string + * @access private + */ + var $version = '1.0'; + +/** + * XML document encoding + * + * @var string + * @access private + */ + var $encoding = 'UTF-8'; + +/** + * Constructor. Sets up the XML parser with options, gives it this object as + * its XML object, and sets some variables. + * + * ### Options + * - 'root': The name of the root element, defaults to '#document' + * - 'version': The XML version, defaults to '1.0' + * - 'encoding': Document encoding, defaults to 'UTF-8' + * - 'namespaces': An array of namespaces (as strings) used in this document + * - 'format': Specifies the format this document converts to when parsed or + * rendered out as text, either 'attributes' or 'tags', defaults to 'attributes' + * - 'tags': An array specifying any tag-specific formatting options, indexed + * by tag name. See XmlNode::normalize(). + * @param mixed $input The content with which this XML document should be initialized. Can be a + * string, array or object. If a string is specified, it may be a literal XML + * document, or a URL or file path to read from. + * @param array $options Options to set up with, for valid options see above: + * @see XmlNode::normalize() + */ + function __construct($input = null, $options = array()) { + $defaults = array( + 'root' => '#document', 'tags' => array(), 'namespaces' => array(), + 'version' => '1.0', 'encoding' => 'UTF-8', 'format' => 'attributes' + ); + $options = array_merge($defaults, Xml::options(), $options); + + foreach (array('version', 'encoding', 'namespaces') as $key) { + $this->{$key} = $options[$key]; + } + $this->__tags = $options['tags']; + parent::__construct('#document'); + + if ($options['root'] !== '#document') { + $Root = $this->createNode($options['root']); + } else { + $Root =& $this; + } + + if (!empty($input)) { + if (is_string($input)) { + $Root->load($input); + } elseif (is_array($input) || is_object($input)) { + $Root->append($input, $options); + } + } + } +/** + * Initialize XML object from a given XML string. Returns false on error. + * + * @param string $input XML string, a path to a file, or an HTTP resource to load + * @return boolean Success + * @access public + */ + function load($input) { + if (!is_string($input)) { + return false; + } + $this->__rawData = null; + $this->__header = null; + + if (strstr($input, "<")) { + $this->__rawData = $input; + } elseif (strpos($input, 'http://') === 0 || strpos($input, 'https://') === 0) { + App::import('Core', 'HttpSocket'); + $socket = new HttpSocket(); + $this->__rawData = $socket->get($input); + } elseif (file_exists($input)) { + $this->__rawData = file_get_contents($input); + } else { + trigger_error('XML cannot be read'); + return false; + } + return $this->parse(); + } +/** + * Parses and creates XML nodes from the __rawData property. + * + * @return boolean Success + * @access public + * @see Xml::load() + * @todo figure out how to link attributes and namespaces + */ + function parse() { + $this->__initParser(); + $this->__rawData = trim($this->__rawData); + $this->__header = trim(str_replace( + array('<' . '?', '?' . '>'), + array('', ''), + substr($this->__rawData, 0, strpos($this->__rawData, '?' . '>')) + )); + + xml_parse_into_struct($this->__parser, $this->__rawData, $vals); + $xml =& $this; + $count = count($vals); + + for ($i = 0; $i < $count; $i++) { + $data = $vals[$i]; + $data += array('tag' => null, 'value' => null, 'attributes' => array()); + switch ($data['type']) { + case "open" : + $xml =& $xml->createElement($data['tag'], $data['value'], $data['attributes']); + break; + case "close" : + $xml =& $xml->parent(); + break; + case "complete" : + $xml->createElement($data['tag'], $data['value'], $data['attributes']); + break; + case 'cdata': + $xml->createTextNode($data['value']); + break; + } + } + return true; + } +/** + * Initializes the XML parser resource + * + * @return void + * @access private + */ + function __initParser() { + if (empty($this->__parser)) { + $this->__parser = xml_parser_create(); + xml_set_object($this->__parser, $this); + xml_parser_set_option($this->__parser, XML_OPTION_CASE_FOLDING, 0); + xml_parser_set_option($this->__parser, XML_OPTION_SKIP_WHITE, 1); + } + } +/** + * Returns a string representation of the XML object + * + * @param mixed $options If boolean: whether to include the XML header with the document + * (defaults to true); if an array, overrides the default XML generation options + * @return string XML data + * @access public + * @deprecated + * @see Xml::toString() + */ + function compose($options = array()) { + return $this->toString($options); + } +/** + * If debug mode is on, this method echoes an error message. + * + * @param string $msg Error message + * @param integer $code Error code + * @param integer $line Line in file + * @access public + */ + function error($msg, $code = 0, $line = 0) { + if (Configure::read('debug')) { + echo $msg . " " . $code . " " . $line; + } + } +/** + * Returns a string with a textual description of the error code, or FALSE if no description was found. + * + * @param integer $code Error code + * @return string Error message + * @access public + */ + function getError($code) { + $r = @xml_error_string($code); + return $r; + } + +// Overridden functions from superclass + +/** + * Get next element. NOT implemented. + * + * @return object + * @access public + */ + function &next() { + $return = null; + return $return; + } +/** + * Get previous element. NOT implemented. + * + * @return object + * @access public + */ + function &previous() { + $return = null; + return $return; + } +/** + * Get parent element. NOT implemented. + * + * @return object + * @access public + */ + function &parent() { + $return = null; + return $return; + } +/** + * Adds a namespace to the current document + * + * @param string $prefix The namespace prefix + * @param string $url The namespace DTD URL + * @return void + */ + function addNamespace($prefix, $url) { + if ($count = count($this->children)) { + for ($i = 0; $i < $count; $i++) { + $this->children[$i]->addNamespace($prefix, $url); + } + return true; + } + return parent::addNamespace($prefix, $url); + } +/** + * Removes a namespace to the current document + * + * @param string $prefix The namespace prefix + * @return void + */ + function removeNamespace($prefix) { + if ($count = count($this->children)) { + for ($i = 0; $i < $count; $i++) { + $this->children[$i]->removeNamespace($prefix); + } + return true; + } + return parent::removeNamespace($prefix); + } +/** + * Return string representation of current object. + * + * @return string String representation + * @access public + */ + function toString($options = array()) { + if (is_bool($options)) { + $options = array('header' => $options); + } + + $defaults = array('header' => false, 'encoding' => $this->encoding); + $options = array_merge($defaults, Xml::options(), $options); + $data = parent::toString($options, 0); + + if ($options['header']) { + if (!empty($this->__header)) { + return $this->header($this->__header) . "\n" . $data; + } + return $this->header() . "\n" . $data; + } + + return $data; + } +/** + * Return a header used on the first line of the xml file + * + * @param mixed $attrib attributes of the header element + * @return string formated header + */ + function header($attrib = array()) { + $header = 'xml'; + if (is_string($attrib)) { + $header = $attrib; + } else { + + $attrib = array_merge(array('version' => $this->version, 'encoding' => $this->encoding), $attrib); + foreach ($attrib as $key=>$val) { + $header .= ' ' . $key . '="' . $val . '"'; + } + } + return '<' . '?' . $header . ' ?' . '>'; + } + +/** + * Destructor, used to free resources. + * + * @access private + */ + function __destruct() { + if (is_resource($this->__parser)) { + xml_parser_free($this->__parser); + } + } +/** + * Adds a namespace to any XML documents generated or parsed + * + * @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 + * @access public + * @static + */ + function addGlobalNs($name, $url = null) { + $_this =& XmlManager::getInstance(); + if ($ns = Xml::resolveNamespace($name, $url)) { + $_this->namespaces = array_merge($_this->namespaces, $ns); + return $ns; + } + return false; + } +/** + * Resolves current namespace + * + * @param string $name + * @param string $url + * @return array + */ + function resolveNamespace($name, $url) { + $_this =& XmlManager::getInstance(); + if ($url == null && isset($_this->defaultNamespaceMap[$name])) { + $url = $_this->defaultNamespaceMap[$name]; + } elseif ($url == null) { + return false; + } + + if (!strpos($url, '://') && isset($_this->defaultNamespaceMap[$name])) { + $_url = $_this->defaultNamespaceMap[$name]; + $name = $url; + $url = $_url; + } + return array($name => $url); + } +/** + * Alias to Xml::addNs + * + * @access public + * @static + */ + function addGlobalNamespace($name, $url = null) { + return Xml::addGlobalNs($name, $url); + } +/** + * Removes a namespace added in addNs() + * + * @param string $name The namespace name or URI + * @access public + * @static + */ + function removeGlobalNs($name) { + $_this =& XmlManager::getInstance(); + if (isset($_this->namespaces[$name])) { + unset($_this->namespaces[$name]); + unset($this->namespaces[$name]); + return true; + } elseif (in_array($name, $_this->namespaces)) { + $keys = array_keys($_this->namespaces); + $count = count($keys); + for ($i = 0; $i < $count; $i++) { + if ($_this->namespaces[$keys[$i]] == $name) { + unset($_this->namespaces[$keys[$i]]); + unset($this->namespaces[$keys[$i]]); + return true; + } + } + } + return false; + } +/** + * Alias to Xml::removeNs + * + * @access public + * @static + */ + function removeGlobalNamespace($name) { + return Xml::removeGlobalNs($name); + } +/** + * Sets/gets global XML options + * + * @param array $options + * @return array + * @access public + * @static + */ + function options($options = array()) { + $_this =& XmlManager::getInstance(); + $_this->options = array_merge($_this->options, $options); + return $_this->options; + } +} +/** + * The XML Element + * + */ +class XmlElement extends XmlNode { +/** + * Construct an Xml element + * + * @param string $name name of the node + * @param string $value value of the node + * @param array $attributes + * @param string $namespace + * @return string A copy of $data in XML format + */ + function __construct($name = null, $value = null, $attributes = array(), $namespace = false) { + parent::__construct($name, $value, $namespace); + $this->addAttribute($attributes); + } +/** + * Get all the attributes for this element + * + * @return array + */ + function attributes() { + return $this->attributes; + } +/** + * Add attributes to this element + * + * @param string $name name of the node + * @param string $value value of the node + * @return boolean + */ + function addAttribute($name, $val = null) { + if (is_object($name)) { + $name = get_object_vars($name); + } + if (is_array($name)) { + foreach ($name as $key => $val) { + $this->addAttribute($key, $val); + } + return true; + } + if (is_numeric($name)) { + $name = $val; + $val = null; + } + if (!empty($name)) { + if (strpos($name, 'xmlns') === 0) { + if ($name == 'xmlns') { + $this->namespace = $val; + } else { + list($pre, $prefix) = explode(':', $name); + $this->addNamespace($prefix, $val); + return true; + } + } + $this->attributes[$name] = $val; + return true; + } + return false; + } +/** + * Remove attributes to this element + * + * @param string $name name of the node + * @return boolean + */ + function removeAttribute($attr) { + if (array_key_exists($attr, $this->attributes)) { + unset($this->attributes[$attr]); + return true; + } + return false; + } +} + +/** + * XML text or CDATA node + * + * Stores XML text data according to the encoding of the parent document + * + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP v .1.2.6000 + */ +class XmlTextNode extends XmlNode { +/** + * Harcoded XML node name, represents this object as a text node + * + * @var string + */ + var $name = '#text'; +/** + * The text/data value which this node contains + * + * @var string + */ + var $value = null; +/** + * Construct text node with the given parent object and data + * + * @param object $parent Parent XmlNode/XmlElement object + * @param mixed $value Node value + */ + function __construct($value = null) { + $this->value = $value; + } +/** + * Looks for child nodes in this element + * + * @return boolean False - not supported + */ + function hasChildren() { + return false; + } +/** + * Append an XML node: XmlTextNode does not support this operation + * + * @return boolean False - not supported + * @todo make convertEntities work without mb support, convert entities to number entities + */ + function append() { + return false; + } +/** + * Return string representation of current text node object. + * + * @return string String representation + * @access public + */ + function toString($options = array(), $depth = 0) { + if (is_int($options)) { + $depth = $options; + $options = array(); + } + + $defaults = array('cdata' => true, 'whitespace' => false, 'convertEntities' => false); + $options = array_merge($defaults, Xml::options(), $options); + $val = $this->value; + + if ($options['convertEntities'] && function_exists('mb_convert_encoding')) { + $val = mb_convert_encoding($val,'UTF-8', 'HTML-ENTITIES'); + } + + if ($options['cdata'] === true && !is_numeric($val)) { + $val = '<![CDATA[' . $val . ']]>'; + } + + if ($options['whitespace']) { + return str_repeat("\t", $depth) . $val . "\n"; + } + return $val; + } +} +/** + * Manages application-wide namespaces and XML parsing/generation settings. + * Private class, used exclusively within scope of XML class. + * + * @access private + */ +class XmlManager { + +/** + * Global XML namespaces. Used in all XML documents processed by this application + * + * @var array + * @access public + */ + var $namespaces = array(); +/** + * Global XML document parsing/generation settings. + * + * @var array + * @access public + */ + var $options = array(); +/** + * Map of common namespace URIs + * + * @access private + * @var array + */ + var $defaultNamespaceMap = array( + 'dc' => 'http://purl.org/dc/elements/1.1/', // Dublin Core + 'dct' => 'http://purl.org/dc/terms/', // Dublin Core Terms + 'g' => 'http://base.google.com/ns/1.0', // Google Base + 'rc' => 'http://purl.org/rss/1.0/modules/content/', // RSS 1.0 Content Module + 'wf' => 'http://wellformedweb.org/CommentAPI/', // Well-Formed Web Comment API + 'fb' => 'http://rssnamespace.org/feedburner/ext/1.0', // FeedBurner extensions + 'lj' => 'http://www.livejournal.org/rss/lj/1.0/', // Live Journal + 'itunes' => 'http://www.itunes.com/dtds/podcast-1.0.dtd', // iTunes + 'xhtml' => 'http://www.w3.org/1999/xhtml', // XHTML, + 'atom' => 'http://www.w3.org/2005/Atom' // Atom + ); +/** + * Returns a reference to the global XML object that manages app-wide XML settings + * + * @return object + * @access public + */ + function &getInstance() { + static $instance = array(); + + if (!$instance) { + $instance[0] =& new XmlManager(); + } + return $instance[0]; + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/basics.test.php b/cake/tests/cases/basics.test.php new file mode 100755 index 0000000..7baeeb6 --- /dev/null +++ b/cake/tests/cases/basics.test.php @@ -0,0 +1,735 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * BasicsTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases + * @since CakePHP(tm) v 1.2.0.4206 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +require_once CAKE . 'basics.php'; +App::import('Core', 'Folder'); +/** + * BasicsTest class + * + * @package cake + * @subpackage cake.tests.cases + */ +class BasicsTest extends CakeTestCase { +/** + * setUp method + * + * @access public + * @return void + */ + function setUp() { + $this->_localePaths = Configure::read('localePaths'); + Configure::write('localePaths', array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'locale')); + $this->_language = Configure::read('Config.language'); + } +/** + * tearDown method + * + * @access public + * @return void + */ + function tearDown() { + Configure::write('localePaths', $this->_localePaths); + Configure::write('Config.language', $this->_language); + } +/** + * test the array_diff_key compatibility function. + * + * @return void + **/ + function testArrayDiffKey() { + $one = array('one' => 1, 'two' => 2, 'three' => 3); + $two = array('one' => 'one', 'two' => 'two'); + $result = array_diff_key($one, $two); + $expected = array('three' => 3); + $this->assertEqual($result, $expected); + + $one = array('one' => array('value', 'value-two'), 'two' => 2, 'three' => 3); + $two = array('two' => 'two'); + $result = array_diff_key($one, $two); + $expected = array('one' => array('value', 'value-two'), 'three' => 3); + $this->assertEqual($result, $expected); + + $one = array('one' => null, 'two' => 2, 'three' => '', 'four' => 0); + $two = array('two' => 'two'); + $result = array_diff_key($one, $two); + $expected = array('one' => null, 'three' => '', 'four' => 0); + $this->assertEqual($result, $expected); + + $one = array('minYear' => null, 'maxYear' => null, 'separator' => '-', 'interval' => 1, 'monthNames' => true); + $two = array('minYear' => null, 'maxYear' => null, 'separator' => '-', 'interval' => 1, 'monthNames' => true); + $result = array_diff_key($one, $two); + $this->assertEqual($result, array()); + + } +/** + * testHttpBase method + * + * @return void + * @access public + */ + function testEnv() { + $this->skipIf(!function_exists('ini_get') || ini_get('safe_mode') === '1', '%s safe mode is on'); + + $__SERVER = $_SERVER; + $__ENV = $_ENV; + + $_SERVER['HTTP_HOST'] = 'localhost'; + $this->assertEqual(env('HTTP_BASE'), ''); + + $_SERVER['HTTP_HOST'] = 'example.com'; + $this->assertEqual(env('HTTP_BASE'), '.example.com'); + + $_SERVER['HTTP_HOST'] = 'www.example.com'; + $this->assertEqual(env('HTTP_BASE'), '.example.com'); + + $_SERVER['HTTP_HOST'] = 'subdomain.example.com'; + $this->assertEqual(env('HTTP_BASE'), '.example.com'); + + $_SERVER['HTTP_HOST'] = 'double.subdomain.example.com'; + $this->assertEqual(env('HTTP_BASE'), '.subdomain.example.com'); + + $_SERVER = $_ENV = array(); + + $_SERVER['SCRIPT_NAME'] = '/a/test/test.php'; + $this->assertEqual(env('SCRIPT_NAME'), '/a/test/test.php'); + + $_SERVER = $_ENV = array(); + + $_ENV['CGI_MODE'] = 'BINARY'; + $_ENV['SCRIPT_URL'] = '/a/test/test.php'; + $this->assertEqual(env('SCRIPT_NAME'), '/a/test/test.php'); + + $_SERVER = $_ENV = array(); + + $this->assertFalse(env('HTTPS')); + + $_SERVER['HTTPS'] = 'on'; + $this->assertTrue(env('HTTPS')); + + $_SERVER['HTTPS'] = '1'; + $this->assertTrue(env('HTTPS')); + + $_SERVER['HTTPS'] = 'I am not empty'; + $this->assertTrue(env('HTTPS')); + + $_SERVER['HTTPS'] = 1; + $this->assertTrue(env('HTTPS')); + + $_SERVER['HTTPS'] = 'off'; + $this->assertFalse(env('HTTPS')); + + $_SERVER['HTTPS'] = false; + $this->assertFalse(env('HTTPS')); + + $_SERVER['HTTPS'] = ''; + $this->assertFalse(env('HTTPS')); + + $_SERVER = array(); + + $_ENV['SCRIPT_URI'] = 'https://domain.test/a/test.php'; + $this->assertTrue(env('HTTPS')); + + $_ENV['SCRIPT_URI'] = 'http://domain.test/a/test.php'; + $this->assertFalse(env('HTTPS')); + + $_SERVER = $_ENV = array(); + + $this->assertFalse(env('TEST_ME')); + + $_ENV['TEST_ME'] = 'a'; + $this->assertEqual(env('TEST_ME'), 'a'); + + $_SERVER['TEST_ME'] = 'b'; + $this->assertEqual(env('TEST_ME'), 'b'); + + unset($_ENV['TEST_ME']); + $this->assertEqual(env('TEST_ME'), 'b'); + + $_SERVER = $__SERVER; + $_ENV = $__ENV; + } +/** + * test uses() + * + * @access public + * @return void + */ + function testUses() { + $this->skipIf(class_exists('Security') || class_exists('Sanitize'), '%s Security and/or Sanitize class already loaded'); + + $this->assertFalse(class_exists('Security')); + $this->assertFalse(class_exists('Sanitize')); + + uses('Security', 'Sanitize'); + + $this->assertTrue(class_exists('Security')); + $this->assertTrue(class_exists('Sanitize')); + } +/** + * Test h() + * + * @access public + * @return void + */ + function testH() { + $string = '<foo>'; + $result = h($string); + $this->assertEqual('&lt;foo&gt;', $result); + + $in = array('this & that', '<p>Which one</p>'); + $result = h($in); + $expected = array('this &amp; that', '&lt;p&gt;Which one&lt;/p&gt;'); + $this->assertEqual($expected, $result); + } +/** + * Test a() + * + * @access public + * @return void + */ + function testA() { + $result = a('this', 'that', 'bar'); + $this->assertEqual(array('this', 'that', 'bar'), $result); + } +/** + * Test aa() + * + * @access public + * @return void + */ + function testAa() { + $result = aa('a', 'b', 'c', 'd'); + $expected = array('a' => 'b', 'c' => 'd'); + $this->assertEqual($expected, $result); + + $result = aa('a', 'b', 'c', 'd', 'e'); + $expected = array('a' => 'b', 'c' => 'd', 'e' => null); + $this->assertEqual($result, $expected); + } +/** + * Test am() + * + * @access public + * @return void + */ + function testAm() { + $result = am(array('one', 'two'), 2, 3, 4); + $expected = array('one', 'two', 2, 3, 4); + $this->assertEqual($result, $expected); + + $result = am(array('one' => array(2, 3), 'two' => array('foo')), array('one' => array(4, 5))); + $expected = array('one' => array(4, 5),'two' => array('foo')); + $this->assertEqual($result, $expected); + } +/** + * test cache() + * + * @access public + * @return void + */ + function testCache() { + $_cacheDisable = Configure::read('Cache.disable'); + + Configure::write('Cache.disable', true); + $result = cache('basics_test', 'simple cache write'); + $this->assertNull($result); + + $result = cache('basics_test'); + $this->assertNull($result); + + Configure::write('Cache.disable', false); + $result = cache('basics_test', 'simple cache write'); + $this->assertTrue($result); + $this->assertTrue(file_exists(CACHE . 'basics_test')); + + $result = cache('basics_test'); + $this->assertEqual($result, 'simple cache write'); + @unlink(CACHE . 'basics_test'); + + cache('basics_test', 'expired', '+1 second'); + sleep(2); + $result = cache('basics_test', null, '+1 second'); + $this->assertNull($result); + + Configure::write('Cache.disable', $_cacheDisable); + } +/** + * test clearCache() + * + * @access public + * @return void + */ + function testClearCache() { + cache('views' . DS . 'basics_test.cache', 'simple cache write'); + $this->assertTrue(file_exists(CACHE . 'views' . DS . 'basics_test.cache')); + + cache('views' . DS . 'basics_test_2.cache', 'simple cache write 2'); + $this->assertTrue(file_exists(CACHE . 'views' . DS . 'basics_test_2.cache')); + + cache('views' . DS . 'basics_test_3.cache', 'simple cache write 3'); + $this->assertTrue(file_exists(CACHE . 'views' . DS . 'basics_test_3.cache')); + + $result = clearCache(array('basics_test', 'basics_test_2'), 'views', '.cache'); + $this->assertTrue($result); + $this->assertFalse(file_exists(CACHE . 'views' . DS . 'basics_test.cache')); + $this->assertFalse(file_exists(CACHE . 'views' . DS . 'basics_test.cache')); + $this->assertTrue(file_exists(CACHE . 'views' . DS . 'basics_test_3.cache')); + + $result = clearCache(null, 'views', '.cache'); + $this->assertTrue($result); + $this->assertFalse(file_exists(CACHE . 'views' . DS . 'basics_test_3.cache')); + + // Different path from views and with prefix + cache('models' . DS . 'basics_test.cache', 'simple cache write'); + $this->assertTrue(file_exists(CACHE . 'models' . DS . 'basics_test.cache')); + + cache('models' . DS . 'basics_test_2.cache', 'simple cache write 2'); + $this->assertTrue(file_exists(CACHE . 'models' . DS . 'basics_test_2.cache')); + + cache('models' . DS . 'basics_test_3.cache', 'simple cache write 3'); + $this->assertTrue(file_exists(CACHE . 'models' . DS . 'basics_test_3.cache')); + + $result = clearCache('basics', 'models', '.cache'); + $this->assertTrue($result); + $this->assertFalse(file_exists(CACHE . 'models' . DS . 'basics_test.cache')); + $this->assertFalse(file_exists(CACHE . 'models' . DS . 'basics_test_2.cache')); + $this->assertFalse(file_exists(CACHE . 'models' . DS . 'basics_test_3.cache')); + } +/** + * test __() + * + * @access public + * @return void + */ + function test__() { + Configure::write('Config.language', 'rule_1_po'); + + $result = __('Plural Rule 1', true); + $expected = 'Plural Rule 1 (translated)'; + $this->assertEqual($result, $expected); + + $result = __('Plural Rule 1 (from core)', true); + $expected = 'Plural Rule 1 (from core translated)'; + $this->assertEqual($result, $expected); + + ob_start(); + __('Plural Rule 1 (from core)'); + $result = ob_get_clean(); + $expected = 'Plural Rule 1 (from core translated)'; + $this->assertEqual($result, $expected); + } +/** + * test __n() + * + * @access public + * @return void + */ + function test__n() { + Configure::write('Config.language', 'rule_1_po'); + + $result = __n('%d = 1', '%d = 0 or > 1', 0, true); + $expected = '%d = 0 or > 1 (translated)'; + $this->assertEqual($result, $expected); + + $result = __n('%d = 1', '%d = 0 or > 1', 1, true); + $expected = '%d = 1 (translated)'; + $this->assertEqual($result, $expected); + + $result = __n('%d = 1 (from core)', '%d = 0 or > 1 (from core)', 2, true); + $expected = '%d = 0 or > 1 (from core translated)'; + $this->assertEqual($result, $expected); + + ob_start(); + __n('%d = 1 (from core)', '%d = 0 or > 1 (from core)', 2); + $result = ob_get_clean(); + $expected = '%d = 0 or > 1 (from core translated)'; + $this->assertEqual($result, $expected); + } +/** + * test __d() + * + * @access public + * @return void + */ + function test__d() { + Configure::write('Config.language', 'rule_1_po'); + + $result = __d('default', 'Plural Rule 1', true); + $expected = 'Plural Rule 1 (translated)'; + $this->assertEqual($result, $expected); + + $result = __d('core', 'Plural Rule 1', true); + $expected = 'Plural Rule 1'; + $this->assertEqual($result, $expected); + + $result = __d('core', 'Plural Rule 1 (from core)', true); + $expected = 'Plural Rule 1 (from core translated)'; + $this->assertEqual($result, $expected); + + ob_start(); + __d('core', 'Plural Rule 1 (from core)'); + $result = ob_get_clean(); + $expected = 'Plural Rule 1 (from core translated)'; + $this->assertEqual($result, $expected); + } +/** + * test __dn() + * + * @access public + * @return void + */ + function test__dn() { + Configure::write('Config.language', 'rule_1_po'); + + $result = __dn('default', '%d = 1', '%d = 0 or > 1', 0, true); + $expected = '%d = 0 or > 1 (translated)'; + $this->assertEqual($result, $expected); + + $result = __dn('core', '%d = 1', '%d = 0 or > 1', 0, true); + $expected = '%d = 0 or > 1'; + $this->assertEqual($result, $expected); + + $result = __dn('core', '%d = 1 (from core)', '%d = 0 or > 1 (from core)', 0, true); + $expected = '%d = 0 or > 1 (from core translated)'; + $this->assertEqual($result, $expected); + + $result = __dn('default', '%d = 1', '%d = 0 or > 1', 1, true); + $expected = '%d = 1 (translated)'; + $this->assertEqual($result, $expected); + + ob_start(); + __dn('core', '%d = 1 (from core)', '%d = 0 or > 1 (from core)', 2); + $result = ob_get_clean(); + $expected = '%d = 0 or > 1 (from core translated)'; + $this->assertEqual($result, $expected); + } +/** + * test __c() + * + * @access public + * @return void + */ + function test__c() { + Configure::write('Config.language', 'rule_1_po'); + + $result = __c('Plural Rule 1', 6, true); + $expected = 'Plural Rule 1 (translated)'; + $this->assertEqual($result, $expected); + + $result = __c('Plural Rule 1 (from core)', 6, true); + $expected = 'Plural Rule 1 (from core translated)'; + $this->assertEqual($result, $expected); + + ob_start(); + __c('Plural Rule 1 (from core)', 6); + $result = ob_get_clean(); + $expected = 'Plural Rule 1 (from core translated)'; + $this->assertEqual($result, $expected); + } +/** + * test __dc() + * + * @access public + * @return void + */ + function test__dc() { + Configure::write('Config.language', 'rule_1_po'); + + $result = __dc('default', 'Plural Rule 1', 6, true); + $expected = 'Plural Rule 1 (translated)'; + $this->assertEqual($result, $expected); + + $result = __dc('default', 'Plural Rule 1 (from core)', 6, true); + $expected = 'Plural Rule 1 (from core translated)'; + $this->assertEqual($result, $expected); + + $result = __dc('core', 'Plural Rule 1', 6, true); + $expected = 'Plural Rule 1'; + $this->assertEqual($result, $expected); + + $result = __dc('core', 'Plural Rule 1 (from core)', 6, true); + $expected = 'Plural Rule 1 (from core translated)'; + $this->assertEqual($result, $expected); + + ob_start(); + __dc('default', 'Plural Rule 1 (from core)', 6); + $result = ob_get_clean(); + $expected = 'Plural Rule 1 (from core translated)'; + $this->assertEqual($result, $expected); + } +/** + * test __dcn() + * + * @access public + * @return void + */ + function test__dcn() { + Configure::write('Config.language', 'rule_1_po'); + + $result = __dcn('default', '%d = 1', '%d = 0 or > 1', 0, 6, true); + $expected = '%d = 0 or > 1 (translated)'; + $this->assertEqual($result, $expected); + + $result = __dcn('default', '%d = 1 (from core)', '%d = 0 or > 1 (from core)', 1, 6, true); + $expected = '%d = 1 (from core translated)'; + $this->assertEqual($result, $expected); + + $result = __dcn('core', '%d = 1', '%d = 0 or > 1', 0, 6, true); + $expected = '%d = 0 or > 1'; + $this->assertEqual($result, $expected); + + ob_start(); + __dcn('default', '%d = 1 (from core)', '%d = 0 or > 1 (from core)', 1, 6); + $result = ob_get_clean(); + $expected = '%d = 1 (from core translated)'; + $this->assertEqual($result, $expected); + } +/** + * test LogError() + * + * @access public + * @return void + */ + function testLogError() { + @unlink(LOGS . 'error.log'); + + LogError('Testing LogError() basic function'); + LogError("Testing with\nmulti-line\nstring"); + + $result = file_get_contents(LOGS . 'error.log'); + $this->assertPattern('/Error: Testing LogError\(\) basic function/', $result); + $this->assertNoPattern("/Error: Testing with\nmulti-line\nstring/", $result); + $this->assertPattern('/Error: Testing with multi-line string/', $result); + } +/** + * test fileExistsInPath() + * + * @access public + * @return void + */ + function testFileExistsInPath() { + $this->skipUnless(function_exists('ini_set'), '%s ini_set function not available'); + + $_includePath = ini_get('include_path'); + + $path = TMP . 'basics_test'; + $folder1 = $path . DS . 'folder1'; + $folder2 = $path . DS . 'folder2'; + $file1 = $path . DS . 'file1.php'; + $file2 = $folder1 . DS . 'file2.php'; + $file3 = $folder1 . DS . 'file3.php'; + $file4 = $folder2 . DS . 'file4.php'; + + new Folder($path, true); + new Folder($folder1, true); + new Folder($folder2, true); + touch($file1); + touch($file2); + touch($file3); + touch($file4); + + ini_set('include_path', $path . PATH_SEPARATOR . $folder1); + + $this->assertEqual(fileExistsInPath('file1.php'), $file1); + $this->assertEqual(fileExistsInPath('file2.php'), $file2); + $this->assertEqual(fileExistsInPath('folder1' . DS . 'file2.php'), $file2); + $this->assertEqual(fileExistsInPath($file2), $file2); + $this->assertEqual(fileExistsInPath('file3.php'), $file3); + $this->assertEqual(fileExistsInPath($file4), $file4); + + $this->assertFalse(fileExistsInPath('file1')); + $this->assertFalse(fileExistsInPath('file4.php')); + + $Folder = new Folder($path); + $Folder->delete(); + + ini_set('include_path', $_includePath); + } +/** + * test convertSlash() + * + * @access public + * @return void + */ + function testConvertSlash() { + $result = convertSlash('\path\to\location\\'); + $expected = '\path\to\location\\'; + $this->assertEqual($result, $expected); + + $result = convertSlash('/path/to/location/'); + $expected = 'path_to_location'; + $this->assertEqual($result, $expected); + } +/** + * test debug() + * + * @access public + * @return void + */ + function testDebug() { + ob_start(); + debug('this-is-a-test'); + $result = ob_get_clean(); + $pattern = '/.*\>(cake(\/|\\\)tests(\/|\\\)cases(\/|\\\)basics\.test\.php|'; + $pattern .= preg_quote(substr(__FILE__, 1), '/') . ')'; + $pattern .= '.*line.*' . (__LINE__ - 4) . '.*this-is-a-test.*/s'; + $this->assertPattern($pattern, $result); + + ob_start(); + debug('<div>this-is-a-test</div>', true); + $result = ob_get_clean(); + $pattern = '/.*\>(cake(\/|\\\)tests(\/|\\\)cases(\/|\\\)basics\.test\.php|'; + $pattern .= preg_quote(substr(__FILE__, 1), '/') . ')'; + $pattern .= '.*line.*' . (__LINE__ - 4) . '.*&lt;div&gt;this-is-a-test&lt;\/div&gt;.*/s'; + $this->assertPattern($pattern, $result); + } +/** + * test pr() + * + * @access public + * @return void + */ + function testPr() { + ob_start(); + pr('this is a test'); + $result = ob_get_clean(); + $expected = "<pre>this is a test</pre>"; + $this->assertEqual($result, $expected); + + ob_start(); + pr(array('this' => 'is', 'a' => 'test')); + $result = ob_get_clean(); + $expected = "<pre>Array\n(\n [this] => is\n [a] => test\n)\n</pre>"; + $this->assertEqual($result, $expected); + } +/** + * test params() + * + * @access public + * @return void + */ + function testParams() { + $this->assertNull(params('weekend')); + $this->assertNull(params(array())); + $this->assertEqual(params(array('weekend')), array('weekend')); + + $nested = array(array('weekend')); + $this->assertEqual(params($nested), array('weekend')); + + $multiple = array(array('weekend'), 'jean-luc', 'godard'); + $this->assertEqual(params($multiple), $multiple); + } +/** + * test stripslashes_deep() + * + * @access public + * @return void + */ + function testStripslashesDeep() { + $this->skipIf(ini_get('magic_quotes_sybase') === '1', '%s magic_quotes_sybase is on'); + + $this->assertEqual(stripslashes_deep("tes\'t"), "tes't"); + $this->assertEqual(stripslashes_deep('tes\\' . chr(0) .'t'), 'tes' . chr(0) .'t'); + $this->assertEqual(stripslashes_deep('tes\"t'), 'tes"t'); + $this->assertEqual(stripslashes_deep("tes\'t"), "tes't"); + $this->assertEqual(stripslashes_deep('te\\st'), 'test'); + + $nested = array( + 'a' => "tes\'t", + 'b' => 'tes\\' . chr(0) .'t', + 'c' => array( + 'd' => 'tes\"t', + 'e' => "te\'s\'t", + array('f' => "tes\'t") + ), + 'g' => 'te\\st' + ); + $expected = array( + 'a' => "tes't", + 'b' => 'tes' . chr(0) .'t', + 'c' => array( + 'd' => 'tes"t', + 'e' => "te's't", + array('f' => "tes't") + ), + 'g' => 'test' + ); + $this->assertEqual(stripslashes_deep($nested), $expected); + } +/** + * test stripslashes_deep() with magic_quotes_sybase on + * + * @access public + * @return void + */ + function testStripslashesDeepSybase() { + $this->skipUnless(ini_get('magic_quotes_sybase') === '1', '%s magic_quotes_sybase is off'); + + $this->assertEqual(stripslashes_deep("tes\'t"), "tes\'t"); + + $nested = array( + 'a' => "tes't", + 'b' => "tes''t", + 'c' => array( + 'd' => "tes'''t", + 'e' => "tes''''t", + array('f' => "tes''t") + ), + 'g' => "te'''''st" + ); + $expected = array( + 'a' => "tes't", + 'b' => "tes't", + 'c' => array( + 'd' => "tes''t", + 'e' => "tes''t", + array('f' => "tes't") + ), + 'g' => "te'''st" + ); + $this->assertEqual(stripslashes_deep($nested), $expected); + } +/** + * test ife() + * + * @access public + * @return void + */ + function testIfe() { + $this->assertEqual(ife(true, 'a', 'b'), 'a'); + $this->assertEqual(ife(' ', 'a', 'b'), 'a'); + $this->assertEqual(ife('test', 'a', 'b'), 'a'); + $this->assertEqual(ife(23, 'a', 'b'), 'a'); + $this->assertEqual(ife(array('t' => 'est'), 'a', 'b'), 'a'); + + $this->assertEqual(ife(false, 'a', 'b'), 'b'); + $this->assertEqual(ife(null, 'a', 'b'), 'b'); + $this->assertEqual(ife('', 'a', 'b'), 'b'); + $this->assertEqual(ife(0, 'a', 'b'), 'b'); + $this->assertEqual(ife(array(), 'a', 'b'), 'b'); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/console/cake.test.php b/cake/tests/cases/console/cake.test.php new file mode 100755 index 0000000..6eda913 --- /dev/null +++ b/cake/tests/cases/console/cake.test.php @@ -0,0 +1,501 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * ShellDispatcherTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2007, Cake Software Foundation, Inc. + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2007, Cake Software Foundation, Inc. + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.console + * @since CakePHP(tm) v 1.2.0.5432 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +if (!defined('DISABLE_AUTO_DISPATCH')) { + define('DISABLE_AUTO_DISPATCH', true); +} + +if (!class_exists('ShellDispatcher')) { + ob_start(); + $argv = false; + require CAKE . 'console' . DS . 'cake.php'; + ob_end_clean(); +} +/** + * TestShellDispatcher class + * + * @package cake + * @subpackage cake.tests.cases.console + */ +class TestShellDispatcher extends ShellDispatcher { +/** + * params property + * + * @var array + * @access public + */ + var $params = array(); +/** + * stdout property + * + * @var string + * @access public + */ + var $stdout = ''; +/** + * stderr property + * + * @var string + * @access public + */ + var $stderr = ''; +/** + * stopped property + * + * @var string + * @access public + */ + var $stopped = null; +/** + * _initEnvironment method + * + * @access protected + * @return void + */ + function _initEnvironment() { + } +/** + * stderr method + * + * @access public + * @return void + */ + function stderr($string) { + $this->stderr .= rtrim($string, ' '); + } +/** + * stdout method + * + * @access public + * @return void + */ + function stdout($string, $newline = true) { + if ($newline) { + $this->stdout .= rtrim($string, ' ') . "\n"; + } else { + $this->stdout .= rtrim($string, ' '); + } + } +/** + * _stop method + * + * @access protected + * @return void + */ + function _stop($status = 0) { + $this->stopped = 'Stopped with status: ' . $status; + } +} +/** + * ShellDispatcherTest + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class ShellDispatcherTest extends UnitTestCase { +/** + * setUp method + * + * @access public + * @return void + */ + function setUp() { + $this->_pluginPaths = Configure::read('pluginPaths'); + $this->_shellPaths = Configure::read('shellPaths'); + + Configure::write('pluginPaths', array( + TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS + )); + Configure::write('shellPaths', array( + CORE_PATH ? CONSOLE_LIBS : ROOT . DS . CONSOLE_LIBS, + TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'vendors' . DS . 'shells' . DS + )); + } +/** + * tearDown method + * + * @access public + * @return void + */ + function tearDown() { + Configure::write('pluginPaths', $this->_pluginPaths); + Configure::write('shellPaths', $this->_shellPaths); + } +/** + * testParseParams method + * + * @access public + * @return void + */ + function testParseParams() { + $Dispatcher =& new TestShellDispatcher(); + + $params = array( + '/cake/1.2.x.x/cake/console/cake.php', + 'bake', + '-app', + 'new', + '-working', + '/var/www/htdocs' + ); + $expected = array( + 'app' => 'new', + 'webroot' => 'webroot', + 'working' => '/var/www/htdocs/new', + 'root' => '/var/www/htdocs' + ); + $Dispatcher->parseParams($params); + $this->assertEqual($expected, $Dispatcher->params); + + + $params = array('cake.php'); + $expected = array( + 'app' => 'app', + 'webroot' => 'webroot', + 'working' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH . DS . 'app'), + 'root' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH), + ); + $Dispatcher->params = $Dispatcher->args = array(); + $Dispatcher->parseParams($params); + $this->assertEqual($expected, $Dispatcher->params); + + + $params = array( + 'cake.php', + '-app', + 'new', + ); + $expected = array( + 'app' => 'new', + 'webroot' => 'webroot', + 'working' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH . DS . 'new'), + 'root' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH) + ); + $Dispatcher->params = $Dispatcher->args = array(); + $Dispatcher->parseParams($params); + $this->assertEqual($expected, $Dispatcher->params); + + + $params = array( + './cake.php', + 'bake', + '-app', + 'new', + '-working', + '/cake/1.2.x.x/cake/console' + ); + + $expected = array( + 'app' => 'new', + 'webroot' => 'webroot', + 'working' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH . DS . 'new'), + 'root' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH) + ); + + $Dispatcher->params = $Dispatcher->args = array(); + $Dispatcher->parseParams($params); + $this->assertEqual($expected, $Dispatcher->params); + + + $params = array( + './console/cake.php', + 'bake', + '-app', + 'new', + '-working', + '/cake/1.2.x.x/cake' + ); + $expected = array( + 'app' => 'new', + 'webroot' => 'webroot', + 'working' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH . DS . 'new'), + 'root' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH) + ); + $Dispatcher->params = $Dispatcher->args = array(); + $Dispatcher->parseParams($params); + $this->assertEqual($expected, $Dispatcher->params); + + + $params = array( + './console/cake.php', + 'bake', + '-app', + 'new', + '-dry', + '-working', + '/cake/1.2.x.x/cake' + ); + $expected = array( + 'app' => 'new', + 'webroot' => 'webroot', + 'working' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH . DS . 'new'), + 'root' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH), + 'dry' => 1 + ); + $Dispatcher->params = $Dispatcher->args = array(); + $Dispatcher->parseParams($params); + $this->assertEqual($expected, $Dispatcher->params); + + + $params = array( + './console/cake.php', + '-working', + '/cake/1.2.x.x/cake', + 'schema', + 'run', + 'create', + '-dry', + '-f', + '-name', + 'DbAcl' + ); + $expected = array( + 'app' => 'app', + 'webroot' => 'webroot', + 'working' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH . DS . 'app'), + 'root' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH), + 'dry' => 1, + 'f' => 1, + 'name' => 'DbAcl' + ); + $Dispatcher->params = $Dispatcher->args = array(); + $Dispatcher->parseParams($params); + $this->assertEqual($expected, $Dispatcher->params); + + + $expected = array('./console/cake.php', 'schema', 'run', 'create'); + $this->assertEqual($expected, $Dispatcher->args); + + + $params = array( + '/cake/1.2.x.x/cake/console/cake.php', + '-working', + '/cake/1.2.x.x/app', + 'schema', + 'run', + 'create', + '-dry', + '-name', + 'DbAcl' + ); + $expected = array( + 'app' => 'app', + 'webroot' => 'webroot', + 'working' => '/cake/1.2.x.x/app', + 'root' => '/cake/1.2.x.x', + 'dry' => 1, + 'name' => 'DbAcl' + ); + $Dispatcher->params = $Dispatcher->args = array(); + $Dispatcher->parseParams($params); + $this->assertEqual($expected, $Dispatcher->params); + + + $expected = array('/cake/1.2.x.x/cake/console/cake.php', 'schema', 'run', 'create'); + $this->assertEqual($expected, $Dispatcher->args); + $params = array( + 'cake.php', + '-working', + 'C:/wamp/www/cake/app', + 'bake', + '-app', + 'C:/wamp/www/apps/cake/app', + ); + $expected = array( + 'app' => 'app', + 'webroot' => 'webroot', + 'working' => 'C:\wamp\www\apps\cake\app', + 'root' => 'C:\wamp\www\apps\cake' + ); + + + $Dispatcher->params = $Dispatcher->args = array(); + $Dispatcher->parseParams($params); + $this->assertEqual($expected, $Dispatcher->params); + + + $params = array( + 'cake.php', + '-working', + 'C:\wamp\www\cake\app', + 'bake', + '-app', + 'C:\wamp\www\apps\cake\app', + ); + $expected = array( + 'app' => 'app', + 'webroot' => 'webroot', + 'working' => 'C:\wamp\www\apps\cake\app', + 'root' => 'C:\wamp\www\apps\cake' + ); + $Dispatcher->params = $Dispatcher->args = array(); + $Dispatcher->parseParams($params); + $this->assertEqual($expected, $Dispatcher->params); + + + $params = array( + 'cake.php', + '-working', + 'C:\wamp\www\apps', + 'bake', + '-app', + 'cake\app', + '-url', + 'http://example.com/some/url/with/a/path' + ); + $expected = array( + 'app' => 'app', + 'webroot' => 'webroot', + 'working' => 'C:\wamp\www\apps\cake\app', + 'root' => 'C:\wamp\www\apps\cake', + 'url' => 'http://example.com/some/url/with/a/path' + ); + $Dispatcher->params = $Dispatcher->args = array(); + $Dispatcher->parseParams($params); + $this->assertEqual($expected, $Dispatcher->params); + + + $params = array( + '/home/amelo/dev/cake-common/cake/console/cake.php', + '-root', + '/home/amelo/dev/lsbu-vacancy', + '-working', + '/home/amelo/dev/lsbu-vacancy', + '-app', + 'app', + ); + $expected = array( + 'app' => 'app', + 'webroot' => 'webroot', + 'working' => '/home/amelo/dev/lsbu-vacancy/app', + 'root' => '/home/amelo/dev/lsbu-vacancy', + ); + $Dispatcher->params = $Dispatcher->args = array(); + $Dispatcher->parseParams($params); + $this->assertEqual($expected, $Dispatcher->params); + + + $params = array( + 'cake.php', + '-working', + 'D:\www', + 'bake', + 'my_app', + ); + $expected = array( + 'working' => 'D:\www', + 'app' => 'www', + 'root' => 'D:', + 'webroot' => 'webroot' + ); + + $Dispatcher->params = $Dispatcher->args = array(); + $Dispatcher->parseParams($params); + $this->assertEqual($expected, $Dispatcher->params); + } +/** + * testBuildPaths method + * + * @access public + * @return void + */ + function testBuildPaths() { + $Dispatcher =& new TestShellDispatcher(); + + $result = $Dispatcher->shellPaths; + $expected = array( + TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS . 'test_plugin' . DS . 'vendors' . DS . 'shells' . DS, + TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS . 'test_plugin_two' . DS . 'vendors' . DS . 'shells' . DS, + APP . 'vendors' . DS . 'shells' . DS, + VENDORS . 'shells' . DS, + CORE_PATH ? CONSOLE_LIBS : ROOT . DS . CONSOLE_LIBS, + TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'vendors' . DS . 'shells' . DS, + ); + $this->assertIdentical(array_diff($result, $expected), array()); + $this->assertIdentical(array_diff($expected, $result), array()); + } +/** + * testDispatch method + * + * @access public + * @return void + */ + function testDispatch() { + $Dispatcher =& new TestShellDispatcher(array('sample')); + $this->assertPattern('/This is the main method called from SampleShell/', $Dispatcher->stdout); + + $Dispatcher =& new TestShellDispatcher(array('test_plugin_two.example')); + $this->assertPattern('/This is the main method called from TestPluginTwo.ExampleShell/', $Dispatcher->stdout); + + $Dispatcher =& new TestShellDispatcher(array('test_plugin_two.welcome', 'say_hello')); + $this->assertPattern('/This is the say_hello method called from TestPluginTwo.WelcomeShell/', $Dispatcher->stdout); + } +/** + * testHelpCommand method + * + * @access public + * @return void + */ + function testHelpCommand() { + $Dispatcher =& new TestShellDispatcher(); + + $expected = "/ CORE(\\\|\/)tests(\\\|\/)test_app(\\\|\/)plugins(\\\|\/)test_plugin(\\\|\/)vendors(\\\|\/)shells:"; + $expected .= "\n\t example"; + $expected .= "\n/"; + $this->assertPattern($expected, $Dispatcher->stdout); + + $expected = "/ CORE(\\\|\/)tests(\\\|\/)test_app(\\\|\/)plugins(\\\|\/)test_plugin_two(\\\|\/)vendors(\\\|\/)shells:"; + $expected .= "\n\t example"; + $expected .= "\n\t welcome"; + $expected .= "\n/"; + $this->assertPattern($expected, $Dispatcher->stdout); + + $expected = "/ APP(\\\|\/)vendors(\\\|\/)shells:"; + $expected .= "\n/"; + $this->assertPattern($expected, $Dispatcher->stdout); + + $expected = "/ ROOT(\\\|\/)vendors(\\\|\/)shells:"; + $expected .= "\n/"; + $this->assertPattern($expected, $Dispatcher->stdout); + + $expected = "/ CORE(\\\|\/)console(\\\|\/)libs:"; + $expected .= "\n\t acl"; + $expected .= "\n\t api"; + $expected .= "\n\t bake"; + $expected .= "\n\t console"; + $expected .= "\n\t i18n"; + $expected .= "\n\t schema"; + $expected .= "\n\t testsuite"; + $expected .= "\n/"; + $this->assertPattern($expected, $Dispatcher->stdout); + + $expected = "/ CORE(\\\|\/)tests(\\\|\/)test_app(\\\|\/)vendors(\\\|\/)shells:"; + $expected .= "\n\t sample"; + $expected .= "\n/"; + $this->assertPattern($expected, $Dispatcher->stdout); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/console/libs/acl.test.php b/cake/tests/cases/console/libs/acl.test.php new file mode 100755 index 0000000..f27a694 --- /dev/null +++ b/cake/tests/cases/console/libs/acl.test.php @@ -0,0 +1,128 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * AclShell Test file + * + * PHP versions 4 and 5 + * + * CakePHP : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2006-2008, Cake Software Foundation, Inc. + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2006-2008, Cake Software Foundation, Inc. + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP Project + * @package cake + * @subpackage cake.tests.cases.console.libs.tasks + * @since CakePHP v 1.2.0.7726 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +App::import('Core', 'Shell'); + +if (!defined('DISABLE_AUTO_DISPATCH')) { + define('DISABLE_AUTO_DISPATCH', true); +} + +if (!class_exists('ShellDispatcher')) { + ob_start(); + $argv = false; + require CAKE . 'console' . DS . 'cake.php'; + ob_end_clean(); +} + +if (!class_exists('AclShell')) { + require CAKE . 'console' . DS . 'libs' . DS . 'acl.php'; +} + +Mock::generatePartial( + 'ShellDispatcher', 'TestAclShellMockShellDispatcher', + array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment') +); +Mock::generatePartial( + 'AclShell', 'MockAclShell', + array('in', 'out', 'hr', 'createFile') +); +/** + * AclShellTest class + * + * @package cake + * @subpackage cake.tests.cases.console.libs.tasks + */ +class AclShellTest extends CakeTestCase { + var $fixtures = array('core.aco', 'core.aro', 'core.aros_aco'); +/** + * configure Configure for testcase + * + * @return void + **/ + function startCase() { + $this->_aclDb = Configure::read('Acl.database'); + $this->_aclClass = Configure::read('Acl.classname'); + + Configure::write('Acl.database', 'test_suite'); + Configure::write('Acl.classname', 'DbAcl'); + } + +/** + * restore Environment settings + * + * @return void + **/ + function endCase() { + Configure::write('Acl.database', $this->_aclDb); + Configure::write('Acl.classname', $this->_aclClass); + } +/** + * setUp method + * + * @return void + * @access public + */ + function startTest() { + $this->Dispatcher =& new TestAclShellMockShellDispatcher(); + $this->Task =& new MockAclShell($this->Dispatcher); + $this->Task->Dispatch =& $this->Dispatcher; + $this->Task->params['datasource'] = 'test_suite'; + } + +/** + * tearDown method + * + * @return void + * @access public + */ + function endTest() { + ClassRegistry::flush(); + } + +/** + * test that model.foreign_key output works when looking at acl rows + * + * @return void + **/ + function testViewWithModelForeignKeyOutput() { + $this->Task->command = 'view'; + $this->Task->startup(); + $data = array( + 'parent_id' => null, + 'model' => 'MyModel', + 'foreign_key' => 2, + ); + $this->Task->Acl->Aro->create($data); + $this->Task->Acl->Aro->save(); + $this->Task->args[0] = 'aro'; + + $this->Task->expectAt(0, 'out', array('Aro tree:')); + $this->Task->expectAt(1, 'out', array(new PatternExpectation('/\[1\]ROOT/'))); + $this->Task->expectAt(3, 'out', array(new PatternExpectation('/\[3\]Gandalf/'))); + $this->Task->expectAt(5, 'out', array(new PatternExpectation('/\[5\]MyModel.2/'))); + + $this->Task->view(); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/console/libs/api.test.php b/cake/tests/cases/console/libs/api.test.php new file mode 100755 index 0000000..b5568fc --- /dev/null +++ b/cake/tests/cases/console/libs/api.test.php @@ -0,0 +1,116 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * ApiShellTest file + * + * PHP versions 4 and 5 + * + * CakePHP : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2006-2008, Cake Software Foundation, Inc. + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2006-2008, Cake Software Foundation, Inc. + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP Project + * @package cake + * @subpackage cake.tests.cases.console.libs.tasks + * @since CakePHP v 1.2.0.7726 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +App::import('Core', 'Shell'); + +if (!defined('DISABLE_AUTO_DISPATCH')) { + define('DISABLE_AUTO_DISPATCH', true); +} + +if (!class_exists('ShellDispatcher')) { + ob_start(); + $argv = false; + require CAKE . 'console' . DS . 'cake.php'; + ob_end_clean(); +} + +if (!class_exists('ApiShell')) { + require CAKE . 'console' . DS . 'libs' . DS . 'api.php'; +} + +Mock::generatePartial( + 'ShellDispatcher', 'ApiShellMockShellDispatcher', + array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment') +); +Mock::generatePartial( + 'ApiShell', 'MockApiShell', + array('in', 'out', 'createFile', 'hr', '_stop') +); + +/** + * ApiShellTest class + * + * @package cake + * @subpackage cake.tests.cases.console.libs.tasks + */ +class ApiShellTest extends CakeTestCase { +/** + * setUp method + * + * @return void + * @access public + */ + function startTest() { + $this->Dispatcher =& new ApiShellMockShellDispatcher(); + $this->Shell =& new MockApiShell($this->Dispatcher); + $this->Shell->Dispatch =& $this->Dispatcher; + } +/** + * tearDown method + * + * @return void + * @access public + */ + function endTest() { + ClassRegistry::flush(); + } +/** + * Test that method names are detected properly including those with no arguments. + * + * @access public + * @return void + */ + function testMethodNameDetection () { + $this->Shell->setReturnValueAt(0, 'in', 'q'); + $this->Shell->expectAt(0, 'out', array('Controller')); + $expected = array( + array( + '1. afterFilter()', + '2. beforeFilter()', + '3. beforeRender()', + '4. constructClasses()', + '5. disableCache()', + '6. flash($message, $url, $pause = 1)', + '7. header($status)', + '8. isAuthorized()', + '9. loadModel($modelClass = null, $id = null)', + '10. paginate($object = null, $scope = array(), $whitelist = array())', + '11. postConditions($data = array(), $op = null, $bool = \'AND\', $exclusive = false)', + '12. redirect($url, $status = null, $exit = true)', + '13. referer($default = null, $local = false)', + '14. render($action = null, $layout = null, $file = null)', + '15. set($one, $two = null)', + '16. setAction($action)', + '17. validate()', + '18. validateErrors()' + ) + ); + $this->Shell->expectAt(1, 'out', $expected); + + $this->Shell->args = array('controller'); + $this->Shell->paths['controller'] = CAKE_CORE_INCLUDE_PATH . DS . LIBS . 'controller' . DS; + $this->Shell->main(); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/console/libs/schema.test.php b/cake/tests/cases/console/libs/schema.test.php new file mode 100755 index 0000000..063910d --- /dev/null +++ b/cake/tests/cases/console/libs/schema.test.php @@ -0,0 +1,353 @@ +<?php +/** + * SchemaShellTest Test file + * + * PHP versions 4 and 5 + * + * CakePHP : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2006-2009, Cake Software Foundation, Inc. + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2006-2009, Cake Software Foundation, Inc. + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP Project + * @package cake + * @subpackage cake.tests.cases.console.libs.Shells + * @since CakePHP v 1.3 + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +App::import('Shell', 'Shell', false); +App::import('Model', 'Schema', false); + +if (!defined('DISABLE_AUTO_DISPATCH')) { + define('DISABLE_AUTO_DISPATCH', true); +} + +if (!class_exists('ShellDispatcher')) { + ob_start(); + $argv = false; + require CAKE . 'console' . DS . 'cake.php'; + ob_end_clean(); +} + +if (!class_exists('SchemaShell')) { + require CAKE . 'console' . DS . 'libs' . DS . 'schema.php'; +} + +Mock::generatePartial( + 'ShellDispatcher', 'TestSchemaShellMockShellDispatcher', + array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment') +); +Mock::generatePartial( + 'SchemaShell', 'MockSchemaShell', + array('in', 'out', 'hr', 'createFile', 'error', 'err', '_stop') +); + +Mock::generate('CakeSchema', 'MockSchemaCakeSchema'); + +/** + * Test for Schema database management + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class SchemaShellTestSchema extends CakeSchema { + +/** + * name property + * + * @var string 'MyApp' + * @access public + */ + var $name = 'SchemaShellTest'; + +/** + * connection property + * + * @var string 'test_suite' + * @access public + */ + var $connection = 'test_suite'; + +/** + * comments property + * + * @var array + * @access public + */ + var $comments = array( + 'id' => array('type' => 'integer', 'null' => false, 'default' => 0, 'key' => 'primary'), + 'post_id' => array('type' => 'integer', 'null' => false, 'default' => 0), + 'user_id' => array('type' => 'integer', 'null' => false), + 'title' => array('type' => 'string', 'null' => false, 'length' => 100), + 'comment' => array('type' => 'text', 'null' => false, 'default' => null), + 'published' => array('type' => 'string', 'null' => true, 'default' => 'N', 'length' => 1), + 'created' => array('type' => 'datetime', 'null' => true, 'default' => null), + 'updated' => array('type' => 'datetime', 'null' => true, 'default' => null), + 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => true)), + ); + +/** + * posts property + * + * @var array + * @access public + */ + var $articles = array( + 'id' => array('type' => 'integer', 'null' => false, 'default' => 0, 'key' => 'primary'), + 'user_id' => array('type' => 'integer', 'null' => true, 'default' => ''), + 'title' => array('type' => 'string', 'null' => false, 'default' => 'Title'), + 'body' => array('type' => 'text', 'null' => true, 'default' => null), + 'summary' => array('type' => 'text', 'null' => true), + 'published' => array('type' => 'string', 'null' => true, 'default' => 'Y', 'length' => 1), + 'created' => array('type' => 'datetime', 'null' => true, 'default' => null), + 'updated' => array('type' => 'datetime', 'null' => true, 'default' => null), + 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => true)), + ); +} + +/** + * SchemaShellTest class + * + * @package cake + * @subpackage cake.tests.cases.console.libs.Shells + */ +class SchemaShellTest extends CakeTestCase { + + var $fixtures = array('core.article', 'core.user'); +/** + * startTest method + * + * @return void + * @access public + */ + function startTest() { + $this->Dispatcher =& new TestSchemaShellMockShellDispatcher(); + $this->Shell =& new MockSchemaShell($this->Dispatcher); + $this->Shell->Dispatch =& $this->Dispatcher; + } + +/** + * endTest method + * + * @return void + * @access public + */ + function endTest() { + ClassRegistry::flush(); + } + +/** + * test startup method + * + * @return void + **/ + function testStartup() { + $this->Shell->startup(); + $this->assertTrue(isset($this->Shell->Schema)); + $this->assertTrue(is_a($this->Shell->Schema, 'CakeSchema')); + $this->assertEqual(strtolower($this->Shell->Schema->name), APP_DIR); + $this->assertEqual($this->Shell->Schema->file, 'schema.php'); + + unset($this->Shell->Schema); + $this->Shell->params = array( + 'name' => 'TestSchema' + ); + $this->Shell->startup(); + $this->assertEqual($this->Shell->Schema->name, 'TestSchema'); + $this->assertEqual($this->Shell->Schema->file, 'test_schema.php'); + $this->assertEqual($this->Shell->Schema->connection, 'default'); + $this->assertEqual($this->Shell->Schema->path, APP . 'config' . DS . 'sql'); + + unset($this->Shell->Schema); + $this->Shell->params = array( + 'file' => 'other_file.php', + 'connection' => 'test_suite', + 'path' => '/test/path' + ); + $this->Shell->startup(); + $this->assertEqual(strtolower($this->Shell->Schema->name), APP_DIR); + $this->assertEqual($this->Shell->Schema->file, 'other_file.php'); + $this->assertEqual($this->Shell->Schema->connection, 'test_suite'); + $this->assertEqual($this->Shell->Schema->path, '/test/path'); + } + +/** + * Test View - and that it dumps the schema file to stdout + * + * @return void + **/ + function testView() { + $this->Shell->startup(); + $this->Shell->Schema->path = APP . 'config' . DS . 'schema'; + $this->Shell->params['file'] = 'i18n.php'; + $this->Shell->expectOnce('_stop'); + $this->Shell->view(); + } + +/** + * test dump() with sql file generation + * + * @return void + **/ + function testDumpWithFileWriting() { + $file =& new File(APP . 'config' . DS . 'sql' . DS . 'i18n.php'); + $contents = $file->read(); + $file =& new File(TMP . 'tests' . DS . 'i18n.php'); + $file->write($contents); + + $this->Shell->params = array('name' => 'i18n'); + $this->Shell->args = array('write'); + $this->Shell->startup(); + $this->Shell->Schema->path = TMP . 'tests'; + $this->Shell->dump(); + + $sql =& new File(TMP . 'tests' . DS . 'i18n.sql'); + $contents = $sql->read(); + $this->assertPattern('/DROP TABLE/', $contents); + $this->assertPattern('/CREATE TABLE `i18n`/', $contents); + $this->assertPattern('/id/', $contents); + $this->assertPattern('/model/', $contents); + $this->assertPattern('/field/', $contents); + $this->assertPattern('/locale/', $contents); + $this->assertPattern('/foreign_key/', $contents); + $this->assertPattern('/content/', $contents); + + $sql->delete(); + $file->delete(); + } + +/** + * test generate with snapshot generation + * + * @return void + */ + function testGenerateSnaphot() { + $this->Shell->path = TMP; + $this->Shell->params['file'] = 'schema.php'; + $this->Shell->args = array('snapshot'); + $this->Shell->Schema =& new MockSchemaCakeSchema(); + $this->Shell->Schema->setReturnValue('read', array('schema data')); + $this->Shell->Schema->setReturnValue('write', true); + + $this->Shell->Schema->expectOnce('read'); + $this->Shell->Schema->expectOnce('write', array(array('schema data', 'file' => 'schema_1.php'))); + + $this->Shell->generate(); + } + +/** + * test generate without a snapshot. + * + * @return void + **/ + function testGenerateNoOverwrite() { + touch(TMP . 'schema.php'); + $this->Shell->params['file'] = 'schema.php'; + $this->Shell->args = array(); + + $this->Shell->setReturnValue('in', 'q'); + $this->Shell->Schema =& new MockSchemaCakeSchema(); + $this->Shell->Schema->path = TMP; + $this->Shell->Schema->expectNever('read'); + + $result = $this->Shell->generate(); + unlink(TMP . 'schema.php'); + } + +/** + * test generate with overwriting of the schema files. + * + * @return void + **/ + function testGenerateOverwrite() { + touch(TMP . 'schema.php'); + $this->Shell->params['file'] = 'schema.php'; + $this->Shell->args = array(); + + $this->Shell->setReturnValue('in', 'o'); + $this->Shell->expectAt(1, 'out', array(new PatternExpectation('/Schema file:\s[a-z\.]+\sgenerated/'))); + $this->Shell->Schema =& new MockSchemaCakeSchema(); + $this->Shell->Schema->path = TMP; + $this->Shell->Schema->setReturnValue('read', array('schema data')); + $this->Shell->Schema->setReturnValue('write', true); + + $this->Shell->Schema->expectOnce('read'); + $this->Shell->Schema->expectOnce('write', array(array('schema data', 'file' => 'schema.php'))); + + $this->Shell->generate(); + unlink(TMP . 'schema.php'); + } + +/** + * Test schema run create with no table args. + * + * @return void + **/ + function testRunCreateNoArgs() { + $this->Shell->params = array( + 'connection' => 'test_suite', + 'name' => 'i18n', + 'path' => APP . 'config' . DS . 'sql' + ); + $this->Shell->args = array('create'); + $this->Shell->startup(); + $this->Shell->setReturnValue('in', 'y'); + $this->Shell->run(); + + $db =& ConnectionManager::getDataSource('test_suite'); + $sources = $db->listSources(); + $this->assertTrue(in_array($db->config['prefix'] . 'i18n', $sources)); + } + +/** + * Test schema run create with no table args. + * + * @return void + **/ + function testRunCreateWithTableArgs() { + $this->Shell->params = array( + 'connection' => 'test_suite', + 'name' => 'DbAcl', + 'path' => APP . 'config' . DS . 'sql' + ); + $this->Shell->args = array('create', 'acos'); + $this->Shell->startup(); + $this->Shell->setReturnValue('in', 'y'); + $this->Shell->run(); + + $db =& ConnectionManager::getDataSource('test_suite'); + $sources = $db->listSources(); + $this->assertTrue(in_array($db->config['prefix'] . 'acos', $sources)); + $this->assertFalse(in_array($db->config['prefix'] . 'aros', $sources)); + $this->assertFalse(in_array('aros_acos', $sources)); + } + +/** + * test run update with a table arg. + * + * @return void + **/ + function testRunUpdateWithTable() { + $this->Shell->params = array( + 'name' => 'SchemaShellTest', + 'connection' => 'test_suite', + 'f' => true + ); + $this->Shell->args = array('update', 'articles'); + $this->Shell->startup(); + $this->Shell->setReturnValue('in', 'y'); + $this->Shell->run(); + + $article =& new Model(array('name' => 'Article', 'ds' => 'test_suite')); + $fields = $article->schema(); + $this->assertTrue(isset($fields['summary'])); + + $this->_fixtures['core.article']->drop($this->db); + $this->_fixtures['core.article']->create($this->db); + } + +} +?> \ No newline at end of file diff --git a/cake/tests/cases/console/libs/shell.test.php b/cake/tests/cases/console/libs/shell.test.php new file mode 100755 index 0000000..b832088 --- /dev/null +++ b/cake/tests/cases/console/libs/shell.test.php @@ -0,0 +1,390 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * ShellTest file + * + * Test Case for Shell + * + * PHP versions 4 and 5 + * + * CakePHP : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2006-2008, Cake Software Foundation, Inc. + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2006-2008, Cake Software Foundation, Inc. + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP Project + * @package cake + * @subpackage cake.tests.cases.console.libs + * @since CakePHP v 1.2.0.7726 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +App::import('Core', array('Shell', 'Folder')); + +if (!defined('DISABLE_AUTO_DISPATCH')) { + define('DISABLE_AUTO_DISPATCH', true); +} + +if (!class_exists('ShellDispatcher')) { + ob_start(); + $argv = false; + require CAKE . 'console' . DS . 'cake.php'; + ob_end_clean(); +} + +Mock::generatePartial('ShellDispatcher', 'TestShellMockShellDispatcher', array( + 'getInput', 'stdout', 'stderr', '_stop', '_initEnvironment' +)); +/** + * TestShell class + * + * @package cake + * @subpackage cake.tests.cases.console.libs + */ +class TestShell extends Shell { +/** + * Fixtures used in this test case + * + * @var name + * @access public + */ + var $name = 'TestShell'; +} +/** + * TestAppleTask class + * + * @package cake + * @subpackage cake.tests.cases.console.libs + */ +class TestAppleTask extends Shell { +} +/** + * TestBananaTask class + * + * @package cake + * @subpackage cake.tests.cases.console.libs + */ +class TestBananaTask extends Shell { +} +/** + * ShellTest class + * + * @package cake + * @subpackage cake.tests.cases.console.libs + */ +class ShellTest extends CakeTestCase { +/** + * Fixtures used in this test case + * + * @var array + * @access public + */ + var $fixtures = array( + 'core.post', 'core.comment', 'core.article', 'core.user', + 'core.tag', 'core.articles_tag', 'core.attachment' + ); +/** + * setUp method + * + * @return void + * @access public + */ + function setUp() { + $this->Dispatcher =& new TestShellMockShellDispatcher(); + $this->Shell =& new TestShell($this->Dispatcher); + } +/** + * tearDown method + * + * @return void + * @access public + */ + function tearDown() { + ClassRegistry::flush(); + } +/** + * testConstruct method + * + * @return void + * @access public + */ + function testConstruct() { + $this->assertIsA($this->Shell->Dispatch, 'TestShellMockShellDispatcher'); + $this->assertEqual($this->Shell->name, 'TestShell'); + $this->assertEqual($this->Shell->alias, 'TestShell'); + } +/** + * testInitialize method + * + * @return void + * @access public + */ + function testInitialize() { + $_back = array( + 'modelPaths' => Configure::read('modelPaths'), + 'pluginPaths' => Configure::read('pluginPaths'), + 'viewPaths' => Configure::read('viewPaths'), + ); + Configure::write('pluginPaths', array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)); + Configure::write('modelPaths', array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'models' . DS)); + $this->Shell->uses = array('TestPlugin.TestPluginPost'); + $this->Shell->initialize(); + + $this->assertTrue(isset($this->Shell->TestPluginPost)); + $this->assertIsA($this->Shell->TestPluginPost, 'TestPluginPost'); + $this->assertEqual($this->Shell->modelClass, 'TestPluginPost'); + + $this->Shell->uses = array('Comment'); + $this->Shell->initialize(); + $this->assertTrue(isset($this->Shell->Comment)); + $this->assertIsA($this->Shell->Comment, 'Comment'); + $this->assertEqual($this->Shell->modelClass, 'Comment'); + + $this->Shell->uses = true; + $this->Shell->initialize(); + $this->assertTrue(isset($this->Shell->AppModel)); + $this->assertIsA($this->Shell->AppModel, 'AppModel'); + + Configure::write('pluginPaths', $_back['pluginPaths']); + Configure::write('modelPaths', $_back['modelPaths']); + } +/** + * testOut method + * + * @return void + * @access public + */ + function testOut() { + $this->Shell->Dispatch->expectAt(0, 'stdout', array('Just a test', true)); + $this->Shell->out('Just a test'); + + $this->Shell->Dispatch->expectAt(1, 'stdout', array("Just\na\ntest\n", true)); + $this->Shell->out(array('Just', 'a', 'test')); + } +/** + * testIn method + * + * @return void + * @access public + */ + function testIn() { + $this->Shell->Dispatch->setReturnValueAt(0, 'getInput', 'n'); + $this->Shell->Dispatch->expectAt(0, 'getInput', array('Just a test?', array('y', 'n'), 'n')); + $result = $this->Shell->in('Just a test?', array('y', 'n'), 'n'); + $this->assertEqual($result, 'n'); + + $this->Shell->Dispatch->setReturnValueAt(1, 'getInput', 'Y'); + $this->Shell->Dispatch->expectAt(1, 'getInput', array('Just a test?', array('y', 'n'), 'n')); + $result = $this->Shell->in('Just a test?', array('y', 'n'), 'n'); + $this->assertEqual($result, 'Y'); + + $this->Shell->Dispatch->setReturnValueAt(2, 'getInput', 'y'); + $this->Shell->Dispatch->expectAt(2, 'getInput', array('Just a test?', 'y,n', 'n')); + $result = $this->Shell->in('Just a test?', 'y,n', 'n'); + $this->assertEqual($result, 'y'); + + $this->Shell->Dispatch->setReturnValueAt(3, 'getInput', 'y'); + $this->Shell->Dispatch->expectAt(3, 'getInput', array('Just a test?', 'y/n', 'n')); + $result = $this->Shell->in('Just a test?', 'y/n', 'n'); + $this->assertEqual($result, 'y'); + + $this->Shell->Dispatch->setReturnValueAt(4, 'getInput', 'y'); + $this->Shell->Dispatch->expectAt(4, 'getInput', array('Just a test?', 'y', 'y')); + $result = $this->Shell->in('Just a test?', 'y', 'y'); + $this->assertEqual($result, 'y'); + + $this->Shell->interactive = false; + + $result = $this->Shell->in('Just a test?', 'y/n', 'n'); + $this->assertEqual($result, 'n'); + } +/** + * testLoadTasks method + * + * @return void + * @access public + */ + function testLoadTasks() { + $this->assertTrue($this->Shell->loadTasks()); + + $this->Shell->tasks = null; + $this->assertTrue($this->Shell->loadTasks()); + + $this->Shell->tasks = false; + $this->assertTrue($this->Shell->loadTasks()); + + $this->Shell->tasks = true; + $this->assertTrue($this->Shell->loadTasks()); + + $this->Shell->tasks = array(); + $this->assertTrue($this->Shell->loadTasks()); + + // Fatal Error + // $this->Shell->tasks = 'TestIDontExist'; + // $this->assertFalse($this->Shell->loadTasks()); + // $this->assertFalse(isset($this->Shell->TestIDontExist)); + + $this->Shell->tasks = 'TestApple'; + $this->assertTrue($this->Shell->loadTasks()); + $this->assertIsA($this->Shell->TestApple, 'TestAppleTask'); + + $this->Shell->tasks = 'TestBanana'; + $this->assertTrue($this->Shell->loadTasks()); + $this->assertIsA($this->Shell->TestApple, 'TestAppleTask'); + $this->assertIsA($this->Shell->TestBanana, 'TestBananaTask'); + + unset($this->Shell->ShellTestApple, $this->Shell->TestBanana); + + $this->Shell->tasks = array('TestApple', 'TestBanana'); + $this->assertTrue($this->Shell->loadTasks()); + $this->assertIsA($this->Shell->TestApple, 'TestAppleTask'); + $this->assertIsA($this->Shell->TestBanana, 'TestBananaTask'); + } +/** + * testShortPath method + * + * @return void + * @access public + */ + function testShortPath() { + $path = $expected = DS . 'tmp' . DS . 'ab' . DS . 'cd'; + $this->assertEqual($this->Shell->shortPath($path), $expected); + + $path = $expected = DS . 'tmp' . DS . 'ab' . DS . 'cd' . DS ; + $this->assertEqual($this->Shell->shortPath($path), $expected); + + $path = $expected = DS . 'tmp' . DS . 'ab' . DS . 'index.php'; + $this->assertEqual($this->Shell->shortPath($path), $expected); + + // Shell::shortPath needs Folder::realpath + // $path = DS . 'tmp' . DS . 'ab' . DS . '..' . DS . 'cd'; + // $expected = DS . 'tmp' . DS . 'cd'; + // $this->assertEqual($this->Shell->shortPath($path), $expected); + + $path = DS . 'tmp' . DS . 'ab' . DS . DS . 'cd'; + $expected = DS . 'tmp' . DS . 'ab' . DS . 'cd'; + $this->assertEqual($this->Shell->shortPath($path), $expected); + + $path = 'tmp' . DS . 'ab'; + $expected = 'tmp' . DS . 'ab'; + $this->assertEqual($this->Shell->shortPath($path), $expected); + + $path = 'tmp' . DS . 'ab'; + $expected = 'tmp' . DS . 'ab'; + $this->assertEqual($this->Shell->shortPath($path), $expected); + + $path = APP; + $expected = DS . basename(APP) . DS; + $this->assertEqual($this->Shell->shortPath($path), $expected); + + $path = APP . 'index.php'; + $expected = DS . basename(APP) . DS . 'index.php'; + $this->assertEqual($this->Shell->shortPath($path), $expected); + } +/** + * testCreateFile method + * + * @return void + * @access public + */ + function testCreateFile() { + $this->skipIf(DIRECTORY_SEPARATOR === '\\', '%s Not supported on Windows'); + + $path = TMP . 'shell_test'; + $file = $path . DS . 'file1.php'; + + new Folder($path, true); + + $this->Shell->interactive = false; + + $contents = "<?php\necho 'test';\n\$te = 'st';\n?>"; + $result = $this->Shell->createFile($file, $contents); + $this->assertTrue($result); + $this->assertTrue(file_exists($file)); + $this->assertEqual(file_get_contents($file), $contents); + + $contents = "<?php\necho 'another test';\n\$te = 'st';\n?>"; + $result = $this->Shell->createFile($file, $contents); + $this->assertTrue($result); + $this->assertTrue(file_exists($file)); + $this->assertEqual(file_get_contents($file), $contents); + + $this->Shell->interactive = true; + + $this->Shell->Dispatch->setReturnValueAt(0, 'getInput', 'n'); + $this->Shell->Dispatch->expectAt(1, 'stdout', array('File exists, overwrite?', '*')); + + $contents = "<?php\necho 'yet another test';\n\$te = 'st';\n?>"; + $result = $this->Shell->createFile($file, $contents); + $this->assertFalse($result); + $this->assertTrue(file_exists($file)); + $this->assertNotEqual(file_get_contents($file), $contents); + + $this->Shell->Dispatch->setReturnValueAt(1, 'getInput', 'y'); + $this->Shell->Dispatch->expectAt(3, 'stdout', array('File exists, overwrite?', '*')); + + $result = $this->Shell->createFile($file, $contents); + $this->assertTrue($result); + $this->assertTrue(file_exists($file)); + $this->assertEqual(file_get_contents($file), $contents); + + $Folder = new Folder($path); + $Folder->delete(); + } +/** + * testCreateFileWindows method + * + * @return void + * @access public + */ + function testCreateFileWindows() { + $this->skipUnless(DIRECTORY_SEPARATOR === '\\', '%s Supported on Windows only'); + + $path = TMP . 'shell_test'; + $file = $path . DS . 'file1.php'; + + new Folder($path, true); + + $this->Shell->interactive = false; + + $contents = "<?php\necho 'test';\r\n\$te = 'st';\r\n?>"; + $result = $this->Shell->createFile($file, $contents); + $this->assertTrue($result); + $this->assertTrue(file_exists($file)); + $this->assertEqual(file_get_contents($file), $contents); + + $contents = "<?php\necho 'another test';\r\n\$te = 'st';\r\n?>"; + $result = $this->Shell->createFile($file, $contents); + $this->assertTrue($result); + $this->assertTrue(file_exists($file)); + $this->assertEqual(file_get_contents($file), $contents); + + $this->Shell->interactive = true; + + $this->Shell->Dispatch->setReturnValueAt(0, 'getInput', 'n'); + $this->Shell->Dispatch->expectAt(1, 'stdout', array('File exists, overwrite?')); + + $contents = "<?php\necho 'yet another test';\r\n\$te = 'st';\r\n?>"; + $result = $this->Shell->createFile($file, $contents); + $this->assertFalse($result); + $this->assertTrue(file_exists($file)); + $this->assertNotEqual(file_get_contents($file), $contents); + + $this->Shell->Dispatch->setReturnValueAt(1, 'getInput', 'y'); + $this->Shell->Dispatch->expectAt(3, 'stdout', array('File exists, overwrite?')); + + $result = $this->Shell->createFile($file, $contents); + $this->assertTrue($result); + $this->assertTrue(file_exists($file)); + $this->assertEqual(file_get_contents($file), $contents); + + $Folder = new Folder($path); + $Folder->delete(); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/console/libs/tasks/extract.test.php b/cake/tests/cases/console/libs/tasks/extract.test.php new file mode 100755 index 0000000..d393a52 --- /dev/null +++ b/cake/tests/cases/console/libs/tasks/extract.test.php @@ -0,0 +1,135 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * ExtractTaskTest file + * + * Test Case for i18n extraction shell task + * + * PHP versions 4 and 5 + * + * CakePHP : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2006-2008, Cake Software Foundation, Inc. + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2006-2008, Cake Software Foundation, Inc. + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP Project + * @package cake + * @subpackage cake.tests.cases.console.libs.tasks + * @since CakePHP v 1.2.0.7726 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +App::import('Core', array('Shell', 'Folder')); + +if (!defined('DISABLE_AUTO_DISPATCH')) { + define('DISABLE_AUTO_DISPATCH', true); +} + +if (!class_exists('ShellDispatcher')) { + ob_start(); + $argv = false; + require CAKE . 'console' . DS . 'cake.php'; + ob_end_clean(); +} + +if (!class_exists('ExtractTask')) { + require CAKE . 'console' . DS . 'libs' . DS . 'tasks' . DS . 'extract.php'; +} + +Mock::generatePartial( + 'ShellDispatcher', 'TestExtractTaskMockShellDispatcher', + array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment') + ); +/** + * ExtractTaskTest class + * + * @package cake + * @subpackage cake.tests.cases.console.libs.tasks + */ +class ExtractTaskTest extends CakeTestCase { +/** + * setUp method + * + * @return void + * @access public + */ + function setUp() { + $this->Dispatcher =& new TestExtractTaskMockShellDispatcher(); + $this->Task =& new ExtractTask($this->Dispatcher); + } +/** + * tearDown method + * + * @return void + * @access public + */ + function tearDown() { + ClassRegistry::flush(); + } +/** + * testExecute method + * + * @return void + * @access public + */ + function testExecute() { + $path = TMP . 'extract_task_test'; + $folder1 = $path . DS . 'locale'; + + new Folder($path, true); + new Folder($folder1, true); + + $this->Task->interactive = false; + + $this->Task->params['path'] = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'pages'; + $this->Task->params['output'] = $path . DS; + $this->Task->Dispatch->expectNever('stderr'); + $this->Task->Dispatch->expectNever('_stop'); + $this->Task->execute(); + $this->assertTrue(file_exists($path . DS . 'default.pot')); + $result = file_get_contents($path . DS . 'default.pot'); + + $pattern = '/"Content-Type\: text\/plain; charset\=utf-8/'; + $this->assertPattern($pattern, $result); + $pattern = '/"Content-Transfer-Encoding\: 8bit/'; + $this->assertPattern($pattern, $result); + $pattern = '/"Plural-Forms\: nplurals\=INTEGER; plural\=EXPRESSION;/'; + $this->assertPattern($pattern, $result); + + $pattern = '/msgid "Your tmp directory is writable."\nmsgstr ""\n/'; + $this->assertPattern($pattern, $result); + $pattern = '/msgid "Your tmp directory is NOT writable."\nmsgstr ""\n/'; + $this->assertPattern($pattern, $result); + $pattern = '/msgid "The %s is being used for caching. To change the config edit '; + $pattern .= 'APP\/config\/core.php "\nmsgstr ""\n/'; + $this->assertPattern($pattern, $result); + $pattern = '/msgid "Your cache is NOT working. Please check '; + $pattern .= 'the settings in APP\/config\/core.php"\nmsgstr ""\n/'; + $this->assertPattern($pattern, $result); + $pattern = '/msgid "Your database configuration file is present."\nmsgstr ""\n/'; + $this->assertPattern($pattern, $result); + $pattern = '/msgid "Your database configuration file is NOT present."\nmsgstr ""\n/'; + $this->assertPattern($pattern, $result); + $pattern = '/msgid "Rename config\/database.php.default to '; + $pattern .= 'config\/database.php"\nmsgstr ""\n/'; + $this->assertPattern($pattern, $result); + $pattern = '/msgid "Cake is able to connect to the database."\nmsgstr ""\n/'; + $this->assertPattern($pattern, $result); + $pattern = '/msgid "Cake is NOT able to connect to the database."\nmsgstr ""\n/'; + $this->assertPattern($pattern, $result); + $pattern = '/msgid "Editing this Page"\nmsgstr ""\n/'; + $this->assertPattern($pattern, $result); + $pattern = '/msgid "To change the content of this page, edit: %s.*To change its layout, '; + $pattern .= 'edit: %s.*You can also add some CSS styles for your pages at: %s"\nmsgstr ""/s'; + $this->assertPattern($pattern, $result); + + $Folder = new Folder($path); + $Folder->delete(); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/console/libs/tasks/test.test.php b/cake/tests/cases/console/libs/tasks/test.test.php new file mode 100755 index 0000000..548f296 --- /dev/null +++ b/cake/tests/cases/console/libs/tasks/test.test.php @@ -0,0 +1,100 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * TestTaskTest file + * + * Test Case for test generation shell task + * + * PHP versions 4 and 5 + * + * CakePHP : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2006-2008, Cake Software Foundation, Inc. + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2006-2008, Cake Software Foundation, Inc. + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP Project + * @package cake + * @subpackage cake.tests.cases.console.libs.tasks + * @since CakePHP v 1.2.0.7726 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +App::import('Core', 'Shell'); + +if (!defined('DISABLE_AUTO_DISPATCH')) { + define('DISABLE_AUTO_DISPATCH', true); +} + +if (!class_exists('ShellDispatcher')) { + ob_start(); + $argv = false; + require CAKE . 'console' . DS . 'cake.php'; + ob_end_clean(); +} + +if (!class_exists('TestTask')) { + require CAKE . 'console' . DS . 'libs' . DS . 'tasks' . DS . 'test.php'; +} + +Mock::generatePartial( + 'ShellDispatcher', 'TestTestTaskMockShellDispatcher', + array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment') + ); +Mock::generatePartial( + 'TestTask', 'MockTestTask', + array('in', 'out', 'createFile') + ); +/** + * TestTaskTest class + * + * @package cake + * @subpackage cake.tests.cases.console.libs.tasks + */ +class TestTaskTest extends CakeTestCase { +/** + * setUp method + * + * @return void + * @access public + */ + function setUp() { + $this->Dispatcher =& new TestTestTaskMockShellDispatcher(); + $this->Task =& new MockTestTask($this->Dispatcher); + $this->Task->Dispatch =& $this->Dispatcher; + } +/** + * tearDown method + * + * @return void + * @access public + */ + function tearDown() { + ClassRegistry::flush(); + } +/** + * Test that file path generation doesn't continuously append paths. + * + * @access public + * @return void + */ + function testFilePathGeneration () { + $file = TESTS . 'cases' . DS . 'models' . DS . 'my_class.test.php'; + + $this->Task->Dispatch->expectNever('stderr'); + $this->Task->Dispatch->expectNever('_stop'); + + $this->Task->setReturnValueAt(0, 'in', 'y'); + $this->Task->expectAt(0, 'createFile', array($file, '*')); + $this->Task->bake('Model', 'MyClass'); + + $this->Task->setReturnValueAt(1, 'in', 'y'); + $this->Task->expectAt(1, 'createFile', array($file, '*')); + $this->Task->bake('Model', 'MyClass'); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/dispatcher.test.php b/cake/tests/cases/dispatcher.test.php new file mode 100755 index 0000000..613c894 --- /dev/null +++ b/cake/tests/cases/dispatcher.test.php @@ -0,0 +1,2182 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * DispatcherTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases + * @since CakePHP(tm) v 1.2.0.4206 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +require_once CAKE . 'dispatcher.php'; + +if (!class_exists('AppController')) { + require_once LIBS . 'controller' . DS . 'app_controller.php'; +} elseif (!defined('APP_CONTROLLER_EXISTS')){ + define('APP_CONTROLLER_EXISTS', true); +} +/** + * TestDispatcher class + * + * @package cake + * @subpackage cake.tests.cases + */ +class TestDispatcher extends Dispatcher { +/** + * invoke method + * + * @param mixed $controller + * @param mixed $params + * @param mixed $missingAction + * @access protected + * @return void + */ + function _invoke(&$controller, $params) { + restore_error_handler(); + if ($result = parent::_invoke($controller, $params)) { + if ($result[0] === 'missingAction') { + return $result; + } + } + set_error_handler('simpleTestErrorHandler'); + + return $controller; + } +/** + * cakeError method + * + * @param mixed $filename + * @access public + * @return void + */ + function cakeError($filename, $params) { + return array($filename, $params); + } +/** + * _stop method + * + * @access protected + * @return void + */ + function _stop() { + return true; + } +} +/** + * MyPluginAppController class + * + * @package cake + * @subpackage cake.tests.cases + */ +class MyPluginAppController extends AppController { +} +/** + * MyPluginController class + * + * @package cake + * @subpackage cake.tests.cases + */ +class MyPluginController extends MyPluginAppController { +/** + * name property + * + * @var string 'MyPlugin' + * @access public + */ + var $name = 'MyPlugin'; +/** + * uses property + * + * @var array + * @access public + */ + var $uses = array(); +/** + * index method + * + * @access public + * @return void + */ + function index() { + return true; + } +/** + * add method + * + * @access public + * @return void + */ + function add() { + return true; + } +/** + * admin_add method + * + * @param mixed $id + * @access public + * @return void + */ + function admin_add($id = null) { + return $id; + } +} +/** + * SomePagesController class + * + * @package cake + * @subpackage cake.tests.cases + */ +class SomePagesController extends AppController { +/** + * name property + * + * @var string 'SomePages' + * @access public + */ + var $name = 'SomePages'; +/** + * uses property + * + * @var array + * @access public + */ + var $uses = array(); +/** + * display method + * + * @param mixed $page + * @access public + * @return void + */ + function display($page = null) { + return $page; + } +/** + * index method + * + * @access public + * @return void + */ + function index() { + return true; + } +/** + * protected method + * + * @access protected + * @return void + */ + function _protected() { + return true; + } +/** + * redirect method overriding + * + * @access public + * @return void + */ + function redirect() { + echo 'this should not be accessible'; + } +} +/** + * OtherPagesController class + * + * @package cake + * @subpackage cake.tests.cases + */ +class OtherPagesController extends MyPluginAppController { +/** + * name property + * + * @var string 'OtherPages' + * @access public + */ + var $name = 'OtherPages'; +/** + * uses property + * + * @var array + * @access public + */ + var $uses = array(); +/** + * display method + * + * @param mixed $page + * @access public + * @return void + */ + function display($page = null) { + return $page; + } +/** + * index method + * + * @access public + * @return void + */ + function index() { + return true; + } +} +/** + * TestDispatchPagesController class + * + * @package cake + * @subpackage cake.tests.cases + */ +class TestDispatchPagesController extends AppController { +/** + * name property + * + * @var string 'TestDispatchPages' + * @access public + */ + var $name = 'TestDispatchPages'; +/** + * uses property + * + * @var array + * @access public + */ + var $uses = array(); +/** + * admin_index method + * + * @access public + * @return void + */ + function admin_index() { + return true; + } +/** + * camelCased method + * + * @access public + * @return void + */ + function camelCased() { + return true; + } +} +/** + * ArticlesTestAppController class + * + * @package cake + * @subpackage cake.tests.cases + */ +class ArticlesTestAppController extends AppController { +} +/** + * ArticlesTestController class + * + * @package cake + * @subpackage cake.tests.cases + */ +class ArticlesTestController extends ArticlesTestAppController { +/** + * name property + * + * @var string 'ArticlesTest' + * @access public + */ + var $name = 'ArticlesTest'; +/** + * uses property + * + * @var array + * @access public + */ + var $uses = array(); +/** + * admin_index method + * + * @access public + * @return void + */ + function admin_index() { + return true; + } +} +/** + * SomePostsController class + * + * @package cake + * @subpackage cake.tests.cases + */ +class SomePostsController extends AppController { +/** + * name property + * + * @var string 'SomePosts' + * @access public + */ + var $name = 'SomePosts'; +/** + * uses property + * + * @var array + * @access public + */ + var $uses = array(); +/** + * autoRender property + * + * @var bool false + * @access public + */ + var $autoRender = false; +/** + * beforeFilter method + * + * @access public + * @return void + */ + function beforeFilter() { + if ($this->params['action'] == 'index') { + $this->params['action'] = 'view'; + } else { + $this->params['action'] = 'change'; + } + $this->params['pass'] = array('changed'); + } +/** + * index method + * + * @access public + * @return void + */ + function index() { + return true; + } +/** + * change method + * + * @access public + * @return void + */ + function change() { + return true; + } +} +/** + * TestCachedPagesController class + * + * @package cake + * @subpackage cake.tests.cases + */ +class TestCachedPagesController extends AppController { +/** + * name property + * + * @var string 'TestCachedPages' + * @access public + */ + var $name = 'TestCachedPages'; +/** + * uses property + * + * @var array + * @access public + */ + var $uses = array(); +/** + * helpers property + * + * @var array + * @access public + */ + var $helpers = array('Cache'); +/** + * cacheAction property + * + * @var array + * @access public + */ + var $cacheAction = array( + 'index'=> '+2 sec', 'test_nocache_tags'=>'+2 sec', + 'view/' => '+2 sec' + ); +/** + * viewPath property + * + * @var string 'posts' + * @access public + */ + var $viewPath = 'posts'; +/** + * index method + * + * @access public + * @return void + */ + function index() { + $this->render(); + } +/** + * test_nocache_tags method + * + * @access public + * @return void + */ + function test_nocache_tags() { + $this->render(); + } +/** + * view method + * + * @access public + * @return void + */ + function view($id = null) { + $this->render('index'); + } +} +/** + * TimesheetsController class + * + * @package cake + * @subpackage cake.tests.cases + */ +class TimesheetsController extends AppController { +/** + * name property + * + * @var string 'Timesheets' + * @access public + */ + var $name = 'Timesheets'; +/** + * uses property + * + * @var array + * @access public + */ + var $uses = array(); +/** + * index method + * + * @access public + * @return void + */ + function index() { + return true; + } +} +/** + * DispatcherTest class + * + * @package cake + * @subpackage cake.tests.cases + */ +class DispatcherTest extends CakeTestCase { +/** + * setUp method + * + * @access public + * @return void + */ + function startTest() { + $this->_get = $_GET; + $_GET = array(); + $this->_post = $_POST; + $this->_files = $_FILES; + $this->_server = $_SERVER; + + $this->_app = Configure::read('App'); + Configure::write('App.base', false); + Configure::write('App.baseUrl', false); + Configure::write('App.dir', 'app'); + Configure::write('App.webroot', 'webroot'); + + $this->_cache = Configure::read('Cache'); + Configure::write('Cache.disable', true); + + $this->_vendorPaths = Configure::read('vendorPaths'); + $this->_pluginPaths = Configure::read('pluginPaths'); + $this->_viewPaths = Configure::read('viewPaths'); + $this->_controllerPaths = Configure::read('controllerPaths'); + $this->_debug = Configure::read('debug'); + + Configure::write('controllerPaths', Configure::corePaths('controller')); + Configure::write('viewPaths', Configure::corePaths('view')); + } +/** + * tearDown method + * + * @access public + * @return void + */ + function endTest() { + $_GET = $this->_get; + $_POST = $this->_post; + $_FILES = $this->_files; + $_SERVER = $this->_server; + Configure::write('App', $this->_app); + Configure::write('Cache', $this->_cache); + Configure::write('vendorPaths', $this->_vendorPaths); + Configure::write('pluginPaths', $this->_pluginPaths); + Configure::write('viewPaths', $this->_viewPaths); + Configure::write('controllerPaths', $this->_controllerPaths); + Configure::write('debug', $this->_debug); + } +/** + * testParseParamsWithoutZerosAndEmptyPost method + * + * @access public + * @return void + */ + function testParseParamsWithoutZerosAndEmptyPost() { + $Dispatcher =& new Dispatcher(); + $test = $Dispatcher->parseParams("/testcontroller/testaction/params1/params2/params3"); + $this->assertIdentical($test['controller'], 'testcontroller'); + $this->assertIdentical($test['action'], 'testaction'); + $this->assertIdentical($test['pass'][0], 'params1'); + $this->assertIdentical($test['pass'][1], 'params2'); + $this->assertIdentical($test['pass'][2], 'params3'); + $this->assertFalse(!empty($test['form'])); + } +/** + * testParseParamsReturnsPostedData method + * + * @access public + * @return void + */ + function testParseParamsReturnsPostedData() { + $_POST['testdata'] = "My Posted Content"; + $Dispatcher =& new Dispatcher(); + $test = $Dispatcher->parseParams("/"); + $this->assertTrue($test['form'], "Parsed URL not returning post data"); + $this->assertIdentical($test['form']['testdata'], "My Posted Content"); + } +/** + * testParseParamsWithSingleZero method + * + * @access public + * @return void + */ + function testParseParamsWithSingleZero() { + $Dispatcher =& new Dispatcher(); + $test = $Dispatcher->parseParams("/testcontroller/testaction/1/0/23"); + $this->assertIdentical($test['controller'], 'testcontroller'); + $this->assertIdentical($test['action'], 'testaction'); + $this->assertIdentical($test['pass'][0], '1'); + $this->assertPattern('/\\A(?:0)\\z/', $test['pass'][1]); + $this->assertIdentical($test['pass'][2], '23'); + } +/** + * testParseParamsWithManySingleZeros method + * + * @access public + * @return void + */ + function testParseParamsWithManySingleZeros() { + $Dispatcher =& new Dispatcher(); + $test = $Dispatcher->parseParams("/testcontroller/testaction/0/0/0/0/0/0"); + $this->assertPattern('/\\A(?:0)\\z/', $test['pass'][0]); + $this->assertPattern('/\\A(?:0)\\z/', $test['pass'][1]); + $this->assertPattern('/\\A(?:0)\\z/', $test['pass'][2]); + $this->assertPattern('/\\A(?:0)\\z/', $test['pass'][3]); + $this->assertPattern('/\\A(?:0)\\z/', $test['pass'][4]); + $this->assertPattern('/\\A(?:0)\\z/', $test['pass'][5]); + } +/** + * testParseParamsWithManyZerosInEachSectionOfUrl method + * + * @access public + * @return void + */ + function testParseParamsWithManyZerosInEachSectionOfUrl() { + $Dispatcher =& new Dispatcher(); + $test = $Dispatcher->parseParams("/testcontroller/testaction/000/0000/00000/000000/000000/0000000"); + $this->assertPattern('/\\A(?:000)\\z/', $test['pass'][0]); + $this->assertPattern('/\\A(?:0000)\\z/', $test['pass'][1]); + $this->assertPattern('/\\A(?:00000)\\z/', $test['pass'][2]); + $this->assertPattern('/\\A(?:000000)\\z/', $test['pass'][3]); + $this->assertPattern('/\\A(?:000000)\\z/', $test['pass'][4]); + $this->assertPattern('/\\A(?:0000000)\\z/', $test['pass'][5]); + } +/** + * testParseParamsWithMixedOneToManyZerosInEachSectionOfUrl method + * + * @access public + * @return void + */ + function testParseParamsWithMixedOneToManyZerosInEachSectionOfUrl() { + $Dispatcher =& new Dispatcher(); + $test = $Dispatcher->parseParams("/testcontroller/testaction/01/0403/04010/000002/000030/0000400"); + $this->assertPattern('/\\A(?:01)\\z/', $test['pass'][0]); + $this->assertPattern('/\\A(?:0403)\\z/', $test['pass'][1]); + $this->assertPattern('/\\A(?:04010)\\z/', $test['pass'][2]); + $this->assertPattern('/\\A(?:000002)\\z/', $test['pass'][3]); + $this->assertPattern('/\\A(?:000030)\\z/', $test['pass'][4]); + $this->assertPattern('/\\A(?:0000400)\\z/', $test['pass'][5]); + } +/** + * testQueryStringOnRoot method + * + * @access public + * @return void + */ + function testQueryStringOnRoot() { + Router::reload(); + Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home')); + Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display')); + + $_GET = array('coffee' => 'life', 'sleep' => 'sissies'); + $Dispatcher =& new Dispatcher(); + $uri = 'posts/home/?coffee=life&sleep=sissies'; + $result = $Dispatcher->parseParams($uri); + $this->assertPattern('/posts/', $result['controller']); + $this->assertPattern('/home/', $result['action']); + $this->assertTrue(isset($result['url']['sleep'])); + $this->assertTrue(isset($result['url']['coffee'])); + + $Dispatcher =& new Dispatcher(); + $uri = '/?coffee=life&sleep=sissy'; + $result = $Dispatcher->parseParams($uri); + $this->assertPattern('/pages/', $result['controller']); + $this->assertPattern('/display/', $result['action']); + $this->assertTrue(isset($result['url']['sleep'])); + $this->assertTrue(isset($result['url']['coffee'])); + $this->assertEqual($result['url']['coffee'], 'life'); + } +/** + * testFileUploadArrayStructure method + * + * @access public + * @return void + */ + function testFileUploadArrayStructure() { + $_FILES = array('data' => array('name' => array( + 'File' => array( + array('data' => 'cake_mssql_patch.patch'), + array('data' => 'controller.diff'), + array('data' => ''), + array('data' => ''), + ), + 'Post' => array('attachment' => 'jquery-1.2.1.js'), + ), + 'type' => array( + 'File' => array( + array('data' => ''), + array('data' => ''), + array('data' => ''), + array('data' => ''), + ), + 'Post' => array('attachment' => 'application/x-javascript'), + ), + 'tmp_name' => array( + 'File' => array( + array('data' => '/private/var/tmp/phpy05Ywj'), + array('data' => '/private/var/tmp/php7MBztY'), + array('data' => ''), + array('data' => ''), + ), + 'Post' => array('attachment' => '/private/var/tmp/phpEwlrIo'), + ), + 'error' => array( + 'File' => array( + array('data' => 0), + array('data' => 0), + array('data' => 4), + array('data' => 4) + ), + 'Post' => array('attachment' => 0) + ), + 'size' => array( + 'File' => array( + array('data' => 6271), + array('data' => 350), + array('data' => 0), + array('data' => 0), + ), + 'Post' => array('attachment' => 80469) + ), + )); + + $Dispatcher =& new Dispatcher(); + $result = $Dispatcher->parseParams('/'); + + $expected = array( + 'File' => array( + array('data' => array( + 'name' => 'cake_mssql_patch.patch', + 'type' => '', + 'tmp_name' => '/private/var/tmp/phpy05Ywj', + 'error' => 0, + 'size' => 6271, + ), + ), + array('data' => array( + 'name' => 'controller.diff', + 'type' => '', + 'tmp_name' => '/private/var/tmp/php7MBztY', + 'error' => 0, + 'size' => 350, + )), + array('data' => array( + 'name' => '', + 'type' => '', + 'tmp_name' => '', + 'error' => 4, + 'size' => 0, + )), + array('data' => array( + 'name' => '', + 'type' => '', + 'tmp_name' => '', + 'error' => 4, + 'size' => 0, + )), + ), + 'Post' => array('attachment' => array( + 'name' => 'jquery-1.2.1.js', + 'type' => 'application/x-javascript', + 'tmp_name' => '/private/var/tmp/phpEwlrIo', + 'error' => 0, + 'size' => 80469, + ))); + $this->assertEqual($result['data'], $expected); + + $_FILES = array( + 'data' => array( + 'name' => array( + 'Document' => array( + 1 => array( + 'birth_cert' => 'born on.txt', + 'passport' => 'passport.txt', + 'drivers_license' => 'ugly pic.jpg' + ), + 2 => array( + 'birth_cert' => 'aunt betty.txt', + 'passport' => 'betty-passport.txt', + 'drivers_license' => 'betty-photo.jpg' + ), + ), + ), + 'type' => array( + 'Document' => array( + 1 => array( + 'birth_cert' => 'application/octet-stream', + 'passport' => 'application/octet-stream', + 'drivers_license' => 'application/octet-stream', + ), + 2 => array( + 'birth_cert' => 'application/octet-stream', + 'passport' => 'application/octet-stream', + 'drivers_license' => 'application/octet-stream', + ) + ) + ), + 'tmp_name' => array( + 'Document' => array( + 1 => array( + 'birth_cert' => '/private/var/tmp/phpbsUWfH', + 'passport' => '/private/var/tmp/php7f5zLt', + 'drivers_license' => '/private/var/tmp/phpMXpZgT', + ), + 2 => array( + 'birth_cert' => '/private/var/tmp/php5kHZt0', + 'passport' => '/private/var/tmp/phpnYkOuM', + 'drivers_license' => '/private/var/tmp/php9Rq0P3', + ) + ) + ), + 'error' => array( + 'Document' => array( + 1 => array( + 'birth_cert' => 0, + 'passport' => 0, + 'drivers_license' => 0, + ), + 2 => array( + 'birth_cert' => 0, + 'passport' => 0, + 'drivers_license' => 0, + ) + ) + ), + 'size' => array( + 'Document' => array( + 1 => array( + 'birth_cert' => 123, + 'passport' => 458, + 'drivers_license' => 875, + ), + 2 => array( + 'birth_cert' => 876, + 'passport' => 976, + 'drivers_license' => 9783, + ) + ) + ) + ) + ); + $Dispatcher =& new Dispatcher(); + $result = $Dispatcher->parseParams('/'); + $expected = array( + 'Document' => array( + 1 => array( + 'birth_cert' => array( + 'name' => 'born on.txt', + 'tmp_name' => '/private/var/tmp/phpbsUWfH', + 'error' => 0, + 'size' => 123, + 'type' => 'application/octet-stream', + ), + 'passport' => array( + 'name' => 'passport.txt', + 'tmp_name' => '/private/var/tmp/php7f5zLt', + 'error' => 0, + 'size' => 458, + 'type' => 'application/octet-stream', + ), + 'drivers_license' => array( + 'name' => 'ugly pic.jpg', + 'tmp_name' => '/private/var/tmp/phpMXpZgT', + 'error' => 0, + 'size' => 875, + 'type' => 'application/octet-stream', + ), + ), + 2 => array( + 'birth_cert' => array( + 'name' => 'aunt betty.txt', + 'tmp_name' => '/private/var/tmp/php5kHZt0', + 'error' => 0, + 'size' => 876, + 'type' => 'application/octet-stream', + ), + 'passport' => array( + 'name' => 'betty-passport.txt', + 'tmp_name' => '/private/var/tmp/phpnYkOuM', + 'error' => 0, + 'size' => 976, + 'type' => 'application/octet-stream', + ), + 'drivers_license' => array( + 'name' => 'betty-photo.jpg', + 'tmp_name' => '/private/var/tmp/php9Rq0P3', + 'error' => 0, + 'size' => 9783, + 'type' => 'application/octet-stream', + ), + ), + ) + ); + $this->assertEqual($result['data'], $expected); + + + $_FILES = array( + 'data' => array( + 'name' => array('birth_cert' => 'born on.txt'), + 'type' => array('birth_cert' => 'application/octet-stream'), + 'tmp_name' => array('birth_cert' => '/private/var/tmp/phpbsUWfH'), + 'error' => array('birth_cert' => 0), + 'size' => array('birth_cert' => 123) + ) + ); + + $Dispatcher =& new Dispatcher(); + $result = $Dispatcher->parseParams('/'); + + $expected = array( + 'birth_cert' => array( + 'name' => 'born on.txt', + 'type' => 'application/octet-stream', + 'tmp_name' => '/private/var/tmp/phpbsUWfH', + 'error' => 0, + 'size' => 123 + ) + ); + + $this->assertEqual($result['data'], $expected); + } +/** + * testGetUrl method + * + * @access public + * @return void + */ + function testGetUrl() { + $Dispatcher =& new Dispatcher(); + $Dispatcher->base = '/app/webroot/index.php'; + $uri = '/app/webroot/index.php/posts/add'; + $result = $Dispatcher->getUrl($uri); + $expected = 'posts/add'; + $this->assertEqual($expected, $result); + + Configure::write('App.baseUrl', '/app/webroot/index.php'); + + $uri = '/posts/add'; + $result = $Dispatcher->getUrl($uri); + $expected = 'posts/add'; + $this->assertEqual($expected, $result); + + $_GET['url'] = array(); + Configure::write('App.base', '/control'); + $Dispatcher =& new Dispatcher(); + $Dispatcher->baseUrl(); + $uri = '/control/students/browse'; + $result = $Dispatcher->getUrl($uri); + $expected = 'students/browse'; + $this->assertEqual($expected, $result); + + $_GET['url'] = array(); + $Dispatcher =& new Dispatcher(); + $Dispatcher->base = ''; + $uri = '/?/home'; + $result = $Dispatcher->getUrl($uri); + $expected = '?/home'; + $this->assertEqual($expected, $result); + + } +/** + * testBaseUrlAndWebrootWithModRewrite method + * + * @access public + * @return void + */ + function testBaseUrlAndWebrootWithModRewrite() { + $Dispatcher =& new Dispatcher(); + + $Dispatcher->base = false; + $_SERVER['DOCUMENT_ROOT'] = '/cake/repo/branches'; + $_SERVER['SCRIPT_FILENAME'] = '/cake/repo/branches/1.2.x.x/app/webroot/index.php'; + $_SERVER['PHP_SELF'] = '/1.2.x.x/app/webroot/index.php'; + $result = $Dispatcher->baseUrl(); + $expected = '/1.2.x.x'; + $this->assertEqual($expected, $result); + $expectedWebroot = '/1.2.x.x/'; + $this->assertEqual($expectedWebroot, $Dispatcher->webroot); + + $Dispatcher->base = false; + $_SERVER['DOCUMENT_ROOT'] = '/cake/repo/branches/1.2.x.x/app/webroot'; + $_SERVER['SCRIPT_FILENAME'] = '/cake/repo/branches/1.2.x.x/app/webroot/index.php'; + $_SERVER['PHP_SELF'] = '/index.php'; + $result = $Dispatcher->baseUrl(); + $expected = ''; + $this->assertEqual($expected, $result); + $expectedWebroot = '/'; + $this->assertEqual($expectedWebroot, $Dispatcher->webroot); + + $Dispatcher->base = false; + $_SERVER['DOCUMENT_ROOT'] = '/cake/repo/branches/1.2.x.x/test/'; + $_SERVER['SCRIPT_FILENAME'] = '/cake/repo/branches/1.2.x.x/test/webroot/index.php'; + $_SERVER['PHP_SELF'] = '/webroot/index.php'; + $result = $Dispatcher->baseUrl(); + $expected = ''; + $this->assertEqual($expected, $result); + $expectedWebroot = '/'; + $this->assertEqual($expectedWebroot, $Dispatcher->webroot); + + $Dispatcher->base = false;; + $_SERVER['DOCUMENT_ROOT'] = '/some/apps/where'; + $_SERVER['SCRIPT_FILENAME'] = '/some/apps/where/app/webroot/index.php'; + $_SERVER['PHP_SELF'] = '/some/apps/where/app/webroot/index.php'; + $result = $Dispatcher->baseUrl(); + $expected = '/some/apps/where'; + $this->assertEqual($expected, $result); + $expectedWebroot = '/some/apps/where/'; + $this->assertEqual($expectedWebroot, $Dispatcher->webroot); + + + Configure::write('App.dir', 'auth'); + + $Dispatcher->base = false;; + $_SERVER['DOCUMENT_ROOT'] = '/cake/repo/branches'; + $_SERVER['SCRIPT_FILENAME'] = '/cake/repo/branches/demos/auth/webroot/index.php'; + $_SERVER['PHP_SELF'] = '/demos/auth/webroot/index.php'; + + $result = $Dispatcher->baseUrl(); + $expected = '/demos/auth'; + $this->assertEqual($expected, $result); + $expectedWebroot = '/demos/auth/'; + $this->assertEqual($expectedWebroot, $Dispatcher->webroot); + + Configure::write('App.dir', 'code'); + + $Dispatcher->base = false;; + $_SERVER['DOCUMENT_ROOT'] = '/Library/WebServer/Documents'; + $_SERVER['SCRIPT_FILENAME'] = '/Library/WebServer/Documents/clients/PewterReport/code/webroot/index.php'; + $_SERVER['PHP_SELF'] = '/clients/PewterReport/code/webroot/index.php'; + $result = $Dispatcher->baseUrl(); + $expected = '/clients/PewterReport/code'; + $this->assertEqual($expected, $result); + $expectedWebroot = '/clients/PewterReport/code/'; + $this->assertEqual($expectedWebroot, $Dispatcher->webroot); + } +/** + * testBaseUrlwithModRewriteAlias method + * + * @access public + * @return void + */ + function testBaseUrlwithModRewriteAlias() { + $_SERVER['DOCUMENT_ROOT'] = '/home/aplusnur/public_html'; + $_SERVER['SCRIPT_FILENAME'] = '/home/aplusnur/cake2/app/webroot/index.php'; + $_SERVER['PHP_SELF'] = '/control/index.php'; + + Configure::write('App.base', '/control'); + + $Dispatcher =& new Dispatcher(); + $result = $Dispatcher->baseUrl(); + $expected = '/control'; + $this->assertEqual($expected, $result); + $expectedWebroot = '/control/'; + $this->assertEqual($expectedWebroot, $Dispatcher->webroot); + + Configure::write('App.base', false); + Configure::write('App.dir', 'affiliate'); + Configure::write('App.webroot', 'newaffiliate'); + + $_SERVER['DOCUMENT_ROOT'] = '/var/www/abtravaff/html'; + $_SERVER['SCRIPT_FILENAME'] = '/var/www/abtravaff/html/newaffiliate/index.php'; + $_SERVER['PHP_SELF'] = '/newaffiliate/index.php'; + $Dispatcher =& new Dispatcher(); + $result = $Dispatcher->baseUrl(); + $expected = '/newaffiliate'; + $this->assertEqual($expected, $result); + $expectedWebroot = '/newaffiliate/'; + $this->assertEqual($expectedWebroot, $Dispatcher->webroot); + } +/** + * testBaseUrlAndWebrootWithBaseUrl method + * + * @access public + * @return void + */ + function testBaseUrlAndWebrootWithBaseUrl() { + $Dispatcher =& new Dispatcher(); + + Configure::write('App.dir', 'app'); + + Configure::write('App.baseUrl', '/app/webroot/index.php'); + $result = $Dispatcher->baseUrl(); + $expected = '/app/webroot/index.php'; + $this->assertEqual($expected, $result); + $expectedWebroot = '/app/webroot/'; + $this->assertEqual($expectedWebroot, $Dispatcher->webroot); + + Configure::write('App.baseUrl', '/app/webroot/test.php'); + $result = $Dispatcher->baseUrl(); + $expected = '/app/webroot/test.php'; + $this->assertEqual($expected, $result); + $expectedWebroot = '/app/webroot/'; + $this->assertEqual($expectedWebroot, $Dispatcher->webroot); + + Configure::write('App.baseUrl', '/app/index.php'); + $result = $Dispatcher->baseUrl(); + $expected = '/app/index.php'; + $this->assertEqual($expected, $result); + $expectedWebroot = '/app/webroot/'; + $this->assertEqual($expectedWebroot, $Dispatcher->webroot); + + Configure::write('App.baseUrl', '/index.php'); + $result = $Dispatcher->baseUrl(); + $expected = '/index.php'; + $this->assertEqual($expected, $result); + $expectedWebroot = '/app/webroot/'; + $this->assertEqual($expectedWebroot, $Dispatcher->webroot); + + Configure::write('App.baseUrl', '/CakeBB/app/webroot/index.php'); + $result = $Dispatcher->baseUrl(); + $expected = '/CakeBB/app/webroot/index.php'; + $this->assertEqual($expected, $result); + $expectedWebroot = '/CakeBB/app/webroot/'; + $this->assertEqual($expectedWebroot, $Dispatcher->webroot); + + Configure::write('App.baseUrl', '/CakeBB/app/index.php'); + $result = $Dispatcher->baseUrl(); + $expected = '/CakeBB/app/index.php'; + $this->assertEqual($expected, $result); + $expectedWebroot = '/CakeBB/app/webroot/'; + $this->assertEqual($expectedWebroot, $Dispatcher->webroot); + + Configure::write('App.baseUrl', '/CakeBB/index.php'); + $result = $Dispatcher->baseUrl(); + $expected = '/CakeBB/index.php'; + $this->assertEqual($expected, $result); + $expectedWebroot = '/CakeBB/app/webroot/'; + $this->assertEqual($expectedWebroot, $Dispatcher->webroot); + + Configure::write('App.baseUrl', '/dbhauser/index.php'); + $_SERVER['DOCUMENT_ROOT'] = '/kunden/homepages/4/d181710652/htdocs/joomla'; + $_SERVER['SCRIPT_FILENAME'] = '/kunden/homepages/4/d181710652/htdocs/joomla/dbhauser/index.php'; + $result = $Dispatcher->baseUrl(); + $expected = '/dbhauser/index.php'; + $this->assertEqual($expected, $result); + $expectedWebroot = '/dbhauser/app/webroot/'; + $this->assertEqual($expectedWebroot, $Dispatcher->webroot); + } +/** + * testBaseUrlAndWebrootWithBase method + * + * @access public + * @return void + */ + function testBaseUrlAndWebrootWithBase() { + $Dispatcher =& new Dispatcher(); + $Dispatcher->base = '/app'; + $result = $Dispatcher->baseUrl(); + $expected = '/app'; + $this->assertEqual($expected, $result); + $expectedWebroot = '/app/'; + $this->assertEqual($expectedWebroot, $Dispatcher->webroot); + + $Dispatcher->base = ''; + $result = $Dispatcher->baseUrl(); + $expected = ''; + $this->assertEqual($expected, $result); + $expectedWebroot = '/'; + $this->assertEqual($expectedWebroot, $Dispatcher->webroot); + + Configure::write('App.dir', 'testbed'); + $Dispatcher->base = '/cake/testbed/webroot'; + $result = $Dispatcher->baseUrl(); + $expected = '/cake/testbed/webroot'; + $this->assertEqual($expected, $result); + $expectedWebroot = '/cake/testbed/webroot/'; + $this->assertEqual($expectedWebroot, $Dispatcher->webroot); + } +/** + * testMissingController method + * + * @access public + * @return void + */ + function testMissingController() { + $Dispatcher =& new TestDispatcher(); + Configure::write('App.baseUrl','/index.php'); + $url = 'some_controller/home/param:value/param2:value2'; + $controller = $Dispatcher->dispatch($url, array('return' => 1)); + $expected = array('missingController', array(array( + 'className' => 'SomeControllerController', + 'webroot' => '/app/webroot/', + 'url' => 'some_controller/home/param:value/param2:value2', + 'base' => '/index.php' + ))); + $this->assertEqual($expected, $controller); + } +/** + * testPrivate method + * + * @access public + * @return void + */ + function testPrivate() { + $Dispatcher =& new TestDispatcher(); + Configure::write('App.baseUrl','/index.php'); + $url = 'some_pages/_protected/param:value/param2:value2'; + + $controller = $Dispatcher->dispatch($url, array('return' => 1)); + + $expected = array('privateAction', array(array( + 'className' => 'SomePagesController', + 'action' => '_protected', + 'webroot' => '/app/webroot/', + 'url' => 'some_pages/_protected/param:value/param2:value2', + 'base' => '/index.php' + ))); + $this->assertEqual($controller, $expected); + } +/** + * testMissingAction method + * + * @access public + * @return void + */ + function testMissingAction() { + $Dispatcher =& new TestDispatcher(); + Configure::write('App.baseUrl','/index.php'); + $url = 'some_pages/home/param:value/param2:value2'; + + $controller = $Dispatcher->dispatch($url, array('return'=> 1)); + + $expected = array('missingAction', array(array( + 'className' => 'SomePagesController', + 'action' => 'home', + 'webroot' => '/app/webroot/', + 'url' => '/index.php/some_pages/home/param:value/param2:value2', + 'base' => '/index.php' + ))); + $this->assertEqual($expected, $controller); + + $Dispatcher =& new TestDispatcher(); + Configure::write('App.baseUrl','/index.php'); + $url = 'some_pages/redirect/param:value/param2:value2'; + + $controller = $Dispatcher->dispatch($url, array('return'=> 1)); + + $expected = array('missingAction', array(array( + 'className' => 'SomePagesController', + 'action' => 'redirect', + 'webroot' => '/app/webroot/', + 'url' => '/index.php/some_pages/redirect/param:value/param2:value2', + 'base' => '/index.php' + ))); + $this->assertEqual($expected, $controller); + } +/** + * testDispatch method + * + * @access public + * @return void + */ + function testDispatch() { + $Dispatcher =& new TestDispatcher(); + Configure::write('App.baseUrl','/index.php'); + $url = 'pages/home/param:value/param2:value2'; + + $controller = $Dispatcher->dispatch($url, array('return' => 1)); + $expected = 'Pages'; + $this->assertEqual($expected, $controller->name); + + $expected = array('0' => 'home', 'param' => 'value', 'param2' => 'value2'); + $this->assertIdentical($expected, $controller->passedArgs); + + Configure::write('App.baseUrl','/pages/index.php'); + + $url = 'pages/home'; + $controller = $Dispatcher->dispatch($url, array('return' => 1)); + + $expected = 'Pages'; + $this->assertEqual($expected, $controller->name); + + $url = 'pages/home/'; + $controller = $Dispatcher->dispatch($url, array('return' => 1)); + + $expected = 'Pages'; + $this->assertEqual($expected, $controller->name); + + unset($Dispatcher); + + $Dispatcher =& new TestDispatcher(); + Configure::write('App.baseUrl','/timesheets/index.php'); + + $url = 'timesheets'; + $controller = $Dispatcher->dispatch($url, array('return' => 1)); + + $expected = 'Timesheets'; + $this->assertEqual($expected, $controller->name); + + $url = 'timesheets/'; + $controller = $Dispatcher->dispatch($url, array('return' => 1)); + + $this->assertEqual('Timesheets', $controller->name); + $this->assertEqual('/timesheets/index.php', $Dispatcher->base); + + + $url = 'test_dispatch_pages/camelCased'; + $controller = $Dispatcher->dispatch($url, array('return' => 1)); + $this->assertEqual('TestDispatchPages', $controller->name); + } +/** + * testDispatchWithArray method + * + * @access public + * @return void + */ + function testDispatchWithArray() { + $Dispatcher =& new TestDispatcher(); + Configure::write('App.baseUrl','/index.php'); + $url = 'pages/home/param:value/param2:value2'; + + $url = array('controller' => 'pages', 'action' => 'display'); + $controller = $Dispatcher->dispatch($url, array('pass' => array('home'), 'named' => array('param' => 'value', 'param2' => 'value2'), 'return' => 1)); + $expected = 'Pages'; + $this->assertEqual($expected, $controller->name); + + $expected = array('0' => 'home', 'param' => 'value', 'param2' => 'value2'); + $this->assertIdentical($expected, $controller->passedArgs); + } +/** + * testAdminDispatch method + * + * @access public + * @return void + */ + function testAdminDispatch() { + $_POST = array(); + $Dispatcher =& new TestDispatcher(); + Configure::write('Routing.admin', 'admin'); + Configure::write('App.baseUrl','/cake/repo/branches/1.2.x.x/index.php'); + $url = 'admin/test_dispatch_pages/index/param:value/param2:value2'; + + Router::reload(); + $Router =& Router::getInstance(); + $controller = $Dispatcher->dispatch($url, array('return' => 1)); + + $expected = 'TestDispatchPages'; + $this->assertEqual($expected, $controller->name); + + $expected = array('param' => 'value', 'param2' => 'value2'); + $this->assertIdentical($expected, $controller->passedArgs); + $this->assertTrue($controller->params['admin']); + + $expected = '/cake/repo/branches/1.2.x.x/index.php/admin/test_dispatch_pages/index/param:value/param2:value2'; + $this->assertIdentical($expected, $controller->here); + + $expected = '/cake/repo/branches/1.2.x.x/index.php'; + $this->assertIdentical($expected, $controller->base); + } +/** + * testPluginDispatch method + * + * @access public + * @return void + */ + function testPluginDispatch() { + $_POST = array(); + $_SERVER['PHP_SELF'] = '/cake/repo/branches/1.2.x.x/index.php'; + + Router::reload(); + $Dispatcher =& new TestDispatcher(); + Router::connect('/my_plugin/:controller/*', array('plugin'=>'my_plugin', 'controller'=>'pages', 'action'=>'display')); + + $Dispatcher->base = false; + $url = 'my_plugin/some_pages/home/param:value/param2:value2'; + $controller = $Dispatcher->dispatch($url, array('return' => 1)); + + $result = $Dispatcher->parseParams($url); + $expected = array( + 'pass' => array('home'), + 'named' => array('param'=> 'value', 'param2'=> 'value2'), 'plugin'=> 'my_plugin', + 'controller'=> 'some_pages', 'action'=> 'display', 'form'=> null, + 'url'=> array('url'=> 'my_plugin/some_pages/home/param:value/param2:value2'), + ); + ksort($expected); + ksort($result); + + $this->assertEqual($expected, $result); + + $expected = 'my_plugin'; + $this->assertIdentical($expected, $controller->plugin); + + $expected = 'SomePages'; + $this->assertIdentical($expected, $controller->name); + + $expected = 'some_pages'; + $this->assertIdentical($expected, $controller->params['controller']); + + $expected = array('0' => 'home', 'param'=>'value', 'param2'=>'value2'); + $this->assertIdentical($expected, $controller->passedArgs); + + $expected = '/cake/repo/branches/1.2.x.x/my_plugin/some_pages/home/param:value/param2:value2'; + $this->assertIdentical($expected, $controller->here); + + $expected = '/cake/repo/branches/1.2.x.x'; + $this->assertIdentical($expected, $controller->base); + } +/** + * testAutomaticPluginDispatch method + * + * @access public + * @return void + */ + function testAutomaticPluginDispatch() { + $_POST = array(); + $_SERVER['PHP_SELF'] = '/cake/repo/branches/1.2.x.x/index.php'; + + Router::reload(); + $Dispatcher =& new TestDispatcher(); + Router::connect('/my_plugin/:controller/:action/*', array('plugin'=>'my_plugin', 'controller'=>'pages', 'action'=>'display')); + + $Dispatcher->base = false; + + $url = 'my_plugin/other_pages/index/param:value/param2:value2'; + $controller = $Dispatcher->dispatch($url, array('return' => 1)); + + $expected = 'my_plugin'; + $this->assertIdentical($expected, $controller->plugin); + + $expected = 'OtherPages'; + $this->assertIdentical($expected, $controller->name); + + $expected = 'index'; + $this->assertIdentical($expected, $controller->action); + + $expected = array('param' => 'value', 'param2' => 'value2'); + $this->assertIdentical($expected, $controller->passedArgs); + + $expected = '/cake/repo/branches/1.2.x.x/my_plugin/other_pages/index/param:value/param2:value2'; + $this->assertIdentical($expected, $controller->here); + + $expected = '/cake/repo/branches/1.2.x.x'; + $this->assertIdentical($expected, $controller->base); + } +/** + * testAutomaticPluginControllerDispatch method + * + * @access public + * @return void + */ + function testAutomaticPluginControllerDispatch() { + $_POST = array(); + $_SERVER['PHP_SELF'] = '/cake/repo/branches/1.2.x.x/index.php'; + + Router::reload(); + $Dispatcher =& new TestDispatcher(); + $Dispatcher->base = false; + + $url = 'my_plugin/add/param:value/param2:value2'; + + $controller = $Dispatcher->dispatch($url, array('return' => 1)); + + $expected = 'my_plugin'; + $this->assertIdentical($controller->plugin, $expected); + + $expected = 'MyPlugin'; + $this->assertIdentical($controller->name, $expected); + + $expected = 'add'; + $this->assertIdentical($controller->action, $expected); + + $expected = array('param' => 'value', 'param2' => 'value2'); + $this->assertEqual($controller->params['named'], $expected); + + + Router::reload(); + $Dispatcher =& new TestDispatcher(); + $Dispatcher->base = false; + + /* Simulates the Route for a real plugin, installed in APP/plugins */ + Router::connect('/my_plugin/:controller/:action/*', array('plugin' => 'my_plugin')); + + $plugin = 'MyPlugin'; + $pluginUrl = Inflector::underscore($plugin); + + $url = $pluginUrl; + $controller = $Dispatcher->dispatch($url, array('return' => 1)); + + $expected = $pluginUrl; + $this->assertIdentical($controller->plugin, $expected); + + $expected = $plugin; + $this->assertIdentical($controller->name, $expected); + + $expected = 'index'; + $this->assertIdentical($controller->action, $expected); + + $expected = $pluginUrl; + $this->assertEqual($controller->params['controller'], $expected); + + + Configure::write('Routing.admin', 'admin'); + + Router::reload(); + $Dispatcher =& new TestDispatcher(); + $Dispatcher->base = false; + + $url = 'admin/my_plugin/add/5/param:value/param2:value2'; + $controller = $Dispatcher->dispatch($url, array('return' => 1)); + + $expected = 'my_plugin'; + $this->assertIdentical($controller->plugin, $expected); + + $expected = 'MyPlugin'; + $this->assertIdentical($controller->name, $expected); + + $expected = 'admin_add'; + $this->assertIdentical($controller->action, $expected); + + $expected = array(0 => 5, 'param'=>'value', 'param2'=>'value2'); + $this->assertEqual($controller->passedArgs, $expected); + + Router::reload(); + + $Dispatcher =& new TestDispatcher(); + $Dispatcher->base = false; + + $controller = $Dispatcher->dispatch('admin/articles_test', array('return' => 1)); + + $expected = 'articles_test'; + $this->assertIdentical($controller->plugin, $expected); + + $expected = 'ArticlesTest'; + $this->assertIdentical($controller->name, $expected); + + $expected = 'admin_index'; + $this->assertIdentical($controller->action, $expected); + + $expected = array( + 'pass'=> array(), 'named' => array(), 'controller' => 'articles_test', 'plugin' => 'articles_test', 'action' => 'admin_index', + 'prefix' => 'admin', 'admin' => true, 'form' => array(), 'url' => array('url' => 'admin/articles_test'), 'return' => 1 + ); + $this->assertEqual($controller->params, $expected); + } +/** + * test Plugin dispatching without controller name and using + * plugin short form instead. + * + * @return void + **/ + function testAutomaticPluginDispatchWithShortAccess() { + $_POST = array(); + $_SERVER['PHP_SELF'] = '/cake/repo/branches/1.2.x.x/index.php'; + + Router::reload(); + Router::connect('/my_plugin/:controller/:action/*', array('plugin'=>'my_plugin')); + + $Dispatcher =& new TestDispatcher(); + $Dispatcher->base = false; + + $url = 'my_plugin/my_plugin/add'; + $controller = $Dispatcher->dispatch($url, array('return' => 1)); + $this->assertFalse(isset($controller->params['pass'][0])); + + $Dispatcher =& new TestDispatcher(); + $Dispatcher->base = false; + + $url = 'my_plugin/my_plugin/add/0'; + $controller = $Dispatcher->dispatch($url, array('return' => 1)); + $this->assertTrue(isset($controller->params['pass'][0])); + + $Dispatcher =& new TestDispatcher(); + $Dispatcher->base = false; + + $url = 'my_plugin/add'; + $controller = $Dispatcher->dispatch($url, array('return' => 1)); + + $this->assertFalse(isset($controller->params['pass'][0])); + + $Dispatcher =& new TestDispatcher(); + $Dispatcher->base = false; + + $url = 'my_plugin/add/0'; + $controller = $Dispatcher->dispatch($url, array('return' => 1)); + $this->assertIdentical('0',$controller->params['pass'][0]); + + $Dispatcher =& new TestDispatcher(); + $Dispatcher->base = false; + + $url = 'my_plugin/add/1'; + $controller = $Dispatcher->dispatch($url, array('return' => 1)); + $this->assertIdentical('1',$controller->params['pass'][0]); + } +/** + * testAutomaticPluginControllerMissingActionDispatch method + * + * @access public + * @return void + */ + function testAutomaticPluginControllerMissingActionDispatch() { + $_POST = array(); + $_SERVER['PHP_SELF'] = '/cake/repo/branches/1.2.x.x/index.php'; + + Router::reload(); + $Dispatcher =& new TestDispatcher(); + $Dispatcher->base = false; + + $url = 'my_plugin/not_here/param:value/param2:value2'; + $controller = $Dispatcher->dispatch($url, array('return'=> 1)); + + $expected = array('missingAction', array(array( + 'className' => 'MyPluginController', + 'action' => 'not_here', + 'webroot' => '/cake/repo/branches/1.2.x.x/', + 'url' => '/cake/repo/branches/1.2.x.x/my_plugin/not_here/param:value/param2:value2', + 'base' => '/cake/repo/branches/1.2.x.x' + ))); + $this->assertIdentical($expected, $controller); + + Router::reload(); + $Dispatcher =& new TestDispatcher(); + $Dispatcher->base = false; + + $url = 'my_plugin/param:value/param2:value2'; + $controller = $Dispatcher->dispatch($url, array('return'=> 1)); + + $expected = array('missingAction', array(array( + 'className' => 'MyPluginController', + 'action' => 'param:value', + 'webroot' => '/cake/repo/branches/1.2.x.x/', + 'url' => '/cake/repo/branches/1.2.x.x/my_plugin/param:value/param2:value2', + 'base' => '/cake/repo/branches/1.2.x.x' + ))); + $this->assertIdentical($expected, $controller); + } +/** + * testPrefixProtection method + * + * @access public + * @return void + */ + function testPrefixProtection() { + $_POST = array(); + $_SERVER['PHP_SELF'] = '/cake/repo/branches/1.2.x.x/index.php'; + + Router::reload(); + Router::connect('/admin/:controller/:action/*', array('prefix'=>'admin'), array('controller', 'action')); + + $Dispatcher =& new TestDispatcher(); + $Dispatcher->base = false; + + $url = 'test_dispatch_pages/admin_index/param:value/param2:value2'; + $controller = $Dispatcher->dispatch($url, array('return' => 1)); + + $expected = array('privateAction', array(array( + 'className' => 'TestDispatchPagesController', + 'action' => 'admin_index', + 'webroot' => '/cake/repo/branches/1.2.x.x/', + 'url' => 'test_dispatch_pages/admin_index/param:value/param2:value2', + 'base' => '/cake/repo/branches/1.2.x.x' + ))); + $this->assertIdentical($expected, $controller); + } +/** + * undocumented function + * + * @return void + **/ + function testTestPluginDispatch() { + $Dispatcher =& new TestDispatcher(); + $_back = Configure::read('pluginPaths'); + Configure::write('pluginPaths', array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)); + $url = '/test_plugin/tests/index'; + $result = $Dispatcher->dispatch($url, array('return' => 1)); + $this->assertTrue(class_exists('TestsController')); + $this->assertTrue(class_exists('TestPluginAppController')); + $this->assertTrue(class_exists('OtherComponentComponent')); + $this->assertTrue(class_exists('PluginsComponentComponent')); + + Configure::write('pluginPaths', $_back); + } +/** + * testChangingParamsFromBeforeFilter method + * + * @access public + * @return void + */ + function testChangingParamsFromBeforeFilter() { + $_SERVER['PHP_SELF'] = '/cake/repo/branches/1.2.x.x/index.php'; + $Dispatcher =& new TestDispatcher(); + $url = 'some_posts/index/param:value/param2:value2'; + $controller = $Dispatcher->dispatch($url, array('return' => 1)); + + $expected = array('missingAction', array(array( + 'className' => 'SomePostsController', + 'action' => 'view', + 'webroot' => '/cake/repo/branches/1.2.x.x/', + 'url' => '/cake/repo/branches/1.2.x.x/some_posts/index/param:value/param2:value2', + 'base' => '/cake/repo/branches/1.2.x.x' + ))); + $this->assertEqual($expected, $controller); + + $url = 'some_posts/something_else/param:value/param2:value2'; + $controller = $Dispatcher->dispatch($url, array('return' => 1)); + + $expected = 'SomePosts'; + $this->assertEqual($expected, $controller->name); + + $expected = 'change'; + $this->assertEqual($expected, $controller->action); + + $expected = array('changed'); + $this->assertIdentical($expected, $controller->params['pass']); + } +/** + * testStaticAssets method + * + * @access public + * @return void + */ + function testStaticAssets() { + Router::reload(); + $Configure = Configure::getInstance(); + $Configure->__objects = null; + + Configure::write('pluginPaths', array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)); + Configure::write('vendorPaths', array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'vendors'. DS)); + + $Dispatcher =& new TestDispatcher(); + + Configure::write('debug', 0); + ob_start(); + $Dispatcher->dispatch('/img/test.jpg'); + $result = ob_get_clean(); + $file = file_get_contents(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'vendors' . DS . 'img' . DS . 'test.jpg'); + $this->assertEqual($file, $result); + + + Configure::write('debug', 0); + $Dispatcher->params = $Dispatcher->parseParams('css/test_asset.css'); + + ob_start(); + $Dispatcher->cached('css/test_asset.css'); + $result = ob_get_clean(); + $this->assertEqual('this is the test asset css file', $result); + + + ob_start(); + $Dispatcher->cached('test_plugin/js/test_plugin/test.js'); + $result = ob_get_clean(); + $this->assertEqual('alert("Test App");', $result); + + + Configure::write('debug', 0); + $Dispatcher->params = $Dispatcher->parseParams('test_plugin/js/test_plugin/test.js'); + ob_start(); + $Dispatcher->cached('test_plugin/js/test_plugin/test.js'); + $result = ob_get_clean(); + $this->assertEqual('alert("Test App");', $result); + + + Configure::write('debug', 0); + $Dispatcher->params = $Dispatcher->parseParams('test_plugin/css/test_plugin_asset.css'); + ob_start(); + $Dispatcher->cached('test_plugin/css/test_plugin_asset.css'); + $result = ob_get_clean(); + $this->assertEqual('this is the test plugin asset css file', $result); + + + Configure::write('debug', 0); + $Dispatcher->params = $Dispatcher->parseParams('test_plugin/img/cake.icon.gif'); + ob_start(); + $Dispatcher->cached('test_plugin/img/cake.icon.gif'); + $result = ob_get_clean(); + $file = file_get_contents(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS . 'test_plugin' .DS . 'vendors' . DS . 'img' . DS . 'cake.icon.gif'); + $this->assertEqual($file, $result); + + header('Content-type: text/html');//reset the header content-type without page can render as plain text. + } +/** + * testFullPageCachingDispatch method + * + * @access public + * @return void + */ + function testFullPageCachingDispatch() { + Configure::write('Cache.disable', false); + Configure::write('Cache.check', true); + Configure::write('debug', 2); + + $_POST = array(); + $_SERVER['PHP_SELF'] = '/'; + + Router::reload(); + Router::connect('/', array('controller' => 'test_cached_pages', 'action' => 'index')); + + Configure::write('viewPaths', array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS)); + + $dispatcher =& new Dispatcher(); + $dispatcher->base = false; + + $url = '/'; + + ob_start(); + $dispatcher->dispatch($url); + $out = ob_get_clean(); + + ob_start(); + $dispatcher->cached($url); + $cached = ob_get_clean(); + + $result = str_replace(array("\t", "\r\n", "\n"), "", $out); + $cached = preg_replace('/<!--+[^<>]+-->/', '', $cached); + $expected = str_replace(array("\t", "\r\n", "\n"), "", $cached); + + $this->assertEqual($result, $expected); + + $filename = $this->__cachePath($dispatcher->here); + unlink($filename); + + $dispatcher->base = false; + $url = 'test_cached_pages/index'; + + ob_start(); + $dispatcher->dispatch($url); + $out = ob_get_clean(); + + ob_start(); + $dispatcher->cached($url); + $cached = ob_get_clean(); + + $result = str_replace(array("\t", "\r\n", "\n"), "", $out); + $cached = preg_replace('/<!--+[^<>]+-->/', '', $cached); + $expected = str_replace(array("\t", "\r\n", "\n"), "", $cached); + + $this->assertEqual($result, $expected); + $filename = $this->__cachePath($dispatcher->here); + unlink($filename); + + $url = 'TestCachedPages/index'; + + ob_start(); + $dispatcher->dispatch($url); + $out = ob_get_clean(); + + ob_start(); + $dispatcher->cached($url); + $cached = ob_get_clean(); + + $result = str_replace(array("\t", "\r\n", "\n"), "", $out); + $cached = preg_replace('/<!--+[^<>]+-->/', '', $cached); + $expected = str_replace(array("\t", "\r\n", "\n"), "", $cached); + + $this->assertEqual($result, $expected); + $filename = $this->__cachePath($dispatcher->here); + unlink($filename); + + $url = 'TestCachedPages/test_nocache_tags'; + + ob_start(); + $dispatcher->dispatch($url); + $out = ob_get_clean(); + + ob_start(); + $dispatcher->cached($url); + $cached = ob_get_clean(); + + $result = str_replace(array("\t", "\r\n", "\n"), "", $out); + $cached = preg_replace('/<!--+[^<>]+-->/', '', $cached); + $expected = str_replace(array("\t", "\r\n", "\n"), "", $cached); + + $this->assertEqual($result, $expected); + $filename = $this->__cachePath($dispatcher->here); + unlink($filename); + + $url = 'test_cached_pages/view/param/param'; + + ob_start(); + $dispatcher->dispatch($url); + $out = ob_get_clean(); + + ob_start(); + $dispatcher->cached($url); + $cached = ob_get_clean(); + + $result = str_replace(array("\t", "\r\n", "\n"), "", $out); + $cached = preg_replace('/<!--+[^<>]+-->/', '', $cached); + $expected = str_replace(array("\t", "\r\n", "\n"), "", $cached); + + $this->assertEqual($result, $expected); + $filename = $this->__cachePath($dispatcher->here); + unlink($filename); + + $url = 'test_cached_pages/view/foo:bar/value:goo'; + + ob_start(); + $dispatcher->dispatch($url); + $out = ob_get_clean(); + + ob_start(); + $dispatcher->cached($url); + $cached = ob_get_clean(); + + $result = str_replace(array("\t", "\r\n", "\n"), "", $out); + $cached = preg_replace('/<!--+[^<>]+-->/', '', $cached); + $expected = str_replace(array("\t", "\r\n", "\n"), "", $cached); + + $this->assertEqual($result, $expected); + $filename = $this->__cachePath($dispatcher->here); + $this->assertTrue(file_exists($filename)); + unlink($filename); + } +/** + * testHttpMethodOverrides method + * + * @access public + * @return void + */ + function testHttpMethodOverrides() { + Router::reload(); + Router::mapResources('Posts'); + + $_SERVER['REQUEST_METHOD'] = 'POST'; + $dispatcher =& new Dispatcher(); + $dispatcher->base = false; + + $result = $dispatcher->parseParams('/posts'); + $expected = array('pass' => array(), 'named' => array(), 'plugin' => null, 'controller' => 'posts', 'action' => 'add', '[method]' => 'POST', 'form' => array(), 'url' => array()); + $this->assertEqual($result, $expected); + + $_SERVER['REQUEST_METHOD'] = 'GET'; + $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] = 'PUT'; + + $result = $dispatcher->parseParams('/posts/5'); + $expected = array('pass' => array('5'), 'named' => array(), 'id' => '5', 'plugin' => null, 'controller' => 'posts', 'action' => 'edit', '[method]' => 'PUT', 'form' => array(), 'url' => array()); + $this->assertEqual($result, $expected); + + unset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']); + $_SERVER['REQUEST_METHOD'] = 'GET'; + + $result = $dispatcher->parseParams('/posts/5'); + $expected = array('pass' => array('5'), 'named' => array(), 'id' => '5', 'plugin' => null, 'controller' => 'posts', 'action' => 'view', '[method]' => 'GET', 'form' => array(), 'url' => array()); + $this->assertEqual($result, $expected); + + $_POST['_method'] = 'PUT'; + + $result = $dispatcher->parseParams('/posts/5'); + $expected = array('pass' => array('5'), 'named' => array(), 'id' => '5', 'plugin' => null, 'controller' => 'posts', 'action' => 'edit', '[method]' => 'PUT', 'form' => array(), 'url' => array()); + $this->assertEqual($result, $expected); + + $_POST['_method'] = 'POST'; + $_POST['data'] = array('Post' => array('title' => 'New Post')); + $_POST['extra'] = 'data'; + $_SERVER = array(); + + $result = $dispatcher->parseParams('/posts'); + $expected = array( + 'pass' => array(), 'named' => array(), 'plugin' => null, 'controller' => 'posts', 'action' => 'add', + '[method]' => 'POST', 'form' => array('extra' => 'data'), 'data' => array('Post' => array('title' => 'New Post')), + 'url' => array() + ); + $this->assertEqual($result, $expected); + + unset($_POST['_method']); + } + +/** + * Tests that invalid characters cannot be injected into the application base path. + * + * @return void + */ + function testBasePathInjection() { + $self = $_SERVER['PHP_SELF']; + $_SERVER['PHP_SELF'] = urldecode( + "/index.php/%22%3E%3Ch1%20onclick=%22alert('xss');%22%3Eheya%3C/h1%3E" + ); + + $dispatcher =& new Dispatcher(); + $result = $dispatcher->baseUrl(); + $expected = '/index.php/h1 onclick=alert(xss);heya'; + $this->assertEqual($result, $expected); + } +/** + * testEnvironmentDetection method + * + * @access public + * @return void + */ + function testEnvironmentDetection() { + $dispatcher =& new Dispatcher(); + + $environments = array( + 'IIS' => array( + 'No rewrite base path' => array( + 'App' => array('base' => false, 'baseUrl' => '/index.php?', 'server' => 'IIS'), + 'SERVER' => array('HTTPS' => 'off', 'SCRIPT_NAME' => '/index.php', 'PATH_TRANSLATED' => 'C:\\Inetpub\\wwwroot', 'QUERY_STRING' => '', 'REMOTE_ADDR' => '127.0.0.1', 'REMOTE_HOST' => '127.0.0.1', 'REQUEST_METHOD' => 'GET', 'SERVER_NAME' => 'localhost', 'SERVER_PORT' => '80', 'SERVER_PROTOCOL' => 'HTTP/1.1', 'SERVER_SOFTWARE' => 'Microsoft-IIS/5.1', 'APPL_PHYSICAL_PATH' => 'C:\\Inetpub\\wwwroot\\', 'REQUEST_URI' => '/index.php', 'URL' => '/index.php', 'SCRIPT_FILENAME' => 'C:\\Inetpub\\wwwroot\\index.php', 'ORIG_PATH_INFO' => '/index.php', 'PATH_INFO' => '', 'ORIG_PATH_TRANSLATED' => 'C:\\Inetpub\\wwwroot\\index.php', 'DOCUMENT_ROOT' => 'C:\\Inetpub\\wwwroot', 'PHP_SELF' => '/index.php', 'HTTP_ACCEPT' => '*/*', 'HTTP_ACCEPT_LANGUAGE' => 'en-us', 'HTTP_CONNECTION' => 'Keep-Alive', 'HTTP_HOST' => 'localhost', 'HTTP_USER_AGENT' => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)', 'HTTP_ACCEPT_ENCODING' => 'gzip, deflate', 'argv' => array(), 'argc' => 0), + 'reload' => true, + 'path' => '' + ), + 'No rewrite with path' => array( + 'SERVER' => array('QUERY_STRING' => '/posts/add', 'REQUEST_URI' => '/index.php?/posts/add', 'URL' => '/index.php?/posts/add', 'argv' => array('/posts/add'), 'argc' => 1), + 'reload' => false, + 'path' => '/posts/add' + ), + 'No rewrite sub dir 1' => array( + 'GET' => array(), + 'SERVER' => array('QUERY_STRING' => '', 'REQUEST_URI' => '/index.php', 'URL' => '/index.php', 'SCRIPT_FILENAME' => 'C:\\Inetpub\\wwwroot\\index.php', 'ORIG_PATH_INFO' => '/index.php', 'PATH_INFO' => '', 'ORIG_PATH_TRANSLATED' => 'C:\\Inetpub\\wwwroot\\index.php', 'DOCUMENT_ROOT' => 'C:\\Inetpub\\wwwroot', 'PHP_SELF' => '/index.php', 'argv' => array(), 'argc' => 0), + 'reload' => false, + 'path' => '' + ), + 'No rewrite sub dir 1 with path' => array( + 'GET' => array('/posts/add' => ''), + 'SERVER' => array('QUERY_STRING' => '/posts/add', 'REQUEST_URI' => '/index.php?/posts/add', 'URL' => '/index.php?/posts/add', 'SCRIPT_FILENAME' => 'C:\\Inetpub\\wwwroot\\index.php', 'argv' => array('/posts/add'), 'argc' => 1), + 'reload' => false, + 'path' => '/posts/add' + ), + 'No rewrite sub dir 2' => array( + 'App' => array('base' => false, 'baseUrl' => '/site/index.php?', 'dir' => 'app', 'webroot' => 'webroot', 'server' => 'IIS'), + 'GET' => array(), + 'POST' => array(), + 'SERVER' => array('SCRIPT_NAME' => '/site/index.php', 'PATH_TRANSLATED' => 'C:\\Inetpub\\wwwroot', 'QUERY_STRING' => '', 'REQUEST_URI' => '/site/index.php', 'URL' => '/site/index.php', 'SCRIPT_FILENAME' => 'C:\\Inetpub\\wwwroot\\site\\index.php', 'DOCUMENT_ROOT' => 'C:\\Inetpub\\wwwroot', 'PHP_SELF' => '/site/index.php', 'argv' => array(), 'argc' => 0), + 'reload' => false, + 'path' => '' + ), + 'No rewrite sub dir 2 with path' => array( + 'GET' => array('/posts/add' => ''), + 'SERVER' => array('SCRIPT_NAME' => '/site/index.php', 'PATH_TRANSLATED' => 'C:\\Inetpub\\wwwroot', 'QUERY_STRING' => '/posts/add', 'REQUEST_URI' => '/site/index.php?/posts/add', 'URL' => '/site/index.php?/posts/add', 'ORIG_PATH_TRANSLATED' => 'C:\\Inetpub\\wwwroot\\site\\index.php', 'DOCUMENT_ROOT' => 'C:\\Inetpub\\wwwroot', 'PHP_SELF' => '/site/index.php', 'argv' => array('/posts/add'), 'argc' => 1), + 'reload' => false, + 'path' => '/posts/add' + ) + ), + 'Apache' => array( + 'No rewrite base path' => array( + 'App' => array('base' => false, 'baseUrl' => '/index.php', 'dir' => 'app', 'webroot' => 'webroot'), + 'SERVER' => array('SERVER_NAME' => 'localhost', 'SERVER_ADDR' => '::1', 'SERVER_PORT' => '80', 'REMOTE_ADDR' => '::1', 'DOCUMENT_ROOT' => '/Library/WebServer/Documents/officespace/app/webroot', 'SCRIPT_FILENAME' => '/Library/WebServer/Documents/site/app/webroot/index.php', 'REQUEST_METHOD' => 'GET', 'QUERY_STRING' => '', 'REQUEST_URI' => '/', 'SCRIPT_NAME' => '/index.php', 'PHP_SELF' => '/index.php', 'argv' => array(), 'argc' => 0), + 'reload' => true, + 'path' => '' + ), + 'No rewrite with path' => array( + 'SERVER' => array('UNIQUE_ID' => 'VardGqn@17IAAAu7LY8AAAAK', 'HTTP_USER_AGENT' => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-us) AppleWebKit/523.10.5 (KHTML, like Gecko) Version/3.0.4 Safari/523.10.6', 'HTTP_ACCEPT' => 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5', 'HTTP_ACCEPT_LANGUAGE' => 'en-us', 'HTTP_ACCEPT_ENCODING' => 'gzip, deflate', 'HTTP_CONNECTION' => 'keep-alive', 'HTTP_HOST' => 'localhost', 'DOCUMENT_ROOT' => '/Library/WebServer/Documents/officespace/app/webroot', 'SCRIPT_FILENAME' => '/Library/WebServer/Documents/officespace/app/webroot/index.php', 'QUERY_STRING' => '', 'REQUEST_URI' => '/index.php/posts/add', 'SCRIPT_NAME' => '/index.php', 'PATH_INFO' => '/posts/add', 'PHP_SELF' => '/index.php/posts/add', 'argv' => array(), 'argc' => 0), + 'reload' => false, + 'path' => '/posts/add' + ), + 'GET Request at base domain' => array( + 'App' => array('base' => false, 'baseUrl' => null, 'dir' => 'app', 'webroot' => 'webroot'), + 'SERVER' => array('UNIQUE_ID' => '2A-v8sCoAQ8AAAc-2xUAAAAB', 'HTTP_ACCEPT_LANGUAGE' => 'en-us', 'HTTP_ACCEPT_ENCODING' => 'gzip, deflate', 'HTTP_COOKIE' => 'CAKEPHP=jcbv51apn84kd9ucv5aj2ln3t3', 'HTTP_CONNECTION' => 'keep-alive', 'HTTP_HOST' => 'cake.1.2', 'SERVER_NAME' => 'cake.1.2', 'SERVER_ADDR' => '127.0.0.1', 'SERVER_PORT' => '80', 'REMOTE_ADDR' => '127.0.0.1', 'DOCUMENT_ROOT' => '/Volumes/Home/htdocs/cake/repo/branches/1.2.x.x/app/webroot', 'SERVER_ADMIN' => 'you@example.com', 'SCRIPT_FILENAME' => '/Volumes/Home/htdocs/cake/repo/branches/1.2.x.x/app/webroot/index.php', 'REMOTE_PORT' => '53550', 'GATEWAY_INTERFACE' => 'CGI/1.1', 'SERVER_PROTOCOL' => 'HTTP/1.1', 'REQUEST_METHOD' => 'GET', 'QUERY_STRING' => 'a=b', 'REQUEST_URI' => '/?a=b', 'SCRIPT_NAME' => '/index.php', 'PHP_SELF' => '/index.php'), + 'GET' => array('a' => 'b'), + 'POST' => array(), + 'reload' => true, + 'path' => '', + 'urlParams' => array('a' => 'b'), + 'environment' => array('CGI_MODE' => false) + ), + 'New CGI no mod_rewrite' => array( + 'App' => array('base' => false, 'baseUrl' => '/limesurvey20/index.php', 'dir' => 'app', 'webroot' => 'webroot'), + 'SERVER' => array('DOCUMENT_ROOT' => '/home/.sites/110/site313/web', 'PATH_INFO' => '/installations', 'PATH_TRANSLATED' => '/home/.sites/110/site313/web/limesurvey20/index.php', 'PHPRC' => '/home/.sites/110/site313', 'QUERY_STRING' => '', 'REQUEST_METHOD' => 'GET', 'REQUEST_URI' => '/limesurvey20/index.php/installations', 'SCRIPT_FILENAME' => '/home/.sites/110/site313/web/limesurvey20/index.php', 'SCRIPT_NAME' => '/limesurvey20/index.php', 'SCRIPT_URI' => 'http://www.gisdat-umfragen.at/limesurvey20/index.php/installations', 'PHP_SELF' => '/limesurvey20/index.php/installations', 'CGI_MODE' => true), + 'GET' => array(), + 'POST' => array(), + 'reload' => true, + 'path' => '/installations', + 'urlParams' => array(), + 'environment' => array('CGI_MODE' => true) + ) + ) + ); + $backup = $this->__backupEnvironment(); + + foreach ($environments as $name => $env) { + foreach ($env as $descrip => $settings) { + if ($settings['reload']) { + $this->__reloadEnvironment(); + } + $this->__loadEnvironment($settings); + $this->assertEqual($dispatcher->uri(), $settings['path'], "%s on environment: {$name}, on setting: {$descrip}"); + + if (isset($settings['urlParams'])) { + $this->assertEqual($_GET, $settings['urlParams'], "%s on environment: {$name}, on setting: {$descrip}"); + } + if (isset($settings['environment'])) { + foreach ($settings['environment'] as $key => $val) { + $this->assertEqual(env($key), $val, "%s on key {$key} on environment: {$name}, on setting: {$descrip}"); + } + } + } + } + $this->__loadEnvironment(array_merge(array('reload' => true), $backup)); + } +/** + * Tests that the Dispatcher does not return an empty action + * + * @access private + * @return void + */ + function testTrailingSlash() { + $_POST = array(); + $_SERVER['PHP_SELF'] = '/cake/repo/branches/1.2.x.x/index.php'; + + Router::reload(); + $Dispatcher =& new TestDispatcher(); + Router::connect('/myalias/:action/*', array('controller' => 'my_controller', 'action' => null)); + + $Dispatcher->base = false; + $url = 'myalias/'; //Fails + $controller = $Dispatcher->dispatch($url, array('return' => 1)); + $result = $Dispatcher->parseParams($url); + $this->assertEqual('index', $result['action']); + + $url = 'myalias'; //Passes + $controller = $Dispatcher->dispatch($url, array('return' => 1)); + $result = $Dispatcher->parseParams($url); + $this->assertEqual('index', $result['action']); + } +/** + * backupEnvironment method + * + * @access private + * @return void + */ + function __backupEnvironment() { + return array( + 'App' => Configure::read('App'), + 'GET' => $_GET, + 'POST' => $_POST, + 'SERVER'=> $_SERVER + ); + } +/** + * reloadEnvironment method + * + * @access private + * @return void + */ + function __reloadEnvironment() { + foreach ($_GET as $key => $val) { + unset($_GET[$key]); + } + foreach ($_POST as $key => $val) { + unset($_POST[$key]); + } + foreach ($_SERVER as $key => $val) { + unset($_SERVER[$key]); + } + Configure::write('App', array()); + } +/** + * loadEnvironment method + * + * @param mixed $env + * @access private + * @return void + */ + function __loadEnvironment($env) { + if ($env['reload']) { + $this->__reloadEnvironment(); + } + + if (isset($env['App'])) { + Configure::write('App', $env['App']); + } + + if (isset($env['GET'])) { + foreach ($env['GET'] as $key => $val) { + $_GET[$key] = $val; + } + } + + if (isset($env['POST'])) { + foreach ($env['POST'] as $key => $val) { + $_POST[$key] = $val; + } + } + + if (isset($env['SERVER'])) { + foreach ($env['SERVER'] as $key => $val) { + $_SERVER[$key] = $val; + } + } + } +/** + * cachePath method + * + * @param mixed $her + * @access private + * @return string + */ + function __cachePath($here) { + $path = $here; + if ($here == '/') { + $path = 'home'; + } + $path = strtolower(Inflector::slug($path)); + + $filename = CACHE . 'views' . DS . $path . '.php'; + + if (!file_exists($filename)) { + $filename = CACHE . 'views' . DS . $path . '_index.php'; + } + return $filename; + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/cache.test.php b/cake/tests/cases/libs/cache.test.php new file mode 100755 index 0000000..37d5ec6 --- /dev/null +++ b/cake/tests/cases/libs/cache.test.php @@ -0,0 +1,235 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * CacheTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs + * @since CakePHP(tm) v 1.2.0.5432 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +if (!class_exists('Cache')) { + require LIBS . 'cache.php'; +} +/** + * CacheTest class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class CacheTest extends CakeTestCase { +/** + * setUp method + * + * @access public + * @return void + */ + function setUp() { + $this->_cacheDisable = Configure::read('Cache.disable'); + Configure::write('Cache.disable', false); + + $this->_defaultCacheConfig = Cache::config('default'); + Cache::config('default', array('engine' => 'File', 'path' => TMP . 'tests')); + + Cache::engine('File', array('path' => TMP . 'tests')); + } +/** + * tearDown method + * + * @access public + * @return void + */ + function tearDown() { + Configure::write('Cache.disable', $this->_cacheDisable); + Cache::config('default', $this->_defaultCacheConfig['settings']); + Cache::engine('File'); + } +/** + * testConfig method + * + * @access public + * @return void + */ + function testConfig() { + $settings = array('engine' => 'File', 'path' => TMP . 'tests', 'prefix' => 'cake_test_'); + $results = Cache::config('new', $settings); + $this->assertEqual($results, Cache::config('new')); + } +/** + * testConfigChange method + * + * @access public + * @return void + */ + function testConfigChange() { + $_cacheConfigSessions = Cache::config('sessions'); + $_cacheConfigTests = Cache::config('tests'); + + $result = Cache::config('sessions', array('engine'=> 'File', 'path' => TMP . 'sessions')); + $this->assertEqual($result['settings'], Cache::settings('File')); + + $result = Cache::config('tests', array('engine'=> 'File', 'path' => TMP . 'tests')); + $this->assertEqual($result['settings'], Cache::settings('File')); + + Cache::config('sessions', $_cacheConfigSessions['settings']); + Cache::config('tests', $_cacheConfigTests['settings']); + } +/** + * testWritingWithConfig method + * + * @access public + * @return void + */ + function testWritingWithConfig() { + $_cacheConfigSessions = Cache::config('sessions'); + + Cache::write('test_somthing', 'this is the test data', 'tests'); + + $expected = array( + 'path' => TMP . 'sessions', + 'prefix' => 'cake_', + 'lock' => false, + 'serialize' => true, + 'duration' => 3600, + 'probability' => 100, + 'engine' => 'File', + 'isWindows' => DIRECTORY_SEPARATOR == '\\' + ); + $this->assertEqual($expected, Cache::settings('File')); + + Cache::config('sessions', $_cacheConfigSessions['settings']); + } +/** + * testInitSettings method + * + * @access public + * @return void + */ + function testInitSettings() { + Cache::engine('File', array('path' => TMP . 'tests')); + + $settings = Cache::settings(); + $expecting = array( + 'engine' => 'File', + 'duration'=> 3600, + 'probability' => 100, + 'path'=> TMP . 'tests', + 'prefix'=> 'cake_', + 'lock' => false, + 'serialize'=> true, + 'isWindows' => DIRECTORY_SEPARATOR == '\\' + ); + $this->assertEqual($settings, $expecting); + + Cache::engine('File'); + } +/** + * testWriteEmptyValues method + * + * @access public + * @return void + */ + function testWriteEmptyValues() { + Cache::write('App.falseTest', false); + $this->assertIdentical(Cache::read('App.falseTest'), false); + + Cache::write('App.trueTest', true); + $this->assertIdentical(Cache::read('App.trueTest'), true); + + Cache::write('App.nullTest', null); + $this->assertIdentical(Cache::read('App.nullTest'), null); + + Cache::write('App.zeroTest', 0); + $this->assertIdentical(Cache::read('App.zeroTest'), 0); + + Cache::write('App.zeroTest2', '0'); + $this->assertIdentical(Cache::read('App.zeroTest2'), '0'); + } +/** + * testCacheDisable method + * + * Check that the "Cache.disable" configuration and a change to it + * (even after a cache config has been setup) is taken into account. + * + * @link https://trac.cakephp.org/ticket/6236 + * @access public + * @return void + */ + function testCacheDisable() { + Configure::write('Cache.disable', false); + Cache::config('test_cache_disable_1', array('engine'=> 'File', 'path' => TMP . 'tests')); + + $this->assertTrue(Cache::write('key_1', 'hello')); + $this->assertIdentical(Cache::read('key_1'), 'hello'); + + Configure::write('Cache.disable', true); + + $this->assertFalse(Cache::write('key_2', 'hello')); + $this->assertFalse(Cache::read('key_2')); + + Configure::write('Cache.disable', false); + + $this->assertTrue(Cache::write('key_3', 'hello')); + $this->assertIdentical(Cache::read('key_3'), 'hello'); + + Configure::write('Cache.disable', true); + Cache::config('test_cache_disable_2', array('engine'=> 'File', 'path' => TMP . 'tests')); + + $this->assertFalse(Cache::write('key_4', 'hello')); + $this->assertFalse(Cache::read('key_4')); + + Configure::write('Cache.disable', false); + + $this->assertTrue(Cache::write('key_5', 'hello')); + $this->assertIdentical(Cache::read('key_5'), 'hello'); + + Configure::write('Cache.disable', true); + + $this->assertFalse(Cache::write('key_6', 'hello')); + $this->assertFalse(Cache::read('key_6')); + } +/** + * testSet method + * + * @access public + * @return void + */ + function testSet() { + $_cacheSet = Cache::set(); + + Cache::set(array('duration' => '+1 year')); + $data = Cache::read('test_cache'); + $this->assertFalse($data); + + $data = 'this is just a simple test of the cache system'; + $write = Cache::write('test_cache', $data); + $this->assertTrue($write); + + Cache::set(array('duration' => '+1 year')); + $data = Cache::read('test_cache'); + $this->assertEqual($data, 'this is just a simple test of the cache system'); + + Cache::delete('test_cache'); + + $global = Cache::settings(); + + Cache::set($_cacheSet); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/cache/apc.test.php b/cake/tests/cases/libs/cache/apc.test.php new file mode 100755 index 0000000..5dd30c5 --- /dev/null +++ b/cake/tests/cases/libs/cache/apc.test.php @@ -0,0 +1,143 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * ApcEngineTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs.cache + * @since CakePHP(tm) v 1.2.0.5434 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +if (!class_exists('Cache')) { + require LIBS . 'cache.php'; +} +/** + * ApcEngineTest class + * + * @package cake + * @subpackage cake.tests.cases.libs.cache + */ +class ApcEngineTest extends UnitTestCase { +/** + * skip method + * + * @access public + * @return void + */ + function skip() { + $skip = true; + if (Cache::engine('Apc')) { + $skip = false; + } + $this->skipIf($skip, '%s Apc is not installed or configured properly'); + } +/** + * setUp method + * + * @access public + * @return void + */ + function setUp() { + $this->_cacheDisable = Configure::read('Cache.disable'); + Configure::write('Cache.disable', false); + Cache::config('apc', array('engine' => 'Apc', 'prefix' => 'cake_')); + } +/** + * tearDown method + * + * @access public + * @return void + */ + function tearDown() { + Configure::write('Cache.disable', $this->_cacheDisable); + Cache::config('default'); + } +/** + * testReadAndWriteCache method + * + * @access public + * @return void + */ + function testReadAndWriteCache() { + Cache::set(array('duration' => 1)); + + $result = Cache::read('test'); + $expecting = ''; + $this->assertEqual($result, $expecting); + + $data = 'this is a test of the emergency broadcasting system'; + $result = Cache::write('test', $data); + $this->assertTrue($result); + + $result = Cache::read('test'); + $expecting = $data; + $this->assertEqual($result, $expecting); + + Cache::delete('test'); + } +/** + * testExpiry method + * + * @access public + * @return void + */ + function testExpiry() { + Cache::set(array('duration' => 1)); + + $result = Cache::read('test'); + $this->assertFalse($result); + + $data = 'this is a test of the emergency broadcasting system'; + $result = Cache::write('other_test', $data); + $this->assertTrue($result); + + sleep(2); + $result = Cache::read('other_test'); + $this->assertFalse($result); + + Cache::set(array('duration' => "+1 second")); + + $data = 'this is a test of the emergency broadcasting system'; + $result = Cache::write('other_test', $data); + $this->assertTrue($result); + + sleep(2); + $result = Cache::read('other_test'); + $this->assertFalse($result); + + sleep(2); + $result = Cache::read('other_test'); + $this->assertFalse($result); + } +/** + * testDeleteCache method + * + * @access public + * @return void + */ + function testDeleteCache() { + $data = 'this is a test of the emergency broadcasting system'; + $result = Cache::write('delete_test', $data); + $this->assertTrue($result); + + $result = Cache::delete('delete_test'); + $this->assertTrue($result); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/cache/file.test.php b/cake/tests/cases/libs/cache/file.test.php new file mode 100755 index 0000000..89ee671 --- /dev/null +++ b/cake/tests/cases/libs/cache/file.test.php @@ -0,0 +1,356 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * FileEngineTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs.cache + * @since CakePHP(tm) v 1.2.0.5434 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +if (!class_exists('Cache')) { + require LIBS . 'cache.php'; +} +if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) { + define('CAKEPHP_UNIT_TEST_EXECUTION', 1); +} +/** + * FileEngineTest class + * + * @package cake + * @subpackage cake.tests.cases.libs.cache + */ +class FileEngineTest extends CakeTestCase { +/** + * config property + * + * @var array + * @access public + */ + var $config = array(); +/** + * startCase method + * + * @access public + * @return void + */ + function startCase() { + $this->_cacheDisable = Configure::read('Cache.disable'); + $this->_cacheConfig = Cache::config('default'); + Configure::write('Cache.disable', false); + Cache::config('default', array('engine' => 'File', 'path' => CACHE)); + } +/** + * endCase method + * + * @access public + * @return void + */ + function endCase() { + Configure::write('Cache.disable', $this->_cacheDisable); + Cache::config('default', $this->_cacheConfig['settings']); + } +/** + * testCacheDirChange method + * + * @access public + * @return void + */ + function testCacheDirChange() { + $result = Cache::config('sessions', array('engine'=> 'File', 'path' => TMP . 'sessions')); + $this->assertEqual($result['settings'], Cache::settings('File')); + $this->assertNotEqual($result, Cache::settings('File')); + + $result = Cache::config('tests', array('engine'=> 'File', 'path' => TMP . 'tests')); + $this->assertEqual($result['settings'], Cache::settings('File')); + $this->assertNotEqual($result, Cache::settings('File')); + } +/** + * testReadAndWriteCache method + * + * @access public + * @return void + */ + function testReadAndWriteCache() { + Cache::config('default'); + + $result = Cache::write(null, 'here'); + $this->assertFalse($result); + + Cache::set(array('duration' => 1)); + + $result = Cache::read('test'); + $expecting = ''; + $this->assertEqual($result, $expecting); + + $data = 'this is a test of the emergency broadcasting system'; + $result = Cache::write('test', $data); + $this->assertTrue(file_exists(CACHE . 'cake_test')); + + $result = Cache::read('test'); + $expecting = $data; + $this->assertEqual($result, $expecting); + + Cache::delete('test'); + } +/** + * testExpiry method + * + * @access public + * @return void + */ + function testExpiry() { + Cache::set(array('duration' => 1)); + + $result = Cache::read('test'); + $this->assertFalse($result); + + $data = 'this is a test of the emergency broadcasting system'; + $result = Cache::write('other_test', $data); + $this->assertTrue($result); + + sleep(2); + $result = Cache::read('other_test'); + $this->assertFalse($result); + + Cache::set(array('duration' => "+1 second")); + + $data = 'this is a test of the emergency broadcasting system'; + $result = Cache::write('other_test', $data); + $this->assertTrue($result); + + sleep(2); + $result = Cache::read('other_test'); + $this->assertFalse($result); + } +/** + * testDeleteCache method + * + * @access public + * @return void + */ + function testDeleteCache() { + $data = 'this is a test of the emergency broadcasting system'; + $result = Cache::write('delete_test', $data); + $this->assertTrue($result); + + $result = Cache::delete('delete_test'); + $this->assertTrue($result); + $this->assertFalse(file_exists(TMP . 'tests' . DS . 'delete_test')); + + $result = Cache::delete('delete_test'); + $this->assertFalse($result); + + } +/** + * testSerialize method + * + * @access public + * @return void + */ + function testSerialize() { + Cache::engine('File', array('serialize' => true)); + $data = 'this is a test of the emergency broadcasting system'; + $write = Cache::write('serialize_test', $data, 1); + $this->assertTrue($write); + + Cache::engine('File', array('serialize' => false)); + $read = Cache::read('serialize_test'); + + $newread = Cache::read('serialize_test'); + + $delete = Cache::delete('serialize_test'); + + $this->assertIdentical($read, serialize($data)); + + $this->assertIdentical(unserialize($newread), $data); + + } +/** + * testClear method + * + * @access public + * @return void + */ + function testClear() { + Cache::engine('File', array('duration' => 1)); + $data = 'this is a test of the emergency broadcasting system'; + $write = Cache::write('serialize_test1', $data); + $write = Cache::write('serialize_test2', $data); + $write = Cache::write('serialize_test3', $data); + $this->assertTrue(file_exists(CACHE . 'cake_serialize_test1')); + $this->assertTrue(file_exists(CACHE . 'cake_serialize_test2')); + $this->assertTrue(file_exists(CACHE . 'cake_serialize_test3')); + sleep(2); + $result = Cache::clear(true); + $this->assertTrue($result); + $this->assertFalse(file_exists(CACHE . 'cake_serialize_test1')); + $this->assertFalse(file_exists(CACHE . 'cake_serialize_test2')); + $this->assertFalse(file_exists(CACHE . 'cake_serialize_test3')); + + $data = 'this is a test of the emergency broadcasting system'; + $write = Cache::write('serialize_test1', $data); + $write = Cache::write('serialize_test2', $data); + $write = Cache::write('serialize_test3', $data); + $this->assertTrue(file_exists(CACHE . 'cake_serialize_test1')); + $this->assertTrue(file_exists(CACHE . 'cake_serialize_test2')); + $this->assertTrue(file_exists(CACHE . 'cake_serialize_test3')); + + $result = Cache::clear(); + $this->assertTrue($result); + $this->assertFalse(file_exists(CACHE . 'cake_serialize_test1')); + $this->assertFalse(file_exists(CACHE . 'cake_serialize_test2')); + $this->assertFalse(file_exists(CACHE . 'cake_serialize_test3')); + + $result = Cache::engine('File', array('path' => CACHE . 'views')); + + $data = 'this is a test of the emergency broadcasting system'; + $write = Cache::write('controller_view_1', $data); + $write = Cache::write('controller_view_2', $data); + $write = Cache::write('controller_view_3', $data); + $write = Cache::write('controller_view_10', $data); + $write = Cache::write('controller_view_11', $data); + $write = Cache::write('controller_view_12', $data); + $this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_1')); + $this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_2')); + $this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_3')); + $this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_10')); + $this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_11')); + $this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_12')); + + clearCache('controller_view_1', 'views', ''); + $this->assertFalse(file_exists(CACHE . 'views'. DS . 'cake_controller_view_1')); + $this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_2')); + $this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_3')); + $this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_10')); + $this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_11')); + $this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_12')); + + clearCache('controller_view', 'views', ''); + $this->assertFalse(file_exists(CACHE . 'views'. DS . 'cake_controller_view_1')); + $this->assertFalse(file_exists(CACHE . 'views'. DS . 'cake_controller_view_2')); + $this->assertFalse(file_exists(CACHE . 'views'. DS . 'cake_controller_view_3')); + $this->assertFalse(file_exists(CACHE . 'views'. DS . 'cake_controller_view_10')); + $this->assertFalse(file_exists(CACHE . 'views'. DS . 'cake_controller_view_11')); + $this->assertFalse(file_exists(CACHE . 'views'. DS . 'cake_controller_view_12')); + + $write = Cache::write('controller_view_1', $data); + $write = Cache::write('controller_view_2', $data); + $write = Cache::write('controller_view_3', $data); + $write = Cache::write('controller_view_10', $data); + $write = Cache::write('controller_view_11', $data); + $write = Cache::write('controller_view_12', $data); + $this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_1')); + $this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_2')); + $this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_3')); + $this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_10')); + $this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_11')); + $this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_12')); + + clearCache(array('controller_view_2', 'controller_view_11', 'controller_view_12'), 'views', ''); + $this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_1')); + $this->assertFalse(file_exists(CACHE . 'views'. DS . 'cake_controller_view_2')); + $this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_3')); + $this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_10')); + $this->assertFalse(file_exists(CACHE . 'views'. DS . 'cake_controller_view_11')); + $this->assertFalse(file_exists(CACHE . 'views'. DS . 'cake_controller_view_12')); + + clearCache('controller_view'); + + Cache::engine('File', array('path' => CACHE)); + } +/** + * testKeyPath method + * + * @access public + * @return void + */ + function testKeyPath() { + $result = Cache::write('views.countries.something', 'here'); + $this->assertTrue($result); + $this->assertTrue(file_exists(CACHE . 'cake_views_countries_something')); + + $result = Cache::read('views.countries.something'); + $this->assertEqual($result, 'here'); + + $result = Cache::clear(); + $this->assertTrue($result); + } +/** + * testRemoveWindowsSlashesFromCache method + * + * @access public + * @return void + */ + function testRemoveWindowsSlashesFromCache() { + Cache::engine('File', array('isWindows' => true, 'prefix' => null, 'path' => TMP)); + + $expected = array ( + 'C:\dev\prj2\sites\cake\libs' => array( + 0 => 'C:\dev\prj2\sites\cake\libs', 1 => 'C:\dev\prj2\sites\cake\libs\view', + 2 => 'C:\dev\prj2\sites\cake\libs\view\scaffolds', 3 => 'C:\dev\prj2\sites\cake\libs\view\pages', + 4 => 'C:\dev\prj2\sites\cake\libs\view\layouts', 5 => 'C:\dev\prj2\sites\cake\libs\view\layouts\xml', + 6 => 'C:\dev\prj2\sites\cake\libs\view\layouts\rss', 7 => 'C:\dev\prj2\sites\cake\libs\view\layouts\js', + 8 => 'C:\dev\prj2\sites\cake\libs\view\layouts\email', 9 => 'C:\dev\prj2\sites\cake\libs\view\layouts\email\text', + 10 => 'C:\dev\prj2\sites\cake\libs\view\layouts\email\html', 11 => 'C:\dev\prj2\sites\cake\libs\view\helpers', + 12 => 'C:\dev\prj2\sites\cake\libs\view\errors', 13 => 'C:\dev\prj2\sites\cake\libs\view\elements', + 14 => 'C:\dev\prj2\sites\cake\libs\view\elements\email', 15 => 'C:\dev\prj2\sites\cake\libs\view\elements\email\text', + 16 => 'C:\dev\prj2\sites\cake\libs\view\elements\email\html', 17 => 'C:\dev\prj2\sites\cake\libs\model', + 18 => 'C:\dev\prj2\sites\cake\libs\model\datasources', 19 => 'C:\dev\prj2\sites\cake\libs\model\datasources\dbo', + 20 => 'C:\dev\prj2\sites\cake\libs\model\behaviors', 21 => 'C:\dev\prj2\sites\cake\libs\controller', + 22 => 'C:\dev\prj2\sites\cake\libs\controller\components', 23 => 'C:\dev\prj2\sites\cake\libs\cache'), + 'C:\dev\prj2\sites\main_site\vendors' => array( + 0 => 'C:\dev\prj2\sites\main_site\vendors', 1 => 'C:\dev\prj2\sites\main_site\vendors\shells', + 2 => 'C:\dev\prj2\sites\main_site\vendors\shells\templates', 3 => 'C:\dev\prj2\sites\main_site\vendors\shells\templates\cdc_project', + 4 => 'C:\dev\prj2\sites\main_site\vendors\shells\tasks', 5 => 'C:\dev\prj2\sites\main_site\vendors\js', + 6 => 'C:\dev\prj2\sites\main_site\vendors\css'), + 'C:\dev\prj2\sites\vendors' => array( + 0 => 'C:\dev\prj2\sites\vendors', 1 => 'C:\dev\prj2\sites\vendors\simpletest', + 2 => 'C:\dev\prj2\sites\vendors\simpletest\test', 3 => 'C:\dev\prj2\sites\vendors\simpletest\test\support', + 4 => 'C:\dev\prj2\sites\vendors\simpletest\test\support\collector', 5 => 'C:\dev\prj2\sites\vendors\simpletest\extensions', + 6 => 'C:\dev\prj2\sites\vendors\simpletest\extensions\testdox', 7 => 'C:\dev\prj2\sites\vendors\simpletest\docs', + 8 => 'C:\dev\prj2\sites\vendors\simpletest\docs\fr', 9 => 'C:\dev\prj2\sites\vendors\simpletest\docs\en'), + 'C:\dev\prj2\sites\main_site\views\helpers' => array( + 0 => 'C:\dev\prj2\sites\main_site\views\helpers')); + + $data = Cache::write('test_dir_map', $expected); + $data = Cache::read('test_dir_map'); + Cache::delete('test_dir_map'); + $this->assertEqual($expected, $data); + } +/** + * testWriteQuotedString method + * + * @access public + * @return void + */ + function testWriteQuotedString() { + Cache::engine('File', array('path' => TMP . 'tests')); + Cache::write('App.doubleQuoteTest', '"this is a quoted string"'); + $this->assertIdentical(Cache::read('App.doubleQuoteTest'), '"this is a quoted string"'); + Cache::write('App.singleQuoteTest', "'this is a quoted string'"); + $this->assertIdentical(Cache::read('App.singleQuoteTest'), "'this is a quoted string'"); + + Cache::engine('File', array('isWindows' => true, 'path' => TMP . 'tests')); + $this->assertIdentical(Cache::read('App.doubleQuoteTest'), '"this is a quoted string"'); + Cache::write('App.singleQuoteTest', "'this is a quoted string'"); + $this->assertIdentical(Cache::read('App.singleQuoteTest'), "'this is a quoted string'"); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/cache/memcache.test.php b/cake/tests/cases/libs/cache/memcache.test.php new file mode 100755 index 0000000..8a48df8 --- /dev/null +++ b/cake/tests/cases/libs/cache/memcache.test.php @@ -0,0 +1,225 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * MemcacheEngineTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs.cache + * @since CakePHP(tm) v 1.2.0.5434 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +if (!class_exists('Cache')) { + require LIBS . 'cache.php'; +} +/** + * MemcacheEngineTest class + * + * @package cake + * @subpackage cake.tests.cases.libs.cache + */ +/** + * MemcacheEngineTest class + * + * @package cake + * @subpackage cake.tests.cases.libs.cache + */ +class MemcacheEngineTest extends CakeTestCase { +/** + * skip method + * + * @access public + * @return void + */ + function skip() { + $skip = true; + if (Cache::engine('Memcache')) { + $skip = false; + } + $this->skipIf($skip, '%s Memcache is not installed or configured properly'); + } +/** + * setUp method + * + * @access public + * @return void + */ + function setUp() { + $this->_cacheDisable = Configure::read('Cache.disable'); + Configure::write('Cache.disable', false); + Cache::config('memcache', array('engine' => 'Memcache', 'prefix' => 'cake_')); + } +/** + * tearDown method + * + * @access public + * @return void + */ + function tearDown() { + Configure::write('Cache.disable', $this->_cacheDisable); + Cache::config('default'); + } +/** + * testSettings method + * + * @access public + * @return void + */ + function testSettings() { + $settings = Cache::settings(); + $expecting = array('prefix' => 'cake_', + 'duration'=> 3600, + 'probability' => 100, + 'servers' => array('127.0.0.1'), + 'compress' => false, + 'engine' => 'Memcache' + ); + $this->assertEqual($settings, $expecting); + } +/** + * testSettings method + * + * @access public + * @return void + */ + function testMultipleServers() { + $servers = array('127.0.0.1:11211', '127.0.0.1:11222'); + + $Cache =& Cache::getInstance(); + $MemCache =& $Cache->_Engine['Memcache']; + + $available = true; + foreach($servers as $server) { + list($host, $port) = explode(':', $server); + if (!@$MemCache->__Memcache->connect($host, $port)) { + $available = false; + } + } + + if ($this->skipIf(!$available, '%s Need memcache servers at ' . implode(', ', $servers) . ' to run this test')) { + return; + } + + unset($MemCache->__Memcache); + $MemCache->init(array('engine' => 'Memcache', 'servers' => $servers)); + + $servers = array_keys($MemCache->__Memcache->getExtendedStats()); + $settings = Cache::settings(); + $this->assertEqual($servers, $settings['servers']); + } +/** + * testConnect method + * + * @access public + * @return void + */ + function testConnect() { + $Cache =& Cache::getInstance(); + $result = $Cache->_Engine['Memcache']->connect('127.0.0.1'); + $this->assertTrue($result); + } +/** + * testReadAndWriteCache method + * + * @access public + * @return void + */ + function testReadAndWriteCache() { + Cache::set(array('duration' => 1)); + + $result = Cache::read('test'); + $expecting = ''; + $this->assertEqual($result, $expecting); + + $data = 'this is a test of the emergency broadcasting system'; + $result = Cache::write('test', $data); + $this->assertTrue($result); + + $result = Cache::read('test'); + $expecting = $data; + $this->assertEqual($result, $expecting); + + Cache::delete('test'); + } +/** + * testExpiry method + * + * @access public + * @return void + */ + function testExpiry() { + Cache::set(array('duration' => 1)); + + $result = Cache::read('test'); + $this->assertFalse($result); + + $data = 'this is a test of the emergency broadcasting system'; + $result = Cache::write('other_test', $data); + $this->assertTrue($result); + + sleep(2); + $result = Cache::read('other_test'); + $this->assertFalse($result); + + Cache::set(array('duration' => "+1 second")); + + $data = 'this is a test of the emergency broadcasting system'; + $result = Cache::write('other_test', $data); + $this->assertTrue($result); + + sleep(2); + $result = Cache::read('other_test'); + $this->assertFalse($result); + + Cache::engine('Memcache', array('duration' => '+1 second')); + sleep(2); + + $result = Cache::read('other_test'); + $this->assertFalse($result); + + Cache::engine('Memcache', array('duration' => '+31 day')); + $data = 'this is a test of the emergency broadcasting system'; + $result = Cache::write('long_expiry_test', $data); + $this->assertTrue($result); + + sleep(2); + $result = Cache::read('long_expiry_test'); + $expecting = $data; + $this->assertEqual($result, $expecting); + + $result = Cache::read('long_expiry_test'); + $this->assertTrue($result); + + Cache::engine('Memcache', array('duration' => 3600)); + } +/** + * testDeleteCache method + * + * @access public + * @return void + */ + function testDeleteCache() { + $data = 'this is a test of the emergency broadcasting system'; + $result = Cache::write('delete_test', $data); + $this->assertTrue($result); + + $result = Cache::delete('delete_test'); + $this->assertTrue($result); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/cache/xcache.test.php b/cake/tests/cases/libs/cache/xcache.test.php new file mode 100755 index 0000000..842cbfa --- /dev/null +++ b/cake/tests/cases/libs/cache/xcache.test.php @@ -0,0 +1,172 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * XcacheEngineTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs.cache + * @since CakePHP(tm) v 1.2.0.5434 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +if (!class_exists('Cache')) { + require LIBS . 'cache.php'; +} +/** + * XcacheEngineTest class + * + * @package cake + * @subpackage cake.tests.cases.libs.cache + */ +class XcacheEngineTest extends UnitTestCase { +/** + * skip method + * + * @access public + * @return void + */ + function skip() { + $skip = true; + if ($result = Cache::engine('Xcache')) { + $skip = false; + } + $this->skipIf($skip, '%s Xcache is not installed or configured properly'); + } +/** + * setUp method + * + * @access public + * @return void + */ + function setUp() { + $this->_cacheDisable = Configure::read('Cache.disable'); + Configure::write('Cache.disable', false); + Cache::config('xcache', array('engine' => 'Xcache', 'prefix' => 'cake_')); + } +/** + * tearDown method + * + * @access public + * @return void + */ + function tearDown() { + Configure::write('Cache.disable', $this->_cacheDisable); + Cache::config('default'); + } +/** + * testSettings method + * + * @access public + * @return void + */ + function testSettings() { + $settings = Cache::settings(); + $expecting = array('prefix' => 'cake_', + 'duration'=> 3600, + 'probability' => 100, + 'engine' => 'Xcache', + 'PHP_AUTH_USER' => 'user', + 'PHP_AUTH_PW' => 'password', + ); + $this->assertEqual($settings, $expecting); + } +/** + * testReadAndWriteCache method + * + * @access public + * @return void + */ + function testReadAndWriteCache() { + Cache::set(array('duration' => 1)); + + $result = Cache::read('test'); + $expecting = ''; + $this->assertEqual($result, $expecting); + + $data = 'this is a test of the emergency broadcasting system'; + $result = Cache::write('test', $data); + $this->assertTrue($result); + + $result = Cache::read('test'); + $expecting = $data; + $this->assertEqual($result, $expecting); + + Cache::delete('test'); + } +/** + * testExpiry method + * + * @access public + * @return void + */ + function testExpiry() { + Cache::set(array('duration' => 1)); + $result = Cache::read('test'); + $this->assertFalse($result); + + $data = 'this is a test of the emergency broadcasting system'; + $result = Cache::write('other_test', $data); + $this->assertTrue($result); + + sleep(2); + $result = Cache::read('other_test'); + $this->assertFalse($result); + + Cache::set(array('duration' => "+1 second")); + + $data = 'this is a test of the emergency broadcasting system'; + $result = Cache::write('other_test', $data); + $this->assertTrue($result); + + sleep(2); + $result = Cache::read('other_test'); + $this->assertFalse($result); + } +/** + * testDeleteCache method + * + * @access public + * @return void + */ + function testDeleteCache() { + $data = 'this is a test of the emergency broadcasting system'; + $result = Cache::write('delete_test', $data); + $this->assertTrue($result); + + $result = Cache::delete('delete_test'); + $this->assertTrue($result); + } +/** + * testClearCache method + * + * @access public + * @return void + */ + function testClearCache() { + $data = 'this is a test of the emergency broadcasting system'; + $result = Cache::write('clear_test_1', $data); + $this->assertTrue($result); + + $result = Cache::write('clear_test_2', $data); + $this->assertTrue($result); + + $result = Cache::clear(); + $this->assertTrue($result); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/cake_log.test.php b/cake/tests/cases/libs/cake_log.test.php new file mode 100755 index 0000000..05dc394 --- /dev/null +++ b/cake/tests/cases/libs/cake_log.test.php @@ -0,0 +1,55 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * CakeLogTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs + * @since CakePHP(tm) v 1.2.0.5432 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +App::import('Core', 'Log'); +/** + * CakeLogTest class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class CakeLogTest extends CakeTestCase { +/** + * testLogFileWriting method + * + * @access public + * @return void + */ + function testLogFileWriting() { + @unlink(LOGS . 'error.log'); + CakeLog::write(LOG_WARNING, 'Test warning'); + $this->assertTrue(file_exists(LOGS . 'error.log')); + unlink(LOGS . 'error.log'); + + CakeLog::write(LOG_WARNING, 'Test warning 1'); + CakeLog::write(LOG_WARNING, 'Test warning 2'); + $result = file_get_contents(LOGS . 'error.log'); + $this->assertPattern('/^2[0-9]{3}-[0-9]+-[0-9]+ [0-9]+:[0-9]+:[0-9]+ Warning: Test warning 1/', $result); + $this->assertPattern('/2[0-9]{3}-[0-9]+-[0-9]+ [0-9]+:[0-9]+:[0-9]+ Warning: Test warning 2$/', $result); + unlink(LOGS . 'error.log'); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/cake_test_case.test.php b/cake/tests/cases/libs/cake_test_case.test.php new file mode 100755 index 0000000..895eaaf --- /dev/null +++ b/cake/tests/cases/libs/cake_test_case.test.php @@ -0,0 +1,418 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * CakeTestCaseTest file + * + * Test Case for CakeTestCase class + * + * PHP versions 4 and 5 + * + * CakePHP : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2006-2008, Cake Software Foundation, Inc. + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2006-2008, Cake Software Foundation, Inc. + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP Project + * @package cake + * @subpackage cake.cake.libs. + * @since CakePHP v 1.2.0.4487 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +App::import('Core', 'CakeTestCase'); + +if (!class_exists('AppController')) { + require_once LIBS . 'controller' . DS . 'app_controller.php'; +} elseif (!defined('APP_CONTROLLER_EXISTS')) { + define('APP_CONTROLLER_EXISTS', true); +} + +Mock::generate('CakeHtmlReporter'); +Mock::generate('CakeTestCase', 'CakeDispatcherMockTestCase'); + +SimpleTest::ignore('SubjectCakeTestCase'); +SimpleTest::ignore('CakeDispatcherMockTestCase'); +/** + * SubjectCakeTestCase + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class SubjectCakeTestCase extends CakeTestCase { +/** + * Feed a Mocked Reporter to the subject case + * prevents its pass/fails from affecting the real test + * + * @param string $reporter + * @access public + * @return void + */ + function setReporter(&$reporter) { + $this->_reporter = &$reporter; + } +/** + * testDummy method + * + * @return void + * @access public + */ + function testDummy() { + } +} +/** + * CakeTestCaseTest + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class CakeTestCaseTest extends CakeTestCase { +/** + * setUp + * + * @access public + * @return void + */ + function setUp() { + $this->Case =& new SubjectCakeTestCase(); + $reporter =& new MockCakeHtmlReporter(); + $this->Case->setReporter($reporter); + $this->Reporter = $reporter; + } +/** + * tearDown + * + * @access public + * @return void + */ + function tearDown() { + unset($this->Case); + unset($this->Reporter); + } +/** + * testAssertGoodTags + * + * @access public + * @return void + */ + function testAssertGoodTags() { + $this->Reporter->expectAtLeastOnce('paintPass'); + $this->Reporter->expectNever('paintFail'); + + $input = '<p>Text</p>'; + $pattern = array( + '<p', + 'Text', + '/p', + ); + $this->assertTrue($this->Case->assertTags($input, $pattern)); + + $input = '<a href="/test.html" class="active">My link</a>'; + $pattern = array( + 'a' => array('href' => '/test.html', 'class' => 'active'), + 'My link', + '/a' + ); + $this->assertTrue($this->Case->assertTags($input, $pattern)); + + $pattern = array( + 'a' => array('class' => 'active', 'href' => '/test.html'), + 'My link', + '/a' + ); + $this->assertTrue($this->Case->assertTags($input, $pattern)); + + $input = "<a href=\"/test.html\"\t\n\tclass=\"active\"\tid=\"primary\">\t<span>My link</span></a>"; + $pattern = array( + 'a' => array('id' => 'primary', 'href' => '/test.html', 'class' => 'active'), + '<span', + 'My link', + '/span', + '/a' + ); + $this->assertTrue($this->Case->assertTags($input, $pattern)); + + $input = '<p class="info"><a href="/test.html" class="active"><strong onClick="alert(\'hey\');">My link</strong></a></p>'; + $pattern = array( + 'p' => array('class' => 'info'), + 'a' => array('class' => 'active', 'href' => '/test.html' ), + 'strong' => array('onClick' => 'alert(\'hey\');'), + 'My link', + '/strong', + '/a', + '/p' + ); + $this->assertTrue($this->Case->assertTags($input, $pattern)); + } +/** + * testBadAssertTags + * + * @access public + * @return void + */ + function testBadAssertTags() { + $this->Reporter->expectAtLeastOnce('paintFail'); + $this->Reporter->expectNever('paintPass'); + + $input = '<a href="/test.html" class="active">My link</a>'; + $pattern = array( + 'a' => array('hRef' => '/test.html', 'clAss' => 'active'), + 'My link', + '/a' + ); + $this->assertFalse($this->Case->assertTags($input, $pattern)); + + $input = '<a href="/test.html" class="active">My link</a>'; + $pattern = array( + '<a' => array('href' => '/test.html', 'class' => 'active'), + 'My link', + '/a' + ); + $this->assertFalse($this->Case->assertTags($input, $pattern)); + } +/** + * testBefore + * + * @access public + * @return void + */ + function testBefore() { + $this->Case->before('testDummy'); + $this->assertFalse(isset($this->Case->db)); + + $this->Case->fixtures = array('core.post'); + $this->Case->before('start'); + $this->assertTrue(isset($this->Case->db)); + $this->assertTrue(isset($this->Case->_fixtures['core.post'])); + $this->assertTrue(is_a($this->Case->_fixtures['core.post'], 'CakeTestFixture')); + $this->assertEqual($this->Case->_fixtureClassMap['Post'], 'core.post'); + } +/** + * testAfter + * + * @access public + * @return void + */ + function testAfter() { + $this->Case->after('testDummy'); + $this->assertFalse($this->Case->__truncated); + + $this->Case->fixtures = array('core.post'); + $this->Case->before('start'); + $this->Case->start(); + $this->Case->after('testDummy'); + $this->assertTrue($this->Case->__truncated); + } +/** + * testLoadFixtures + * + * @access public + * @return void + */ + function testLoadFixtures() { + $this->Case->fixtures = array('core.post'); + $this->Case->autoFixtures = false; + $this->Case->before('start'); + $this->expectError(); + $this->Case->loadFixtures('Wrong!'); + $this->Case->end(); + } +/** + * testGetTests Method + * + * @return void + * @access public + */ + function testGetTests() { + $result = $this->Case->getTests(); + $this->assertEqual(array_slice($result, 0, 2), array('start', 'startCase')); + $this->assertEqual(array_slice($result, -2), array('endCase', 'end')); + } +/** + * TestTestAction + * + * @access public + * @return void + **/ + function testTestAction() { + $_back = array( + 'controller' => Configure::read('controllerPaths'), + 'view' => Configure::read('viewPaths'), + 'model' => Configure::read('modelPaths'), + 'plugin' => Configure::read('pluginPaths') + ); + Configure::write('controllerPaths', array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'controllers' . DS)); + Configure::write('viewPaths', array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS)); + Configure::write('modelPaths', array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'models' . DS)); + Configure::write('pluginPaths', array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)); + + $result = $this->Case->testAction('/tests_apps/index', array('return' => 'view')); + $this->assertPattern('/This is the TestsAppsController index view/', $result); + + $result = $this->Case->testAction('/tests_apps/index', array('return' => 'contents')); + $this->assertPattern('/This is the TestsAppsController index view/', $result); + $this->assertPattern('/<html/', $result); + $this->assertPattern('/<\/html>/', $result); + + $result = $this->Case->testAction('/tests_apps/some_method', array('return' => 'result')); + $this->assertEqual($result, 5); + + $result = $this->Case->testAction('/tests_apps/set_action', array('return' => 'vars')); + $this->assertEqual($result, array('var' => 'string')); + + $db =& ConnectionManager::getDataSource('test_suite'); + $fixture =& new PostFixture(); + $fixture->create($db); + + $result = $this->Case->testAction('/tests_apps_posts/add', array('return' => 'vars')); + $this->assertTrue(array_key_exists('posts', $result)); + $this->assertEqual(count($result['posts']), 1); + + $result = $this->Case->testAction('/tests_apps_posts/url_var/var1:value1/var2:val2', array( + 'return' => 'vars', + 'method' => 'get', + )); + $this->assertTrue(isset($result['params']['url']['url'])); + $this->assertTrue(isset($result['params']['url']['output'])); + $this->assertEqual(array_keys($result['params']['named']), array('var1', 'var2')); + + $result = $this->Case->testAction('/tests_apps_posts/url_var/gogo/val2', array( + 'return' => 'vars', + 'method' => 'get', + )); + $this->assertEqual($result['params']['pass'], array('gogo', 'val2')); + + + $result = $this->Case->testAction('/tests_apps_posts/url_var', array( + 'return' => 'vars', + 'method' => 'get', + 'data' => array( + 'red' => 'health', + 'blue' => 'mana' + ) + )); + $this->assertTrue(isset($result['params']['url']['output'])); + $this->assertTrue(isset($result['params']['url']['red'])); + $this->assertTrue(isset($result['params']['url']['blue'])); + $this->assertTrue(isset($result['params']['url']['url'])); + + $result = $this->Case->testAction('/tests_apps_posts/post_var', array( + 'return' => 'vars', + 'method' => 'post', + 'data' => array( + 'name' => 'is jonas', + 'pork' => 'and beans', + ) + )); + $this->assertEqual(array_keys($result['data']), array('name', 'pork')); + $fixture->drop($db); + + $db =& ConnectionManager::getDataSource('test_suite'); + $_backPrefix = $db->config['prefix']; + $db->config['prefix'] = 'cake_testaction_test_suite_'; + + $config = $db->config; + $config['prefix'] = 'cake_testcase_test_'; + + ConnectionManager::create('cake_test_case', $config); + $db2 =& ConnectionManager::getDataSource('cake_test_case'); + + $fixture =& new PostFixture($db2); + $fixture->create($db2); + $fixture->insert($db2); + + $result = $this->Case->testAction('/tests_apps_posts/fixtured', array( + 'return' => 'vars', + 'fixturize' => true, + 'connection' => 'cake_test_case', + )); + $this->assertTrue(isset($result['posts'])); + $this->assertEqual(count($result['posts']), 3); + $tables = $db2->listSources(); + $this->assertFalse(in_array('cake_testaction_test_suite_posts', $tables)); + + $fixture->drop($db2); + + $db =& ConnectionManager::getDataSource('test_suite'); + + //test that drop tables behaves as exepected with testAction + $db =& ConnectionManager::getDataSource('test_suite'); + $_backPrefix = $db->config['prefix']; + $db->config['prefix'] = 'cake_testaction_test_suite_'; + + $config = $db->config; + $config['prefix'] = 'cake_testcase_test_'; + + ConnectionManager::create('cake_test_case', $config); + $db =& ConnectionManager::getDataSource('cake_test_case'); + $fixture =& new PostFixture($db); + $fixture->create($db); + $fixture->insert($db); + + $this->Case->dropTables = false; + $result = $this->Case->testAction('/tests_apps_posts/fixtured', array( + 'return' => 'vars', + 'fixturize' => true, + 'connection' => 'cake_test_case', + )); + + $tables = $db->listSources(); + $this->assertTrue(in_array('cake_testaction_test_suite_posts', $tables)); + + $fixture->drop($db); + $db =& ConnectionManager::getDataSource('test_suite'); + $db->config['prefix'] = $_backPrefix; + $fixture->drop($db); + + + Configure::write('modelPaths', $_back['model']); + Configure::write('controllerPaths', $_back['controller']); + Configure::write('viewPaths', $_back['view']); + Configure::write('pluginPaths', $_back['plugin']); + } +/** + * testSkipIf + * + * @return void + **/ + function testSkipIf() { + $this->assertTrue($this->Case->skipIf(true)); + $this->assertFalse($this->Case->skipIf(false)); + } +/** + * testTestDispatcher + * + * @access public + * @return void + */ + function testTestDispatcher() { + $_back = array( + 'controller' => Configure::read('controllerPaths'), + 'view' => Configure::read('viewPaths'), + 'plugin' => Configure::read('pluginPaths') + ); + Configure::write('controllerPaths', array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'controllers' . DS)); + Configure::write('viewPaths', array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS)); + Configure::write('pluginPaths', array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)); + + $Dispatcher =& new CakeTestDispatcher(); + $Case =& new CakeDispatcherMockTestCase(); + + $Case->expectOnce('startController'); + $Case->expectOnce('endController'); + + $Dispatcher->testCase($Case); + $this->assertTrue(isset($Dispatcher->testCase)); + + $return = $Dispatcher->dispatch('/tests_apps/index', array('autoRender' => 0, 'return' => 1, 'requested' => 1)); + + Configure::write('controllerPaths', $_back['controller']); + Configure::write('viewPaths', $_back['view']); + Configure::write('pluginPaths', $_back['plugin']); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/cake_test_fixture.test.php b/cake/tests/cases/libs/cake_test_fixture.test.php new file mode 100755 index 0000000..d5844bc --- /dev/null +++ b/cake/tests/cases/libs/cake_test_fixture.test.php @@ -0,0 +1,289 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * CakeTestFixture file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.cake.tests.libs + * @since CakePHP(tm) v 1.2.0.4667 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +App::import('Core', 'DboSource'); +/** + * CakeTestFixtureTestFixture class + * + * @package cake + * @subpackage cake.cake.tests.cases.libs + */ +class CakeTestFixtureTestFixture extends CakeTestFixture { +/** + * name Property + * + * @var string + */ + var $name = 'FixtureTest'; +/** + * table property + * + * @var string + */ + var $table = 'fixture_tests'; +/** + * Fields array + * + * @var array + */ + var $fields = array( + 'id' => array('type' => 'integer', 'key' => 'primary'), + 'name' => array('type' => 'string', 'length' => '255'), + 'created' => array('type' => 'datetime') + ); +/** + * Records property + * + * @var array + */ + var $records = array( + array('name' => 'Gandalf', 'created' => '2009-04-28 19:20:00'), + array('name' => 'Captain Picard', 'created' => '2009-04-28 19:20:00'), + array('name' => 'Chewbacca', 'created' => '2009-04-28 19:20:00') + ); +} +/** + * CakeTestFixtureImportFixture class + * + * @package cake + * @subpackage cake.cake.tests.cases.libs + */ +class CakeTestFixtureImportFixture extends CakeTestFixture { +/** + * Name property + * + * @var string + */ + var $name = 'ImportFixture'; +/** + * Import property + * + * @var mixed + */ + var $import = array('table' => 'fixture_tests', 'connection' => 'test_suite'); +} +/** + * CakeTestFixtureDefaultImportFixture class + * + * @package cake + * @subpackage cake.cake.tests.cases.libs + */ +class CakeTestFixtureDefaultImportFixture extends CakeTestFixture { +/** + * Name property + * + * @var string + */ + var $name = 'ImportFixture'; +} +/** + * FixtureImportTestModel class + * + * @package default + * @subpackage cake.cake.tests.cases.libs. + **/ +class FixtureImportTestModel extends Model { + var $name = 'FixtureImport'; + var $useTable = 'fixture_tests'; + var $useDbConfig = 'test_suite'; +} +Mock::generate('DboSource', 'FixtureMockDboSource'); +/** + * Test case for CakeTestFixture + * + * @package cake + * @subpackage cake.cake.tests.cases.libs + */ +class CakeTestFixtureTest extends CakeTestCase { +/** + * setUp method + * + * @access public + * @return void + */ + function setUp() { + $this->criticDb =& new FixtureMockDboSource(); + $this->criticDb->fullDebug = true; + } +/** + * tearDown + * + * @access public + * @return void + */ + function tearDown() { + unset($this->criticDb); + } +/** + * testInit + * + * @access public + * @return void + */ + function testInit() { + $Fixture =& new CakeTestFixtureTestFixture(); + unset($Fixture->table); + $Fixture->init(); + $this->assertEqual($Fixture->table, 'fixture_tests'); + $this->assertEqual($Fixture->primaryKey, 'id'); + + $Fixture =& new CakeTestFixtureTestFixture(); + $Fixture->primaryKey = 'my_random_key'; + $Fixture->init(); + $this->assertEqual($Fixture->primaryKey, 'my_random_key'); + + $this->_initDb(); + $Source =& new CakeTestFixtureTestFixture(); + $Source->create($this->db); + $Source->insert($this->db); + + $Fixture =& new CakeTestFixtureImportFixture(); + $expected = array('id', 'name', 'created'); + $this->assertEqual(array_keys($Fixture->fields), $expected); + + $db =& ConnectionManager::getDataSource('test_suite'); + $config = $db->config; + $config['prefix'] = 'fixture_test_suite_'; + ConnectionManager::create('fixture_test_suite', $config); + + $Fixture->fields = $Fixture->records = null; + $Fixture->import = array('table' => 'fixture_tests', 'connection' => 'test_suite', 'records' => true); + $Fixture->init(); + $this->assertEqual(count($Fixture->records), count($Source->records)); + + $Fixture =& new CakeTestFixtureImportFixture(); + $Fixture->fields = $Fixture->records = null; + $Fixture->import = array('model' => 'FixtureImportTestModel', 'connection' => 'test_suite'); + $Fixture->init(); + $this->assertEqual(array_keys($Fixture->fields), array('id', 'name', 'created')); + + $keys = array_flip(ClassRegistry::keys()); + $this->assertFalse(array_key_exists('fixtureimporttestmodel', $keys)); + + $Source->drop($this->db); + } +/** + * testImport + * + * @access public + * @return void + */ + function testImport() { + $this->_initDb(); + + $defaultDb =& ConnectionManager::getDataSource('default'); + $testSuiteDb =& ConnectionManager::getDataSource('test_suite'); + $defaultConfig = $defaultDb->config; + $testSuiteConfig = $testSuiteDb->config; + ConnectionManager::create('new_test_suite', array_merge($testSuiteConfig, array('prefix' => 'new_' . $testSuiteConfig['prefix']))); + $newTestSuiteDb =& ConnectionManager::getDataSource('new_test_suite'); + + $Source =& new CakeTestFixtureTestFixture(); + $Source->create($newTestSuiteDb); + $Source->insert($newTestSuiteDb); + + $defaultDb->config = $newTestSuiteDb->config; + + $Fixture =& new CakeTestFixtureDefaultImportFixture(); + $Fixture->fields = $Fixture->records = null; + $Fixture->import = array('model' => 'FixtureImportTestModel', 'connection' => 'new_test_suite'); + $Fixture->init(); + $this->assertEqual(array_keys($Fixture->fields), array('id', 'name', 'created')); + + $defaultDb->config = $defaultConfig; + + $keys = array_flip(ClassRegistry::keys()); + $this->assertFalse(array_key_exists('fixtureimporttestmodel', $keys)); + + $Source->drop($newTestSuiteDb); + } +/** + * test create method + * + * @access public + * @return void + */ + function testCreate() { + $Fixture =& new CakeTestFixtureTestFixture(); + $this->criticDb->expectAtLeastOnce('execute'); + $this->criticDb->expectAtLeastOnce('createSchema'); + $return = $Fixture->create($this->criticDb); + $this->assertTrue($this->criticDb->fullDebug); + $this->assertTrue($return); + + unset($Fixture->fields); + $return = $Fixture->create($this->criticDb); + $this->assertFalse($return); + } +/** + * test the insert method + * + * @access public + * @return void + */ + function testInsert() { + $Fixture =& new CakeTestFixtureTestFixture(); + $this->criticDb->setReturnValue('insertMulti', true); + $this->criticDb->expectAtLeastOnce('insertMulti'); + + $return = $Fixture->insert($this->criticDb); + $this->assertTrue($this->criticDb->fullDebug); + $this->assertTrue($return); + } +/** + * Test the drop method + * + * @access public + * @return void + */ + function testDrop() { + $Fixture =& new CakeTestFixtureTestFixture(); + $this->criticDb->setReturnValueAt(0, 'execute', true); + $this->criticDb->expectAtLeastOnce('execute'); + $this->criticDb->expectAtLeastOnce('dropSchema'); + + $return = $Fixture->drop($this->criticDb); + $this->assertTrue($this->criticDb->fullDebug); + $this->assertTrue($return); + + $this->criticDb->setReturnValueAt(1, 'execute', false); + $return = $Fixture->drop($this->criticDb); + $this->assertFalse($return); + } +/** + * Test the truncate method. + * + * @access public + * @return void + */ + function testTruncate() { + $Fixture =& new CakeTestFixtureTestFixture(); + $this->criticDb->expectAtLeastOnce('truncate'); + $Fixture->truncate($this->criticDb); + $this->assertTrue($this->criticDb->fullDebug); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/class_registry.test.php b/cake/tests/cases/libs/class_registry.test.php new file mode 100755 index 0000000..d40ed0e --- /dev/null +++ b/cake/tests/cases/libs/class_registry.test.php @@ -0,0 +1,314 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * ClassRegistryTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs + * @since CakePHP(tm) v 1.2.0.5432 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +App::import('Core', 'ClassRegistry'); +/** + * ClassRegisterModel class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class ClassRegisterModel extends CakeTestModel { +/** + * useTable property + * + * @var bool false + * @access public + */ + var $useTable = false; +} +/** + * RegisterArticle class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class RegisterArticle extends ClassRegisterModel { +/** + * name property + * + * @var string 'RegisterArticle' + * @access public + */ + var $name = 'RegisterArticle'; +} +/** + * RegisterArticleFeatured class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class RegisterArticleFeatured extends ClassRegisterModel { +/** + * name property + * + * @var string 'RegisterArticleFeatured' + * @access public + */ + var $name = 'RegisterArticleFeatured'; +} +/** + * RegisterArticleTag class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class RegisterArticleTag extends ClassRegisterModel { +/** + * name property + * + * @var string 'RegisterArticleTag' + * @access public + */ + var $name = 'RegisterArticleTag'; +} +/** + * RegistryPluginAppModel class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class RegistryPluginAppModel extends ClassRegisterModel { +/** + * tablePrefix property + * + * @var string 'something_' + * @access public + */ + var $tablePrefix = 'something_'; +} +/** + * TestRegistryPluginModel class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class TestRegistryPluginModel extends RegistryPluginAppModel { +/** + * name property + * + * @var string 'TestRegistryPluginModel' + * @access public + */ + var $name = 'TestRegistryPluginModel'; +} +/** + * RegisterCategory class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class RegisterCategory extends ClassRegisterModel { +/** + * name property + * + * @var string 'RegisterCategory' + * @access public + */ + var $name = 'RegisterCategory'; +} +/** + * ClassRegistryTest class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class ClassRegistryTest extends CakeTestCase { +/** + * testAddModel method + * + * @access public + * @return void + */ + function testAddModel() { + if (PHP5) { + $Tag = ClassRegistry::init('RegisterArticleTag'); + } else { + $Tag =& ClassRegistry::init('RegisterArticleTag'); + } + $this->assertTrue(is_a($Tag, 'RegisterArticleTag')); + + $TagCopy = ClassRegistry::isKeySet('RegisterArticleTag'); + $this->assertTrue($TagCopy); + + $Tag->name = 'SomeNewName'; + + if (PHP5) { + $TagCopy = ClassRegistry::getObject('RegisterArticleTag'); + } else { + $TagCopy =& ClassRegistry::getObject('RegisterArticleTag'); + } + + $this->assertTrue(is_a($TagCopy, 'RegisterArticleTag')); + $this->assertIdentical($Tag, $TagCopy); + + if (PHP5) { + $NewTag = ClassRegistry::init(array('class' => 'RegisterArticleTag', 'alias' => 'NewTag')); + } else { + $NewTag =& ClassRegistry::init(array('class' => 'RegisterArticleTag', 'alias' => 'NewTag')); + } + $this->assertTrue(is_a($Tag, 'RegisterArticleTag')); + + if (PHP5) { + $NewTagCopy = ClassRegistry::init(array('class' => 'RegisterArticleTag', 'alias' => 'NewTag')); + } else { + $NewTagCopy =& ClassRegistry::init(array('class' => 'RegisterArticleTag', 'alias' => 'NewTag')); + } + + $this->assertNotIdentical($Tag, $NewTag); + $this->assertIdentical($NewTag, $NewTagCopy); + + $NewTag->name = 'SomeOtherName'; + $this->assertNotIdentical($Tag, $NewTag); + $this->assertIdentical($NewTag, $NewTagCopy); + + $Tag->name = 'SomeOtherName'; + $this->assertNotIdentical($Tag, $NewTag); + + $this->assertTrue($TagCopy->name === 'SomeOtherName'); + + if (PHP5) { + $User = ClassRegistry::init(array('class' => 'RegisterUser', 'alias' => 'User', 'table' => false)); + } else { + $User =& ClassRegistry::init(array('class' => 'RegisterUser', 'alias' => 'User', 'table' => false)); + } + $this->assertTrue(is_a($User, 'AppModel')); + + if (PHP5) { + $UserCopy = ClassRegistry::init(array('class' => 'RegisterUser', 'alias' => 'User', 'table' => false)); + } else { + $UserCopy =& ClassRegistry::init(array('class' => 'RegisterUser', 'alias' => 'User', 'table' => false)); + } + $this->assertTrue(is_a($UserCopy, 'AppModel')); + $this->assertIdentical($User, $UserCopy); + + if (PHP5) { + $Category = ClassRegistry::init(array('class' => 'RegisterCategory')); + } else { + $Category =& ClassRegistry::init(array('class' => 'RegisterCategory')); + } + $this->assertTrue(is_a($Category, 'RegisterCategory')); + + if (PHP5) { + $ParentCategory = ClassRegistry::init(array('class' => 'RegisterCategory', 'alias' => 'ParentCategory')); + } else { + $ParentCategory =& ClassRegistry::init(array('class' => 'RegisterCategory', 'alias' => 'ParentCategory')); + } + $this->assertTrue(is_a($ParentCategory, 'RegisterCategory')); + $this->assertNotIdentical($Category, $ParentCategory); + + $this->assertNotEqual($Category->alias, $ParentCategory->alias); + $this->assertEqual('RegisterCategory', $Category->alias); + $this->assertEqual('ParentCategory', $ParentCategory->alias); + } +/** + * testClassRegistryFlush method + * + * @access public + * @return void + */ + function testClassRegistryFlush() { + $ArticleTag = ClassRegistry::getObject('RegisterArticleTag'); + $this->assertTrue(is_a($ArticleTag, 'RegisterArticleTag')); + ClassRegistry::flush(); + + $NoArticleTag = ClassRegistry::isKeySet('RegisterArticleTag'); + $this->assertFalse($NoArticleTag); + $this->assertTrue(is_a($ArticleTag, 'RegisterArticleTag')); + } +/** + * testAddMultipleModels method + * + * @access public + * @return void + */ + function testAddMultipleModels() { + $Article = ClassRegistry::isKeySet('Article'); + $this->assertFalse($Article); + + $Featured = ClassRegistry::isKeySet('Featured'); + $this->assertFalse($Featured); + + $Tag = ClassRegistry::isKeySet('Tag'); + $this->assertFalse($Tag); + + $models = array(array('class' => 'RegisterArticle', 'alias' => 'Article'), + array('class' => 'RegisterArticleFeatured', 'alias' => 'Featured'), + array('class' => 'RegisterArticleTag', 'alias' => 'Tag')); + + $added = ClassRegistry::init($models); + $this->assertTrue($added); + + $Article = ClassRegistry::isKeySet('Article'); + $this->assertTrue($Article); + + $Featured = ClassRegistry::isKeySet('Featured'); + $this->assertTrue($Featured); + + $Tag = ClassRegistry::isKeySet('Tag'); + $this->assertTrue($Tag); + + $Article = ClassRegistry::getObject('Article'); + $this->assertTrue(is_a($Article, 'RegisterArticle')); + + $Featured = ClassRegistry::getObject('Featured'); + $this->assertTrue(is_a($Featured, 'RegisterArticleFeatured')); + + $Tag = ClassRegistry::getObject('Tag'); + $this->assertTrue(is_a($Tag, 'RegisterArticleTag')); + } +/** + * testPluginAppModel method + * + * @access public + * @return void + */ + function testPluginAppModel() { + $TestRegistryPluginModel = ClassRegistry::isKeySet('TestRegistryPluginModel'); + $this->assertFalse($TestRegistryPluginModel); + + $TestRegistryPluginModel = ClassRegistry::init('RegistryPlugin.TestRegistryPluginModel'); + $this->assertTrue(is_a($TestRegistryPluginModel, 'TestRegistryPluginModel')); + + $this->assertEqual($TestRegistryPluginModel->tablePrefix, 'something_'); + + if (PHP5) { + $PluginUser = ClassRegistry::init(array('class' => 'RegistryPlugin.RegisterUser', 'alias' => 'RegistryPluginUser', 'table' => false)); + } else { + $PluginUser =& ClassRegistry::init(array('class' => 'RegistryPlugin.RegisterUser', 'alias' => 'RegistryPluginUser', 'table' => false)); + } + $this->assertTrue(is_a($PluginUser, 'RegistryPluginAppModel')); + + if (PHP5) { + $PluginUserCopy = ClassRegistry::getObject('RegistryPluginUser'); + } else { + $PluginUserCopy =& ClassRegistry::getObject('RegistryPluginUser'); + } + $this->assertTrue(is_a($PluginUserCopy, 'RegistryPluginAppModel')); + $this->assertIdentical($PluginUser, $PluginUserCopy); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/code_coverage_manager.test.php b/cake/tests/cases/libs/code_coverage_manager.test.php new file mode 100755 index 0000000..9a1e6c4 --- /dev/null +++ b/cake/tests/cases/libs/code_coverage_manager.test.php @@ -0,0 +1,655 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * CodeCoverageManagerTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs + * @since CakePHP(tm) v 1.2.0.4206 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +App::import('Core', 'CodeCoverageManager'); +require_once CAKE . 'tests' . DS . 'lib' . DS . 'cli_reporter.php'; +require_once CAKE . 'tests' . DS . 'lib' . DS . 'cake_reporter.php'; +/** + * CodeCoverageManagerTest class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class CodeCoverageManagerTest extends CakeTestCase { +/** + * Skip if XDebug not installed + * + * @access public + */ + function skip() { + $this->skipIf(!extension_loaded('xdebug'), '%s XDebug not installed'); + } +/** + * startTest Method + * Store reference of $_GET to restore later. + * + * @return void + **/ + function startCase() { + $this->_get = $_GET; + } +/** + * End Case - restore GET vars. + * + * @return void + **/ + function endCase() { + $_GET = $this->_get; + } +/** + * testNoTestCaseSupplied method + * + * @access public + * @return void + */ + function testNoTestCaseSupplied() { + if (PHP_SAPI != 'cli') { + unset($_GET['group']); + CodeCoverageManager::start(substr(md5(microtime()), 0, 5), new CakeHtmlReporter()); + CodeCoverageManager::report(false); + $this->assertError(); + + CodeCoverageManager::start('libs/'.basename(__FILE__), new CakeHtmlReporter()); + CodeCoverageManager::report(false); + $this->assertError(); + + $path = LIBS; + if (strpos(LIBS, ROOT) === false) { + $path = ROOT.DS.LIBS; + } + App::import('Core', 'Folder'); + $folder = new Folder(); + $folder->cd($path); + $contents = $folder->ls(); +/** + * remove method + * + * @param mixed $var + * @access public + * @return void + */ + function remove($var) { + return ($var != basename(__FILE__)); + } + $contents[1] = array_filter($contents[1], "remove"); + + foreach ($contents[1] as $file) { + CodeCoverageManager::start('libs'.DS.$file, new CakeHtmlReporter()); + CodeCoverageManager::report(false); + $this->assertNoErrors('libs'.DS.$file); + } + } + } +/** + * testGetTestObjectFileNameFromTestCaseFile method + * + * @access public + * @return void + */ + function testGetTestObjectFileNameFromTestCaseFile() { + $manager =& CodeCoverageManager::getInstance(); + $manager->reporter = new CakeHtmlReporter(); + + $expected = $manager->__testObjectFileFromCaseFile('models/some_file.test.php', true); + $this->assertIdentical(APP.'models'.DS.'some_file.php', $expected); + + $expected = $manager->__testObjectFileFromCaseFile('datasources/some_file.test.php', true); + $this->assertIdentical(APP.'models'.DS.'datasources'.DS.'some_file.php', $expected); + + $expected = $manager->__testObjectFileFromCaseFile('controllers/some_file.test.php', true); + $this->assertIdentical(APP.'controllers'.DS.'some_file.php', $expected); + + $expected = $manager->__testObjectFileFromCaseFile('views/some_file.test.php', true); + $this->assertIdentical(APP.'views'.DS.'some_file.php', $expected); + + $expected = $manager->__testObjectFileFromCaseFile('behaviors/some_file.test.php', true); + $this->assertIdentical(APP.'models'.DS.'behaviors'.DS.'some_file.php', $expected); + + $expected = $manager->__testObjectFileFromCaseFile('components/some_file.test.php', true); + $this->assertIdentical(APP.'controllers'.DS.'components'.DS.'some_file.php', $expected); + + $expected = $manager->__testObjectFileFromCaseFile('helpers/some_file.test.php', true); + $this->assertIdentical(APP.'views'.DS.'helpers'.DS.'some_file.php', $expected); + + $manager->pluginTest = 'bugs'; + $expected = $manager->__testObjectFileFromCaseFile('models/some_file.test.php', false); + $this->assertIdentical(APP.'plugins'.DS.'bugs'.DS.'models'.DS.'some_file.php', $expected); + + $manager->pluginTest = false; + $manager->reporter = new CLIReporter; + $expected = $manager->__testObjectFileFromCaseFile('libs/set.test.php', false); + $this->assertIdentical(ROOT.DS.'cake'.DS.'libs'.DS.'set.php', $expected); + } +/** + * testOfHtmlReport method + * + * @access public + * @return void + */ + function testOfHtmlReport() { + $manager =& CodeCoverageManager::getInstance(); + $code = <<<PHP +/** + * Set class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ + class Set extends Object { +/** + * Value of the Set object. + * + * @var array + * @access public + */ + var \$value = array(); +/** + * Constructor. Defaults to an empty array. + * + * @access public + */ + function __construct() { + if (func_num_args() == 1 && is_array(func_get_arg(0))) { + \$this->value = func_get_arg(0); + } else { + \$this->value = func_get_args(); + } + } +/** + * Returns the contents of the Set object + * + * @return array + * @access public + */ + function &get() { + return \$this->value; + } +/** + * This function can be thought of as a hybrid between PHP's array_merge and array_merge_recursive. The difference + * to the two is that if an array key contains another array then the function behaves recursive (unlike array_merge) + * but does not do if for keys containing strings (unlike array_merge_recursive). See the unit test for more information. + * + * Note: This function will work with an unlimited amount of arguments and typecasts non-array parameters into arrays. + * + * @param array \$arr1 Array to be merged + * @param array \$arr2 Array to merge with + * @return array Merged array + * @access public + */ + function merge(\$arr1, \$arr2 = null) { + \$args = func_get_args(); + + if (isset(\$this) && is_a(\$this, 'set')) { + \$backtrace = debug_backtrace(); + \$previousCall = strtolower(\$backtrace[1]['class'].'::'.\$backtrace[1]['function']); + if (\$previousCall != 'set::merge') { + \$r =& \$this->value; + array_unshift(\$args, null); + } + } + if (!isset(\$r)) { + \$r = (array)current(\$args); + } + + while ((\$arg = next(\$args)) !== false) { + if (is_a(\$arg, 'set')) { + \$arg = \$arg->get(); + } + + foreach ((array)\$arg as \$key => \$val) { + if (is_array(\$val) && isset(\$r[\$key]) && is_array(\$r[\$key])) { + \$r[\$key] = Set::merge(\$r[\$key], \$val); + } elseif (is_int(\$key)) { + + } else { + \$r[\$key] = \$val; + } + } + } + return \$r; + } +PHP; + + $testObjectFile = explode("\n", $code); + $coverageData = array( + 0 => 1, + 1 => 1, + 2 => -2, + 3 => -2, + 4 => -2, + 5 => -2, + 6 => -2, + 7 => -2, + 8 => -1, + 9 => -2, + 10 => -2, + 11 => -2, + 12 => -2, + 13 => -2, + 14 => 1, + 15 => 1, + 16 => -1, + 17 => 1, + 18 => 1, + 19 => -1, + 20 => 1, + 21 => -2, + 22 => -2, + 23 => -2, + 24 => -2, + 25 => -2, + 26 => -2, + 27 => 1, + 28 => -1, + 29 => 1, + 30 => 1, + 31 => -2, + 32 => -2, + 33 => -2, + 34 => -2, + 35 => -2, + 36 => -2, + 37 => -2, + 38 => -2, + 39 => -2, + 40 => -2, + 41 => -2, + 42 => -2, + 43 => -1, + ); + $execCodeLines = range(0, 72); + $result = explode("</div>", $report = $manager->reportCaseHtml($testObjectFile, $coverageData, $execCodeLines)); + + foreach ($result as $num => $line) { + $num++; + if (array_key_exists($num, $coverageData)) { + if ($coverageData[$num] == 1) { + $this->assertTrue(strpos($line, 'covered') !== false, $num.': '.$line." fails"); + } + + if (!array_key_exists($num, $execCodeLines) || $coverageData[$num] == -2) { + $this->assertTrue(strpos($line, 'ignored') !== false, $num.': '.$line." fails"); + } + + if ($coverageData[$num] == -1) { + $this->assertTrue(strpos($line, 'uncovered') !== false, $num.': '.$line." fails"); + } + } + } + } +/** + * testOfHtmlDiffReport method + * + * @access public + * @return void + */ + function testOfHtmlDiffReport() { + $manager =& CodeCoverageManager::getInstance(); + $code = <<<PHP +/** + * Set class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ + class Set extends Object { +/** + * Value of the Set object. + * + * @var array + * @access public + */ + var \$value = array(); +/** + * Constructor. Defaults to an empty array. + * + * @access public + */ + function __construct() { + if (func_num_args() == 1 && is_array(func_get_arg(0))) { + \$this->value = func_get_arg(0); + } else { + \$this->value = func_get_args(); + } + } +/** + * Returns the contents of the Set object + * + * @return array + * @access public + */ + function &get() { + return \$this->value; + } +/** + * This function can be thought of as a hybrid between PHP's array_merge and array_merge_recursive. The difference + * to the two is that if an array key contains another array then the function behaves recursive (unlike array_merge) + * but does not do if for keys containing strings (unlike array_merge_recursive). See the unit test for more information. + * + * Note: This function will work with an unlimited amount of arguments and typecasts non-array parameters into arrays. + * + * @param array \$arr1 Array to be merged + * @param array \$arr2 Array to merge with + * @return array Merged array + * @access public + */ + function merge(\$arr1, \$arr2 = null) { + \$args = func_get_args(); + + if (isset(\$this) && is_a(\$this, 'set')) { + \$backtrace = debug_backtrace(); + \$previousCall = strtolower(\$backtrace[1]['class'].'::'.\$backtrace[1]['function']); + if (\$previousCall != 'set::merge') { + \$r =& \$this->value; + array_unshift(\$args, null); + } + } + if (!isset(\$r)) { + \$r = (array)current(\$args); + } + + while ((\$arg = next(\$args)) !== false) { + if (is_a(\$arg, 'set')) { + \$arg = \$arg->get(); + } + + foreach ((array)\$arg as \$key => \$val) { + if (is_array(\$val) && isset(\$r[\$key]) && is_array(\$r[\$key])) { + \$r[\$key] = Set::merge(\$r[\$key], \$val); + } elseif (is_int(\$key)) { + + } else { + \$r[\$key] = \$val; + } + } + } + return \$r; + } +PHP; + + $testObjectFile = explode("\n", $code); + $coverageData = array( + 0 => 1, + 1 => 1, + 2 => -2, + 3 => -2, + 4 => -2, + 5 => -2, + 6 => -2, + 7 => -2, + 8 => -1, + 9 => -2, + 10 => -2, + 11 => -2, + 12 => -2, + 13 => -2, + 14 => 1, + 15 => 1, + 16 => -1, + 17 => 1, + 18 => 1, + 19 => -1, + 20 => 1, + 21 => -2, + 22 => -2, + 23 => -2, + 24 => -2, + 25 => -2, + 26 => -2, + 27 => 1, + 28 => -1, + 29 => 1, + 30 => 1, + 31 => -2, + 32 => -2, + 33 => -2, + 34 => -2, + 35 => -2, + 36 => -2, + 37 => -2, + 38 => -2, + 39 => -2, + 40 => -2, + 41 => -2, + 42 => -2, + 43 => -1, + 44 => -2, + 45 => -2, + 46 => -2, + 47 => -2, + 48 => 1, + 49 => 1, + 50 => -1, + 51 => 1, + 52 => 1, + 53 => -2, + 54 => -2, + 55 => 1, + 56 => 1, + 57 => 1, + 58 => 1, + 59 => -1, + 60 => 1, + 61 => 1, + 62 => -2, + 63 => -2, + 64 => 1, + 65 => -2, + 66 => 1, + 67 => -1, + 68 => -2, + 69 => -1, + 70 => -1, + 71 => 1, + 72 => -2, + ); + $expected = array( + 0 => 'ignored', + 1 => 'ignored', + 2 => 'ignored', + 3 => 'ignored', + 4 => 'ignored', + 5 => 'ignored show start realstart', + 6 => 'ignored show', + 7 => 'ignored show', + 8 => 'uncovered show', + 9 => 'ignored show', + 10 => 'ignored show', + 11 => 'ignored show end', + 12 => 'ignored', + 13 => 'ignored show start', + 14 => 'covered show', + 15 => 'covered show', + 16 => 'uncovered show', + 17 => 'covered show show', + 18 => 'covered show show', + 19 => 'uncovered show', + 20 => 'covered show', + 21 => 'ignored show', + 22 => 'ignored show end', + 23 => 'ignored', + 24 => 'ignored', + 25 => 'ignored show start', + 26 => 'ignored show', + 27 => 'covered show', + 28 => 'uncovered show', + 29 => 'covered show', + 30 => 'covered show', + 31 => 'ignored show end', + 32 => 'ignored', + 33 => 'ignored', + 34 => 'ignored', + 35 => 'ignored', + 36 => 'ignored', + 37 => 'ignored', + 38 => 'ignored', + 39 => 'ignored', + 40 => 'ignored show start', + 41 => 'ignored show', + 42 => 'ignored show', + 43 => 'uncovered show', + 41 => 'ignored show', + 42 => 'ignored show', + 43 => 'uncovered show', + 44 => 'ignored show', + 45 => 'ignored show', + 46 => 'ignored show', + 47 => 'ignored show', + 48 => 'covered show', + 49 => 'covered show', + 50 => 'uncovered show', + 51 => 'covered show', + 52 => 'covered show', + 53 => 'ignored show end', + 54 => 'ignored', + 55 => 'covered', + 56 => 'covered show start', + 57 => 'covered show', + 58 => 'covered show', + 59 => 'uncovered show', + 60 => 'covered show', + 61 => 'covered show', + 62 => 'ignored show end', + 63 => 'ignored', + 64 => 'covered show start', + 65 => 'ignored show', + 66 => 'covered show show', + 67 => 'uncovered show', + 68 => 'ignored show', + 69 => 'uncovered show', + 70 => 'uncovered show', + 71 => 'covered show', + 72 => 'ignored show', + 73 => 'ignored show end end', + ); + $execCodeLines = range(0, 72); + $result = explode("</div>", $report = $manager->reportCaseHtmlDiff($testObjectFile, $coverageData, $execCodeLines, 3)); + + foreach ($result as $line) { + preg_match('/<span class="line-num">(.*?)<\/span>/', $line, $matches); + if (!isset($matches[1])) { + continue; + } + + $num = $matches[1]; + $class = $expected[$num]; + $pattern = '/<div class="code-line '.$class.'">/'; + $this->assertPattern($pattern, $line, $num.': '.$line." fails"); + } + } +/** + * testArrayStrrpos method + * + * @access public + * @return void + */ + function testArrayStrrpos() { + $manager =& CodeCoverageManager::getInstance(); + + $a = array( + 'apples', + 'bananas', + 'oranges' + ); + $this->assertEqual(1, $manager->__array_strpos($a, 'ba', true)); + $this->assertEqual(2, $manager->__array_strpos($a, 'range', true)); + $this->assertEqual(0, $manager->__array_strpos($a, 'pp', true)); + $this->assertFalse($manager->__array_strpos('', 'ba', true)); + $this->assertFalse($manager->__array_strpos(false, 'ba', true)); + $this->assertFalse($manager->__array_strpos(array(), 'ba', true)); + + $a = array( + 'rang', + 'orange', + 'oranges' + ); + $this->assertEqual(0, $manager->__array_strpos($a, 'rang')); + $this->assertEqual(2, $manager->__array_strpos($a, 'rang', true)); + $this->assertEqual(1, $manager->__array_strpos($a, 'orange', false)); + $this->assertEqual(1, $manager->__array_strpos($a, 'orange')); + $this->assertEqual(2, $manager->__array_strpos($a, 'orange', true)); + } +/** + * testGetExecutableLines method + * + * @access public + * @return void + */ + function testGetExecutableLines() { + $manager =& CodeCoverageManager::getInstance(); + $code = <<<HTML + \$manager =& CodeCoverageManager::getInstance(); +HTML; + $result = $manager->__getExecutableLines($code); + foreach ($result as $line) { + $this->assertNotIdentical($line, ''); + } + + $code = <<<HTML + { + } + <?php?> + ?> + <? +} +{{}} +(()) + @codeCoverageIgnoreStart + some + more + code + here + @codeCoverageIgnoreEnd +HTML; + $result = $manager->__getExecutableLines($code); + foreach ($result as $line) { + $this->assertIdentical(trim($line), ''); + } + } +/** + * testCalculateCodeCoverage method + * + * @access public + * @return void + */ + function testCalculateCodeCoverage() { + $manager =& CodeCoverageManager::getInstance(); + $data = array( + '25' => array(100, 25), + '50' => array(100, 50), + '0' => array(0, 0), + '0' => array(100, 0), + '100' => array(100, 100), + ); + foreach ($data as $coverage => $lines) { + $this->assertEqual($coverage, $manager->__calcCoverage($lines[0], $lines[1])); + } + + $manager->__calcCoverage(100, 1000); + $this->assertError(); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/configure.test.php b/cake/tests/cases/libs/configure.test.php new file mode 100755 index 0000000..e29f504 --- /dev/null +++ b/cake/tests/cases/libs/configure.test.php @@ -0,0 +1,540 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * ConfigureTest file + * + * Holds several tests + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs + * @since CakePHP(tm) v 1.2.0.5432 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +App::import('Core', 'Configure'); +/** + * ConfigureTest + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class ConfigureTest extends CakeTestCase { +/** + * setUp method + * + * @access public + * @return void + */ + function setUp() { + $this->_cacheDisable = Configure::read('Cache.disable'); + Configure::write('Cache.disable', true); + + $this->_debug = Configure::read('debug'); + } +/** + * tearDown method + * + * @access public + * @return void + */ + function tearDown() { + if (file_exists(TMP . 'cache' . DS . 'persistent' . DS . 'cake_core_core_paths')) { + unlink(TMP . 'cache' . DS . 'persistent' . DS . 'cake_core_core_paths'); + } + if (file_exists(TMP . 'cache' . DS . 'persistent' . DS . 'cake_core_dir_map')) { + unlink(TMP . 'cache' . DS . 'persistent' . DS . 'cake_core_dir_map'); + } + if (file_exists(TMP . 'cache' . DS . 'persistent' . DS . 'cake_core_file_map')) { + unlink(TMP . 'cache' . DS . 'persistent' . DS . 'cake_core_file_map'); + } + if (file_exists(TMP . 'cache' . DS . 'persistent' . DS . 'cake_core_object_map')) { + unlink(TMP . 'cache' . DS . 'persistent' . DS . 'cake_core_object_map'); + } + if (file_exists(TMP . 'cache' . DS . 'persistent' . DS . 'test.config.php')) { + unlink(TMP . 'cache' . DS . 'persistent' . DS . 'test.config.php'); + } + if (file_exists(TMP . 'cache' . DS . 'persistent' . DS . 'test.php')) { + unlink(TMP . 'cache' . DS . 'persistent' . DS . 'test.php'); + } + Configure::write('debug', $this->_debug); + Configure::write('Cache.disable', $this->_cacheDisable); + } +/** + * testListObjects method + * + * @access public + * @return void + */ + function testListObjects() { + $result = Configure::listObjects('class', TEST_CAKE_CORE_INCLUDE_PATH . 'libs'); + $this->assertTrue(in_array('Xml', $result)); + $this->assertTrue(in_array('Cache', $result)); + $this->assertTrue(in_array('HttpSocket', $result)); + + $result = Configure::listObjects('behavior'); + $this->assertTrue(in_array('Tree', $result)); + + $result = Configure::listObjects('controller'); + $this->assertTrue(in_array('Pages', $result)); + + $result = Configure::listObjects('component'); + $this->assertTrue(in_array('Auth', $result)); + + $result = Configure::listObjects('view'); + $this->assertTrue(in_array('Media', $result)); + + $result = Configure::listObjects('helper'); + $this->assertTrue(in_array('Html', $result)); + + $result = Configure::listObjects('model'); + $notExpected = array('AppModel', 'Behavior', 'ConnectionManager', 'DbAcl', 'Model', 'Schema'); + + foreach ($notExpected as $class) { + $this->assertFalse(in_array($class, $result)); + } + + $result = Configure::listObjects('file'); + $this->assertFalse($result); + + $result = Configure::listObjects('file', 'non_existing_configure'); + $expected = array(); + $this->assertEqual($result, $expected); + + $result = Configure::listObjects('NonExistingType'); + $this->assertFalse($result); + } +/** + * testRead method + * + * @access public + * @return void + */ + function testRead() { + $expected = 'ok'; + Configure::write('level1.level2.level3_1', $expected); + Configure::write('level1.level2.level3_2', 'something_else'); + $result = Configure::read('level1.level2.level3_1'); + $this->assertEqual($expected, $result); + + $result = Configure::read('level1.level2.level3_2'); + $this->assertEqual($result, 'something_else'); + + $result = Configure::read('debug'); + $this->assertTrue($result >= 0); + } +/** + * testWrite method + * + * @access public + * @return void + */ + function testWrite() { + Configure::write('SomeName.someKey', 'myvalue'); + $result = Configure::read('SomeName.someKey'); + $this->assertEqual($result, 'myvalue'); + + Configure::write('SomeName.someKey', null); + $result = Configure::read('SomeName.someKey'); + $this->assertEqual($result, null); + } +/** + * testSetErrorReporting Level + * + * @return void + **/ + function testSetErrorReportingLevel() { + Configure::write('debug', 0); + $result = ini_get('error_reporting'); + $this->assertEqual($result, 0); + + Configure::write('debug', 2); + $result = ini_get('error_reporting'); + $this->assertEqual($result, E_ALL & ~E_DEPRECATED); + + $result = ini_get('display_errors'); + $this->assertEqual($result, 1); + + Configure::write('debug', 0); + $result = ini_get('error_reporting'); + $this->assertEqual($result, 0); + } +/** + * testDelete method + * + * @access public + * @return void + */ + function testDelete() { + Configure::write('SomeName.someKey', 'myvalue'); + $result = Configure::read('SomeName.someKey'); + $this->assertEqual($result, 'myvalue'); + + Configure::delete('SomeName.someKey'); + $result = Configure::read('SomeName.someKey'); + $this->assertTrue($result === null); + + Configure::write('SomeName', array('someKey' => 'myvalue', 'otherKey' => 'otherValue')); + + $result = Configure::read('SomeName.someKey'); + $this->assertEqual($result, 'myvalue'); + + $result = Configure::read('SomeName.otherKey'); + $this->assertEqual($result, 'otherValue'); + + Configure::delete('SomeName'); + + $result = Configure::read('SomeName.someKey'); + $this->assertTrue($result === null); + + $result = Configure::read('SomeName.otherKey'); + $this->assertTrue($result === null); + } +/** + * testLoad method + * + * @access public + * @return void + */ + function testLoad() { + $result = Configure::load('non_existing_configuration_file'); + $this->assertFalse($result); + + $result = Configure::load('config'); + $this->assertTrue($result === null); + } +/** + * testStore method + * + * @access public + * @return void + */ + function testStoreAndLoad() { + Configure::write('Cache.disable', false); + + $expected = array('data' => 'value'); + Configure::store('SomeExample', 'test', $expected); + + Configure::load('test'); + $config = Configure::read('SomeExample'); + $this->assertEqual($config, $expected); + + $expected = array('data' => array('first' => 'value', 'second' => 'value2')); + Configure::store('AnotherExample', 'test.config', $expected); + + Configure::load('test.config'); + $config = Configure::read('AnotherExample'); + $this->assertEqual($config, $expected); + } +/** + * testVersion method + * + * @access public + * @return void + */ + function testVersion() { + $result = Configure::version(); + $this->assertTrue(version_compare($result, '1.2', '>=')); + } +/** + * testBuildPaths method + * + * @access public + * @return void + */ + function testBuildPaths() { + Configure::buildPaths(array()); + $models = Configure::read('modelPaths'); + $this->assertTrue(!empty($models)); + } +} +/** + * AppImportTest class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class AppImportTest extends UnitTestCase { +/** + * testClassLoading method + * + * @access public + * @return void + */ + function testClassLoading() { + $file = App::import(); + $this->assertTrue($file); + + $file = App::import('Core', 'Model', false); + $this->assertTrue($file); + + $file = App::import('Model', 'SomeRandomModelThatDoesNotExist', false); + $this->assertFalse($file); + + $file = App::import('Model', 'AppModel', false); + $this->assertTrue($file); + + $file = App::import('WrongType', null, true, array(), ''); + $this->assertTrue($file); + + $file = App::import('Model', 'NonExistingPlugin.NonExistingModel', false); + $this->assertFalse($file); + + $file = App::import('Core', 'NonExistingPlugin.NonExistingModel', false); + $this->assertFalse($file); + + $file = App::import('Model', array('NonExistingPlugin.NonExistingModel'), false); + $this->assertFalse($file); + + $file = App::import('Core', array('NonExistingPlugin.NonExistingModel'), false); + $this->assertFalse($file); + + $file = App::import('Core', array('NonExistingPlugin.NonExistingModel.AnotherChild'), false); + $this->assertFalse($file); + + if (!class_exists('AppController')) { + $classes = array_flip(get_declared_classes()); + + if (PHP5) { + $this->assertFalse(isset($classes['PagesController'])); + $this->assertFalse(isset($classes['AppController'])); + } else { + $this->assertFalse(isset($classes['pagescontroller'])); + $this->assertFalse(isset($classes['appcontroller'])); + } + + $file = App::import('Controller', 'Pages'); + $this->assertTrue($file); + + $classes = array_flip(get_declared_classes()); + + if (PHP5) { + $this->assertTrue(isset($classes['PagesController'])); + $this->assertTrue(isset($classes['AppController'])); + } else { + $this->assertTrue(isset($classes['pagescontroller'])); + $this->assertTrue(isset($classes['appcontroller'])); + } + + $file = App::import('Behavior', 'Containable'); + $this->assertTrue($file); + + $file = App::import('Component', 'RequestHandler'); + $this->assertTrue($file); + + $file = App::import('Helper', 'Form'); + $this->assertTrue($file); + + $file = App::import('Model', 'NonExistingModel'); + $this->assertFalse($file); + } + + $_back = Configure::read('pluginPaths'); + Configure::write('pluginPaths', array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)); + + $result = App::import('Controller', 'TestPlugin.Tests'); + $this->assertTrue($result); + $this->assertTrue(class_exists('TestPluginAppController')); + $this->assertTrue(class_exists('TestsController')); + + $result = App::import('Helper', 'TestPlugin.OtherHelper'); + $this->assertTrue($result); + $this->assertTrue(class_exists('OtherHelperHelper')); + + Configure::write('pluginPaths', $_back); + } +/** + * testFileLoading method + * + * @access public + * @return void + */ + function testFileLoading () { + $file = App::import('File', 'RealFile', false, array(), TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'config.php'); + $this->assertTrue($file); + + $file = App::import('File', 'NoFile', false, array(), TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'cake' . DS . 'config.php'); + $this->assertFalse($file); + } + // import($type = null, $name = null, $parent = true, $file = null, $search = array(), $return = false) { +/** + * testFileLoadingWithArray method + * + * @access public + * @return void + */ + function testFileLoadingWithArray() { + $type = array('type' => 'File', 'name' => 'SomeName', 'parent' => false, + 'file' => TEST_CAKE_CORE_INCLUDE_PATH . DS . 'config' . DS . 'config.php'); + $file = App::import($type); + $this->assertTrue($file); + + $type = array('type' => 'File', 'name' => 'NoFile', 'parent' => false, + 'file' => TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'cake' . DS . 'config.php'); + $file = App::import($type); + $this->assertFalse($file); + } +/** + * testFileLoadingReturnValue method + * + * @access public + * @return void + */ + function testFileLoadingReturnValue () { + $file = App::import('File', 'Name', false, array(), TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'config.php', true); + $this->assertTrue($file); + + $this->assertTrue(isset($file['Cake.version'])); + + $type = array('type' => 'File', 'name' => 'OtherName', 'parent' => false, + 'file' => TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'config.php', 'return' => true); + $file = App::import($type); + $this->assertTrue($file); + + $this->assertTrue(isset($file['Cake.version'])); + } +/** + * testLoadingWithSearch method + * + * @access public + * @return void + */ + function testLoadingWithSearch () { + $file = App::import('File', 'NewName', false, array(TEST_CAKE_CORE_INCLUDE_PATH ), 'config.php'); + $this->assertTrue($file); + + $file = App::import('File', 'AnotherNewName', false, array(LIBS), 'config.php'); + $this->assertFalse($file); + } +/** + * testLoadingWithSearchArray method + * + * @access public + * @return void + */ + function testLoadingWithSearchArray () { + $type = array('type' => 'File', 'name' => 'RandomName', 'parent' => false, 'file' => 'config.php', 'search' => array(TEST_CAKE_CORE_INCLUDE_PATH )); + $file = App::import($type); + $this->assertTrue($file); + + $type = array('type' => 'File', 'name' => 'AnotherRandomName', 'parent' => false, 'file' => 'config.php', 'search' => array(LIBS)); + $file = App::import($type); + $this->assertFalse($file); + } +/** + * testMultipleLoading method + * + * @access public + * @return void + */ + function testMultipleLoading() { + $toLoad = array('I18n', 'Socket'); + + $classes = array_flip(get_declared_classes()); + $this->assertFalse(isset($classes['i18n'])); + $this->assertFalse(isset($classes['Socket'])); + + $load = App::import($toLoad); + $this->assertTrue($load); + + $classes = array_flip(get_declared_classes()); + + if (PHP5) { + $this->assertTrue(isset($classes['I18n'])); + } else { + $this->assertTrue(isset($classes['i18n'])); + } + + $load = App::import(array('I18n', 'SomeNotFoundClass', 'Socket')); + $this->assertFalse($load); + + $load = App::import($toLoad); + $this->assertTrue($load); + } +/** + * This test only works if you have plugins/my_plugin set up. + * plugins/my_plugin/models/my_plugin.php and other_model.php + */ +/* + function testMultipleLoadingByType() { + $classes = array_flip(get_declared_classes()); + $this->assertFalse(isset($classes['OtherPlugin'])); + $this->assertFalse(isset($classes['MyPlugin'])); + + + $load = App::import('Model', array('MyPlugin.OtherPlugin', 'MyPlugin.MyPlugin')); + $this->assertTrue($load); + + $classes = array_flip(get_declared_classes()); + $this->assertTrue(isset($classes['OtherPlugin'])); + $this->assertTrue(isset($classes['MyPlugin'])); + } +*/ + function testLoadingVendor() { + Configure::write('pluginPaths', array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)); + Configure::write('vendorPaths', array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'vendors'. DS)); + + ob_start(); + $result = App::import('Vendor', 'TestPlugin.TestPluginAsset', array('ext' => 'css')); + $text = ob_get_clean(); + $this->assertTrue($result); + $this->assertEqual($text, 'this is the test plugin asset css file'); + + ob_start(); + $result = App::import('Vendor', 'TestAsset', array('ext' => 'css')); + $text = ob_get_clean(); + $this->assertTrue($result); + $this->assertEqual($text, 'this is the test asset css file'); + + $result = App::import('Vendor', 'TestPlugin.SamplePlugin'); + $this->assertTrue($result); + $this->assertTrue(class_exists('SamplePluginClassTestName')); + + $result = App::import('Vendor', 'ConfigureTestVendorSample'); + $this->assertTrue($result); + $this->assertTrue(class_exists('ConfigureTestVendorSample')); + + ob_start(); + $result = App::import('Vendor', 'SomeName', array('file' => 'some.name.php')); + $text = ob_get_clean(); + $this->assertTrue($result); + $this->assertEqual($text, 'This is a file with dot in file name'); + + ob_start(); + $result = App::import('Vendor', 'TestHello', array('file' => 'Test'.DS.'hello.php')); + $text = ob_get_clean(); + $this->assertTrue($result); + $this->assertEqual($text, 'This is the hello.php file in Test directory'); + + ob_start(); + $result = App::import('Vendor', 'MyTest', array('file' => 'Test'.DS.'MyTest.php')); + $text = ob_get_clean(); + $this->assertTrue($result); + $this->assertEqual($text, 'This is the MyTest.php file'); + + ob_start(); + $result = App::import('Vendor', 'Welcome'); + $text = ob_get_clean(); + $this->assertTrue($result); + $this->assertEqual($text, 'This is the welcome.php file in vendors directory'); + + ob_start(); + $result = App::import('Vendor', 'TestPlugin.Welcome'); + $text = ob_get_clean(); + $this->assertTrue($result); + $this->assertEqual($text, 'This is the welcome.php file in test_plugin/vendors directory'); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/controller/component.test.php b/cake/tests/cases/libs/controller/component.test.php new file mode 100755 index 0000000..e3f78b2 --- /dev/null +++ b/cake/tests/cases/libs/controller/component.test.php @@ -0,0 +1,521 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * ComponentTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs.controller + * @since CakePHP(tm) v 1.2.0.5436 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +App::import('Core', array('Component', 'Controller')); + +if (!class_exists('AppController')) { +/** + * AppController class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ + class AppController extends Controller { +/** + * name property + * + * @var string 'App' + * @access public + */ + var $name = 'App'; +/** + * uses property + * + * @var array + * @access public + */ + var $uses = array(); +/** + * helpers property + * + * @var array + * @access public + */ + var $helpers = array(); +/** + * components property + * + * @var array + * @access public + */ + var $components = array('Orange' => array('colour' => 'blood orange')); + } +} elseif (!defined('APP_CONTROLLER_EXISTS')){ + define('APP_CONTROLLER_EXISTS', true); +} +/** + * ParamTestComponent + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class ParamTestComponent extends Object { +/** + * name property + * + * @var string 'ParamTest' + * @access public + */ + var $name = 'ParamTest'; +/** + * components property + * + * @var array + * @access public + */ + var $components = array('Banana' => array('config' => 'value')); +/** + * initialize method + * + * @param mixed $controller + * @param mixed $settings + * @access public + * @return void + */ + function initialize(&$controller, $settings) { + foreach ($settings as $key => $value) { + if (is_numeric($key)) { + $this->{$value} = true; + } else { + $this->{$key} = $value; + } + } + } +} +/** + * ComponentTestController class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class ComponentTestController extends AppController { +/** + * name property + * + * @var string 'ComponentTest' + * @access public + */ + var $name = 'ComponentTest'; +/** + * uses property + * + * @var array + * @access public + */ + var $uses = array(); +} +/** + * AppleComponent class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class AppleComponent extends Object { +/** + * components property + * + * @var array + * @access public + */ + var $components = array('Orange'); +/** + * testName property + * + * @var mixed null + * @access public + */ + var $testName = null; +/** + * startup method + * + * @param mixed $controller + * @access public + * @return void + */ + function startup(&$controller) { + $this->testName = $controller->name; + } +} +/** + * OrangeComponent class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class OrangeComponent extends Object { +/** + * components property + * + * @var array + * @access public + */ + var $components = array('Banana'); +/** + * initialize method + * + * @param mixed $controller + * @access public + * @return void + */ + function initialize(&$controller, $settings) { + $this->Controller = $controller; + $this->Banana->testField = 'OrangeField'; + $this->settings = $settings; + } +/** + * startup method + * + * @param Controller $controller + * @return string + * @access public + */ + function startup(&$controller) { + $controller->foo = 'pass'; + } +} +/** + * BananaComponent class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class BananaComponent extends Object { +/** + * testField property + * + * @var string 'BananaField' + * @access public + */ + var $testField = 'BananaField'; +/** + * startup method + * + * @param Controller $controller + * @return string + * @access public + */ + function startup(&$controller) { + $controller->bar = 'fail'; + } +} +/** + * MutuallyReferencingOneComponent class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class MutuallyReferencingOneComponent extends Object { +/** + * components property + * + * @var array + * @access public + */ + var $components = array('MutuallyReferencingTwo'); +} +/** + * MutuallyReferencingTwoComponent class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class MutuallyReferencingTwoComponent extends Object { +/** + * components property + * + * @var array + * @access public + */ + var $components = array('MutuallyReferencingOne'); +} +/** + * SomethingWithEmailComponent class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class SomethingWithEmailComponent extends Object { +/** + * components property + * + * @var array + * @access public + */ + var $components = array('Email'); +} +/** + * ComponentTest class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class ComponentTest extends CakeTestCase { +/** + * setUp method + * + * @access public + * @return void + */ + function setUp() { + $this->_pluginPaths = Configure::read('pluginPaths'); + Configure::write('pluginPaths', array( + TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS + )); + } +/** + * tearDown method + * + * @access public + * @return void + */ + function tearDown() { + Configure::write('pluginPaths', $this->_pluginPaths); + ClassRegistry::flush(); + } +/** + * testLoadComponents method + * + * @access public + * @return void + */ + function testLoadComponents() { + $Controller =& new ComponentTestController(); + $Controller->components = array('RequestHandler'); + + $Component =& new Component(); + $Component->init($Controller); + + $this->assertTrue(is_a($Controller->RequestHandler, 'RequestHandlerComponent')); + + $Controller =& new ComponentTestController(); + $Controller->plugin = 'test_plugin'; + $Controller->components = array('RequestHandler', 'TestPluginComponent'); + + $Component =& new Component(); + $Component->init($Controller); + + $this->assertTrue(is_a($Controller->RequestHandler, 'RequestHandlerComponent')); + $this->assertTrue(is_a($Controller->TestPluginComponent, 'TestPluginComponentComponent')); + $this->assertTrue(is_a( + $Controller->TestPluginComponent->TestPluginOtherComponent, + 'TestPluginOtherComponentComponent' + )); + $this->assertFalse(isset($Controller->TestPluginOtherComponent)); + + $Controller =& new ComponentTestController(); + $Controller->components = array('Security'); + + $Component =& new Component(); + $Component->init($Controller); + + $this->assertTrue(is_a($Controller->Security, 'SecurityComponent')); + $this->assertTrue(is_a($Controller->Security->Session, 'SessionComponent')); + + $Controller =& new ComponentTestController(); + $Controller->components = array('Security', 'Cookie', 'RequestHandler'); + + $Component =& new Component(); + $Component->init($Controller); + + $this->assertTrue(is_a($Controller->Security, 'SecurityComponent')); + $this->assertTrue(is_a($Controller->Security->RequestHandler, 'RequestHandlerComponent')); + $this->assertTrue(is_a($Controller->RequestHandler, 'RequestHandlerComponent')); + $this->assertTrue(is_a($Controller->Cookie, 'CookieComponent')); + } +/** + * test component loading + * + * @return void + */ + function testNestedComponentLoading() { + $Controller =& new ComponentTestController(); + $Controller->components = array('Apple'); + $Controller->constructClasses(); + $Controller->Component->initialize($Controller); + + $this->assertTrue(is_a($Controller->Apple, 'AppleComponent')); + $this->assertTrue(is_a($Controller->Apple->Orange, 'OrangeComponent')); + $this->assertTrue(is_a($Controller->Apple->Orange->Banana, 'BananaComponent')); + $this->assertTrue(is_a($Controller->Apple->Orange->Controller, 'ComponentTestController')); + $this->assertTrue(empty($Controller->Apple->Session)); + $this->assertTrue(empty($Controller->Apple->Orange->Session)); + } +/** + * Tests Component::startup() and only running callbacks for components directly attached to + * the controller. + * + * @return void + */ + function testComponentStartup() { + $Controller =& new ComponentTestController(); + $Controller->components = array('Apple'); + $Controller->constructClasses(); + $Controller->Component->initialize($Controller); + $Controller->beforeFilter(); + $Controller->Component->startup($Controller); + + $this->assertTrue(is_a($Controller->Apple, 'AppleComponent')); + $this->assertEqual($Controller->Apple->testName, 'ComponentTest'); + + $expected = !(defined('APP_CONTROLLER_EXISTS') && APP_CONTROLLER_EXISTS); + $this->assertEqual(isset($Controller->foo), $expected); + $this->assertFalse(isset($Controller->bar)); + } +/** + * test a component being used more than once. + * + * @return void + */ + function testMultipleComponentInitialize() { + $Controller =& new ComponentTestController(); + $Controller->components = array('Orange', 'Banana'); + $Controller->constructClasses(); + $Controller->Component->initialize($Controller); + + $this->assertEqual($Controller->Banana->testField, 'OrangeField'); + $this->assertEqual($Controller->Orange->Banana->testField, 'OrangeField'); + } +/** + * Test Component declarations with Parameters + * tests merging of component parameters and merging / construction of components. + * + * @return void + */ + function testComponentsWithParams() { + if ($this->skipIf(defined('APP_CONTROLLER_EXISTS'), '%s Need a non-existent AppController')) { + return; + } + + $Controller =& new ComponentTestController(); + $Controller->components = array('ParamTest' => array('test' => 'value', 'flag'), 'Apple'); + + $Controller->constructClasses(); + $Controller->Component->initialize($Controller); + + $this->assertTrue(is_a($Controller->ParamTest, 'ParamTestComponent')); + $this->assertTrue(is_a($Controller->ParamTest->Banana, 'BananaComponent')); + $this->assertTrue(is_a($Controller->Orange, 'OrangeComponent')); + $this->assertTrue(is_a($Controller->Session, 'SessionComponent')); + $this->assertEqual($Controller->Orange->settings, array('colour' => 'blood orange')); + $this->assertEqual($Controller->ParamTest->test, 'value'); + $this->assertEqual($Controller->ParamTest->flag, true); + + //Settings are merged from app controller and current controller. + $Controller =& new ComponentTestController(); + $Controller->components = array( + 'ParamTest' => array('test' => 'value'), + 'Orange' => array('ripeness' => 'perfect') + ); + $Controller->constructClasses(); + $Controller->Component->initialize($Controller); + + $expected = array('colour' => 'blood orange', 'ripeness' => 'perfect'); + $this->assertEqual($Controller->Orange->settings, $expected); + $this->assertEqual($Controller->ParamTest->test, 'value'); + } + +/** + * Ensure that settings are not duplicated when passed into component initialize. + * + * @return void + **/ + function testComponentParamsNoDuplication() { + $Controller =& new ComponentTestController(); + $Controller->components = array('Orange' => array('setting' => array('itemx'))); + + $Controller->constructClasses(); + $Controller->Component->initialize($Controller); + $expected = array('setting' => array('itemx'), 'colour' => 'blood orange'); + $this->assertEqual($Controller->Orange->settings, $expected, 'Params duplication has occured %s'); + } +/** + * Test mutually referencing components. + * + * @return void + */ + function testMutuallyReferencingComponents() { + $Controller =& new ComponentTestController(); + $Controller->components = array('MutuallyReferencingOne'); + $Controller->constructClasses(); + $Controller->Component->initialize($Controller); + + $this->assertTrue(is_a( + $Controller->MutuallyReferencingOne, + 'MutuallyReferencingOneComponent' + )); + $this->assertTrue(is_a( + $Controller->MutuallyReferencingOne->MutuallyReferencingTwo, + 'MutuallyReferencingTwoComponent' + )); + $this->assertTrue(is_a( + $Controller->MutuallyReferencingOne->MutuallyReferencingTwo->MutuallyReferencingOne, + 'MutuallyReferencingOneComponent' + )); + } +/** + * Test mutually referencing components. + * + * @return void + */ + function testSomethingReferencingEmailComponent() { + $Controller =& new ComponentTestController(); + $Controller->components = array('SomethingWithEmail'); + $Controller->constructClasses(); + $Controller->Component->initialize($Controller); + $Controller->beforeFilter(); + $Controller->Component->startup($Controller); + + $this->assertTrue(is_a( + $Controller->SomethingWithEmail, + 'SomethingWithEmailComponent' + )); + $this->assertTrue(is_a( + $Controller->SomethingWithEmail->Email, + 'EmailComponent' + )); + $this->assertTrue(is_a( + $Controller->SomethingWithEmail->Email->Controller, + 'ComponentTestController' + )); + } +/** + * Test that SessionComponent doesn't get added if its already in the components array. + * + * @return void + * @access public + */ + function testDoubleLoadingOfSessionComponent() { + $this->skipIf(defined('APP_CONTROLLER_EXISTS'), '%s Need a non-existent AppController'); + + $Controller =& new ComponentTestController(); + $Controller->uses = array(); + $Controller->components = array('Session'); + $Controller->constructClasses(); + + $this->assertEqual($Controller->components, array('Session' => '', 'Orange' => array('colour' => 'blood orange'))); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/controller/components/acl.test.php b/cake/tests/cases/libs/controller/components/acl.test.php new file mode 100755 index 0000000..755d7ae --- /dev/null +++ b/cake/tests/cases/libs/controller/components/acl.test.php @@ -0,0 +1,614 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * AclComponentTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs.controller.components + * @since CakePHP(tm) v 1.2.0.5435 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) { + define('CAKEPHP_UNIT_TEST_EXECUTION', 1); +} +App::import(array('controller' .DS . 'components' . DS . 'acl', 'model' . DS . 'db_acl')); +/** + * AclNodeTwoTestBase class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller.components + */ +class AclNodeTwoTestBase extends AclNode { +/** + * useDbConfig property + * + * @var string 'test_suite' + * @access public + */ + var $useDbConfig = 'test_suite'; +/** + * cacheSources property + * + * @var bool false + * @access public + */ + var $cacheSources = false; +} +/** + * AroTwoTest class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller.components + */ +class AroTwoTest extends AclNodeTwoTestBase { +/** + * name property + * + * @var string 'AroTwoTest' + * @access public + */ + var $name = 'AroTwoTest'; +/** + * useTable property + * + * @var string 'aro_twos' + * @access public + */ + var $useTable = 'aro_twos'; +/** + * hasAndBelongsToMany property + * + * @var array + * @access public + */ + var $hasAndBelongsToMany = array('AcoTwoTest' => array('with' => 'PermissionTwoTest')); +} +/** + * AcoTwoTest class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller.components + */ +class AcoTwoTest extends AclNodeTwoTestBase { +/** + * name property + * + * @var string 'AcoTwoTest' + * @access public + */ + var $name = 'AcoTwoTest'; +/** + * useTable property + * + * @var string 'aco_twos' + * @access public + */ + var $useTable = 'aco_twos'; +/** + * hasAndBelongsToMany property + * + * @var array + * @access public + */ + var $hasAndBelongsToMany = array('AroTwoTest' => array('with' => 'PermissionTwoTest')); +} +/** + * PermissionTwoTest class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller.components + */ +class PermissionTwoTest extends CakeTestModel { +/** + * name property + * + * @var string 'PermissionTwoTest' + * @access public + */ + var $name = 'PermissionTwoTest'; +/** + * useTable property + * + * @var string 'aros_aco_twos' + * @access public + */ + var $useTable = 'aros_aco_twos'; +/** + * cacheQueries property + * + * @var bool false + * @access public + */ + var $cacheQueries = false; +/** + * belongsTo property + * + * @var array + * @access public + */ + var $belongsTo = array('AroTwoTest' => array('foreignKey' => 'aro_id'), 'AcoTwoTest' => array('foreignKey' => 'aco_id')); +/** + * actsAs property + * + * @var mixed null + * @access public + */ + var $actsAs = null; +} +/** + * DbAclTwoTest class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller.components + */ +class DbAclTwoTest extends DbAcl { +/** + * construct method + * + * @access private + * @return void + */ + function __construct() { + $this->Aro =& new AroTwoTest(); + $this->Aro->Permission =& new PermissionTwoTest(); + $this->Aco =& new AcoTwoTest(); + $this->Aro->Permission =& new PermissionTwoTest(); + } +} +/** + * IniAclTest class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller.components + */ +class IniAclTest extends IniAcl { +} +/** + * Short description for class. + * + * @package cake + * @subpackage cake.tests.cases.libs.controller.components + */ +class AclComponentTest extends CakeTestCase { +/** + * fixtures property + * + * @var array + * @access public + */ + var $fixtures = array('core.aro_two', 'core.aco_two', 'core.aros_aco_two'); +/** + * startTest method + * + * @access public + * @return void + */ + function startTest() { + $this->Acl =& new AclComponent(); + } +/** + * before method + * + * @param mixed $method + * @access public + * @return void + */ + function before($method) { + Configure::write('Acl.classname', 'DbAclTwoTest'); + Configure::write('Acl.database', 'test_suite'); + parent::before($method); + } +/** + * tearDown method + * + * @access public + * @return void + */ + function tearDown() { + unset($this->Acl); + } +/** + * testAclCreate method + * + * @access public + * @return void + */ + function testAclCreate() { + $this->Acl->Aro->create(array('alias' => 'Chotchkey')); + $this->assertTrue($this->Acl->Aro->save()); + + $parent = $this->Acl->Aro->id; + + $this->Acl->Aro->create(array('parent_id' => $parent, 'alias' => 'Joanna')); + $this->assertTrue($this->Acl->Aro->save()); + + $this->Acl->Aro->create(array('parent_id' => $parent, 'alias' => 'Stapler')); + $this->assertTrue($this->Acl->Aro->save()); + + $root = $this->Acl->Aco->node('ROOT'); + $parent = $root[0]['AcoTwoTest']['id']; + + $this->Acl->Aco->create(array('parent_id' => $parent, 'alias' => 'Drinks')); + $this->assertTrue($this->Acl->Aco->save()); + + $this->Acl->Aco->create(array('parent_id' => $parent, 'alias' => 'PiecesOfFlair')); + $this->assertTrue($this->Acl->Aco->save()); + } +/** + * testAclCreateWithParent method + * + * @access public + * @return void + */ + function testAclCreateWithParent() { + $parent = $this->Acl->Aro->findByAlias('Peter', null, null, -1); + $this->Acl->Aro->create(); + $this->Acl->Aro->save(array( + 'alias' => 'Subordinate', + 'model' => 'User', + 'foreign_key' => 7, + 'parent_id' => $parent['AroTwoTest']['id'] + )); + $result = $this->Acl->Aro->findByAlias('Subordinate', null, null, -1); + $this->assertEqual($result['AroTwoTest']['lft'], 16); + $this->assertEqual($result['AroTwoTest']['rght'], 17); + } +/** + * testDbAclAllow method + * + * @access public + * @return void + */ + function testDbAclAllow() { + $this->assertFalse($this->Acl->check('Micheal', 'tpsReports', 'read')); + $this->assertTrue($this->Acl->allow('Micheal', 'tpsReports', array('read', 'delete', 'update'))); + $this->assertTrue($this->Acl->check('Micheal', 'tpsReports', 'update')); + $this->assertTrue($this->Acl->check('Micheal', 'tpsReports', 'read')); + $this->assertTrue($this->Acl->check('Micheal', 'tpsReports', 'delete')); + + $this->assertFalse($this->Acl->check('Micheal', 'tpsReports', 'create')); + $this->assertTrue($this->Acl->allow('Micheal', 'ROOT/tpsReports', 'create')); + $this->assertTrue($this->Acl->check('Micheal', 'tpsReports', 'create')); + $this->assertTrue($this->Acl->check('Micheal', 'tpsReports', 'delete')); + $this->assertTrue($this->Acl->allow('Micheal', 'printers', 'create')); + // Michael no longer has his delete permission for tpsReports! + $this->assertTrue($this->Acl->check('Micheal', 'tpsReports', 'delete')); + $this->assertTrue($this->Acl->check('Micheal', 'printers', 'create')); + + $this->assertFalse($this->Acl->check('root/users/Samir', 'ROOT/tpsReports/view')); + $this->assertTrue($this->Acl->allow('root/users/Samir', 'ROOT/tpsReports/view', '*')); + $this->assertTrue($this->Acl->check('Samir', 'view', 'read')); + $this->assertTrue($this->Acl->check('root/users/Samir', 'ROOT/tpsReports/view', 'update')); + + $this->assertFalse($this->Acl->check('root/users/Samir', 'ROOT/tpsReports/update','*')); + $this->assertTrue($this->Acl->allow('root/users/Samir', 'ROOT/tpsReports/update', '*')); + $this->assertTrue($this->Acl->check('Samir', 'update', 'read')); + $this->assertTrue($this->Acl->check('root/users/Samir', 'ROOT/tpsReports/update', 'update')); + // Samir should still have his tpsReports/view permissions, but does not + $this->assertTrue($this->Acl->check('root/users/Samir', 'ROOT/tpsReports/view', 'update')); + + $this->expectError('DbAcl::allow() - Invalid node'); + $this->assertFalse($this->Acl->allow('Lumbergh', 'ROOT/tpsReports/DoesNotExist', 'create')); + + $this->expectError('DbAcl::allow() - Invalid node'); + $this->assertFalse($this->Acl->allow('Homer', 'tpsReports', 'create')); + } +/** + * testDbAclCheck method + * + * @access public + * @return void + */ + function testDbAclCheck() { + $this->assertTrue($this->Acl->check('Samir', 'print', 'read')); + $this->assertTrue($this->Acl->check('Lumbergh', 'current', 'read')); + $this->assertFalse($this->Acl->check('Milton', 'smash', 'read')); + $this->assertFalse($this->Acl->check('Milton', 'current', 'update')); + + $this->expectError("DbAcl::check() - Failed ARO/ACO node lookup in permissions check. Node references:\nAro: WRONG\nAco: tpsReports"); + $this->assertFalse($this->Acl->check('WRONG', 'tpsReports', 'read')); + + $this->expectError("ACO permissions key foobar does not exist in DbAcl::check()"); + $this->assertFalse($this->Acl->check('Lumbergh', 'smash', 'foobar')); + + $this->expectError("DbAcl::check() - Failed ARO/ACO node lookup in permissions check. Node references:\nAro: users\nAco: NonExistant"); + $this->assertFalse($this->Acl->check('users', 'NonExistant', 'read')); + + $this->assertFalse($this->Acl->check(null, 'printers', 'create')); + $this->assertFalse($this->Acl->check('managers', null, 'read')); + + $this->assertTrue($this->Acl->check('Bobs', 'ROOT/tpsReports/view/current', 'read')); + $this->assertFalse($this->Acl->check('Samir', 'ROOT/tpsReports/update', 'read')); + + $this->assertFalse($this->Acl->check('root/users/Milton', 'smash', 'delete')); + } +/** + * testDbAclCascadingDeny function + * + * Setup the acl permissions such that Bobs inherits from admin. + * deny Admin delete access to a specific resource, check the permisssions are inherited. + * + * @access public + * @return void + */ + function testDbAclCascadingDeny() { + $this->Acl->inherit('Bobs', 'ROOT', '*'); + $this->assertTrue($this->Acl->check('admin', 'tpsReports', 'delete')); + $this->assertTrue($this->Acl->check('Bobs', 'tpsReports', 'delete')); + $this->Acl->deny('admin', 'tpsReports', 'delete'); + $this->assertFalse($this->Acl->check('admin', 'tpsReports', 'delete')); + $this->assertFalse($this->Acl->check('Bobs', 'tpsReports', 'delete')); + } +/** + * testDbAclDeny method + * + * @access public + * @return void + */ + function testDbAclDeny() { + $this->assertTrue($this->Acl->check('Micheal', 'smash', 'delete')); + $this->Acl->deny('Micheal', 'smash', 'delete'); + $this->assertFalse($this->Acl->check('Micheal', 'smash', 'delete')); + $this->assertTrue($this->Acl->check('Micheal', 'smash', 'read')); + $this->assertTrue($this->Acl->check('Micheal', 'smash', 'create')); + $this->assertTrue($this->Acl->check('Micheal', 'smash', 'update')); + $this->assertFalse($this->Acl->check('Micheal', 'smash', '*')); + + $this->assertTrue($this->Acl->check('Samir', 'refill', '*')); + $this->Acl->deny('Samir', 'refill', '*'); + $this->assertFalse($this->Acl->check('Samir', 'refill', 'create')); + $this->assertFalse($this->Acl->check('Samir', 'refill', 'update')); + $this->assertFalse($this->Acl->check('Samir', 'refill', 'read')); + $this->assertFalse($this->Acl->check('Samir', 'refill', 'delete')); + + $result = $this->Acl->Aro->Permission->find('all', array('conditions' => array('AroTwoTest.alias' => 'Samir'))); + $expected = '-1'; + $this->assertEqual($result[0]['PermissionTwoTest']['_delete'], $expected); + + $this->expectError('DbAcl::allow() - Invalid node'); + $this->assertFalse($this->Acl->deny('Lumbergh', 'ROOT/tpsReports/DoesNotExist', 'create')); + } +/** + * testAclNodeLookup method + * + * @access public + * @return void + */ + function testAclNodeLookup() { + $result = $this->Acl->Aro->node('root/users/Samir'); + $expected = array( + array('AroTwoTest' => array('id' => '7', 'parent_id' => '4', 'model' => 'User', 'foreign_key' => 3, 'alias' => 'Samir')), + array('AroTwoTest' => array('id' => '4', 'parent_id' => '1', 'model' => 'Group', 'foreign_key' => 3, 'alias' => 'users')), + array('AroTwoTest' => array('id' => '1', 'parent_id' => null, 'model' => null, 'foreign_key' => null, 'alias' => 'root')) + ); + $this->assertEqual($result, $expected); + + $result = $this->Acl->Aco->node('ROOT/tpsReports/view/current'); + $expected = array( + array('AcoTwoTest' => array('id' => '4', 'parent_id' => '3', 'model' => null, 'foreign_key' => null, 'alias' => 'current')), + array('AcoTwoTest' => array('id' => '3', 'parent_id' => '2', 'model' => null, 'foreign_key' => null, 'alias' => 'view')), + array('AcoTwoTest' => array('id' => '2', 'parent_id' => '1', 'model' => null, 'foreign_key' => null, 'alias' => 'tpsReports')), + array('AcoTwoTest' => array('id' => '1', 'parent_id' => null, 'model' => null, 'foreign_key' => null, 'alias' => 'ROOT')), + ); + $this->assertEqual($result, $expected); + } +/** + * testDbInherit method + * + * @access public + * @return void + */ + function testDbInherit() { + //parent doesn't have access inherit should still deny + $this->assertFalse($this->Acl->check('Milton', 'smash', 'delete')); + $this->Acl->inherit('Milton', 'smash', 'delete'); + $this->assertFalse($this->Acl->check('Milton', 'smash', 'delete')); + + //inherit parent + $this->assertFalse($this->Acl->check('Milton', 'smash', 'read')); + $this->Acl->inherit('Milton', 'smash', 'read'); + $this->assertTrue($this->Acl->check('Milton', 'smash', 'read')); + } +/** + * testDbGrant method + * + * @access public + * @return void + */ + function testDbGrant() { + $this->assertFalse($this->Acl->check('Samir', 'tpsReports', 'create')); + $this->Acl->grant('Samir', 'tpsReports', 'create'); + $this->assertTrue($this->Acl->check('Samir', 'tpsReports', 'create')); + + $this->assertFalse($this->Acl->check('Micheal', 'view', 'read')); + $this->Acl->grant('Micheal', 'view', array('read', 'create', 'update')); + $this->assertTrue($this->Acl->check('Micheal', 'view', 'read')); + $this->assertTrue($this->Acl->check('Micheal', 'view', 'create')); + $this->assertTrue($this->Acl->check('Micheal', 'view', 'update')); + $this->assertFalse($this->Acl->check('Micheal', 'view', 'delete')); + + $this->expectError('DbAcl::allow() - Invalid node'); + $this->assertFalse($this->Acl->grant('Peter', 'ROOT/tpsReports/DoesNotExist', 'create')); + } +/** + * testDbRevoke method + * + * @access public + * @return void + */ + function testDbRevoke() { + $this->assertTrue($this->Acl->check('Bobs', 'tpsReports', 'read')); + $this->Acl->revoke('Bobs', 'tpsReports', 'read'); + $this->assertFalse($this->Acl->check('Bobs', 'tpsReports', 'read')); + + $this->assertTrue($this->Acl->check('users', 'printers', 'read')); + $this->Acl->revoke('users', 'printers', 'read'); + $this->assertFalse($this->Acl->check('users', 'printers', 'read')); + $this->assertFalse($this->Acl->check('Samir', 'printers', 'read')); + $this->assertFalse($this->Acl->check('Peter', 'printers', 'read')); + + $this->expectError('DbAcl::allow() - Invalid node'); + $this->assertFalse($this->Acl->deny('Bobs', 'ROOT/printers/DoesNotExist', 'create')); + } +/** + * testStartup method + * + * @access public + * @return void + */ + function testStartup() { + $controller = new Controller(); + $this->assertTrue($this->Acl->startup($controller)); + } +/** + * testIniReadConfigFile + * + * @access public + * @return void + */ + function testIniReadConfigFile() { + Configure::write('Acl.classname', 'IniAclTest'); + unset($this->Acl); + $this->Acl = new AclComponent(); + $iniFile = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'config'. DS . 'acl.ini.php'; + $result = $this->Acl->_Instance->readConfigFile($iniFile); + $expected = array( + 'admin' => array( + 'groups' => 'administrators', + 'allow' => '', + 'deny' => 'ads', + ), + 'paul' => array( + 'groups' => 'users', + 'allow' =>'', + 'deny' => '', + ), + 'jenny' => array( + 'groups' => 'users', + 'allow' => 'ads', + 'deny' => 'images, files', + ), + 'nobody' => array( + 'groups' => 'anonymous', + 'allow' => '', + 'deny' => '', + ), + 'administrators' => array( + 'deny' => '', + 'allow' => 'posts, comments, images, files, stats, ads', + ), + 'users' => array( + 'allow' => 'posts, comments, images, files', + 'deny' => 'stats, ads', + ), + 'anonymous' => array( + 'allow' => '', + 'deny' => 'posts, comments, images, files, stats, ads', + ), + ); + $this->assertEqual($result, $expected); + } +/** + * testIniCheck method + * + * @access public + * @return void + */ + function testIniCheck() { + Configure::write('Acl.classname', 'IniAclTest'); + unset($this->Acl); + $iniFile = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'config'. DS . 'acl.ini.php'; + + $this->Acl = new AclComponent(); + $this->Acl->_Instance->config= $this->Acl->_Instance->readConfigFile($iniFile); + + $this->assertFalse($this->Acl->check('admin', 'ads')); + $this->assertTrue($this->Acl->check('admin', 'posts')); + + $this->assertTrue($this->Acl->check('jenny', 'posts')); + $this->assertTrue($this->Acl->check('jenny', 'ads')); + + $this->assertTrue($this->Acl->check('paul', 'posts')); + $this->assertFalse($this->Acl->check('paul', 'ads')); + + $this->assertFalse($this->Acl->check('nobody', 'comments')); + } +/** + * debug function - to help editing/creating test cases for the ACL component + * + * To check the overal ACL status at any time call $this->__debug(); + * Generates a list of the current aro and aco structures and a grid dump of the permissions that are defined + * Only designed to work with the db based ACL + * + * @param bool $treesToo + * @access private + * @return void + */ + function __debug ($printTreesToo = false) { + $this->Acl->Aro->displayField = 'alias'; + $this->Acl->Aco->displayField = 'alias'; + $aros = $this->Acl->Aro->find('list', array('order' => 'lft')); + $acos = $this->Acl->Aco->find('list', array('order' => 'lft')); + $rights = array('*', 'create', 'read', 'update', 'delete'); + $permissions['Aros v Acos >'] = $acos; + foreach ($aros as $aro) { + $row = array(); + foreach ($acos as $aco) { + $perms = ''; + foreach ($rights as $right) { + if ($this->Acl->check($aro, $aco, $right)) { + if ($right == '*') { + $perms .= '****'; + break; + } + $perms .= $right[0]; + } elseif ($right != '*') { + $perms .= ' '; + } + } + $row[] = $perms; + } + $permissions[$aro] = $row; + } + foreach ($permissions as $key => $values) { + array_unshift($values, $key); + $values = array_map(array(&$this, '__pad'), $values); + $permissions[$key] = implode (' ', $values); + } + $permisssions = array_map(array(&$this, '__pad'), $permissions); + array_unshift($permissions, 'Current Permissions :'); + if ($printTreesToo) { + debug (array('aros' => $this->Acl->Aro->generateTreeList(), 'acos' => $this->Acl->Aco->generateTreeList())); + } + debug (implode("\r\n", $permissions)); + } +/** + * pad function + * Used by debug to format strings used in the data dump + * + * @param string $string + * @param int $len + * @access private + * @return void + */ + function __pad($string = '', $len = 14) { + return str_pad($string, $len); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/controller/components/auth.test.php b/cake/tests/cases/libs/controller/components/auth.test.php new file mode 100755 index 0000000..481b2dd --- /dev/null +++ b/cake/tests/cases/libs/controller/components/auth.test.php @@ -0,0 +1,1266 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * AutComponentTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.cake.tests.cases.libs.controller.components + * @since CakePHP(tm) v 1.2.0.5347 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +App::import(array('controller' . DS . 'components' . DS .'auth', 'controller' . DS . 'components' . DS .'acl')); +App::import(array('controller' . DS . 'components' . DS . 'acl', 'model' . DS . 'db_acl')); +App::import('Core', 'Xml'); +/** +* TestAuthComponent class +* +* @package cake +* @subpackage cake.tests.cases.libs.controller.components +*/ +class TestAuthComponent extends AuthComponent { +/** + * testStop property + * + * @var bool false + * @access public + */ + var $testStop = false; +/** + * Sets default login state + * + * @var bool true + * @access protected + */ + var $_loggedIn = true; +/** + * stop method + * + * @access public + * @return void + */ + function _stop() { + $this->testStop = true; + } +} +/** +* AuthUser class +* +* @package cake +* @subpackage cake.tests.cases.libs.controller.components +*/ +class AuthUser extends CakeTestModel { +/** + * name property + * + * @var string 'AuthUser' + * @access public + */ + var $name = 'AuthUser'; +/** + * useDbConfig property + * + * @var string 'test_suite' + * @access public + */ + var $useDbConfig = 'test_suite'; +/** + * parentNode method + * + * @access public + * @return void + */ + function parentNode() { + return true; + } +/** + * bindNode method + * + * @param mixed $object + * @access public + * @return void + */ + function bindNode($object) { + return 'Roles/Admin'; + } +/** + * isAuthorized method + * + * @param mixed $user + * @param mixed $controller + * @param mixed $action + * @access public + * @return void + */ + function isAuthorized($user, $controller = null, $action = null) { + if (!empty($user)) { + return true; + } + return false; + } +} +/** + * AuthUserCustomField class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller.components + */ +class AuthUserCustomField extends AuthUser { +/** + * name property + * + * @var string 'AuthUser' + * @access public + */ + var $name = 'AuthUserCustomField'; +} +/** +* UuidUser class +* +* @package cake +* @subpackage cake.tests.cases.libs.controller.components +*/ +class UuidUser extends CakeTestModel { +/** + * name property + * + * @var string 'AuthUser' + * @access public + */ + var $name = 'UuidUser'; +/** + * useDbConfig property + * + * @var string 'test_suite' + * @access public + */ + var $useDbConfig = 'test_suite'; +/** + * useTable property + * + * @var string 'uuid' + * @access public + */ + var $useTable = 'uuids'; +/** + * parentNode method + * + * @access public + * @return void + */ + function parentNode() { + return true; + } +/** + * bindNode method + * + * @param mixed $object + * @access public + * @return void + */ + function bindNode($object) { + return 'Roles/Admin'; + } +/** + * isAuthorized method + * + * @param mixed $user + * @param mixed $controller + * @param mixed $action + * @access public + * @return void + */ + function isAuthorized($user, $controller = null, $action = null) { + if (!empty($user)) { + return true; + } + return false; + } +} +/** +* AuthTestController class +* +* @package cake +* @subpackage cake.tests.cases.libs.controller.components +*/ +class AuthTestController extends Controller { +/** + * name property + * + * @var string 'AuthTest' + * @access public + */ + var $name = 'AuthTest'; +/** + * uses property + * + * @var array + * @access public + */ + var $uses = array('AuthUser'); +/** + * components property + * + * @var array + * @access public + */ + var $components = array('Auth', 'Acl'); +/** + * testUrl property + * + * @var mixed null + * @access public + */ + var $testUrl = null; +/** + * construct method + * + * @access private + * @return void + */ + function __construct() { + $this->params = Router::parse('/auth_test'); + Router::setRequestInfo(array($this->params, array('base' => null, 'here' => '/auth_test', 'webroot' => '/', 'passedArgs' => array(), 'argSeparator' => ':', 'namedArgs' => array()))); + parent::__construct(); + } +/** + * beforeFilter method + * + * @access public + * @return void + */ + function beforeFilter() { + } +/** + * login method + * + * @access public + * @return void + */ + function login() { + } +/** + * admin_login method + * + * @access public + * @return void + */ + function admin_login() { + } +/** + * logout method + * + * @access public + * @return void + */ + function logout() { + // $this->redirect($this->Auth->logout()); + } +/** + * add method + * + * @access public + * @return void + */ + function add() { + echo "add"; + } +/** + * add method + * + * @access public + * @return void + */ + function camelCase() { + echo "camelCase"; + } +/** + * redirect method + * + * @param mixed $url + * @param mixed $status + * @param mixed $exit + * @access public + * @return void + */ + function redirect($url, $status = null, $exit = true) { + $this->testUrl = Router::url($url); + return false; + } +/** + * isAuthorized method + * + * @access public + * @return void + */ + function isAuthorized() { + if (isset($this->params['testControllerAuth'])) { + return false; + } + return true; + } +/** + * Mock delete method + * + * @param mixed $url + * @param mixed $status + * @param mixed $exit + * @access public + * @return void + */ + function delete($id = null) { + if ($this->TestAuth->testStop !== true && $id !== null) { + echo 'Deleted Record: ' . var_export($id, true); + } + } +} +/** + * AjaxAuthController class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller.components + */ +class AjaxAuthController extends Controller { +/** + * name property + * + * @var string 'AjaxAuth' + * @access public + */ + var $name = 'AjaxAuth'; +/** + * components property + * + * @var array + * @access public + */ + var $components = array('TestAuth'); +/** + * uses property + * + * @var array + * @access public + */ + var $uses = array(); +/** + * testUrl property + * + * @var mixed null + * @access public + */ + var $testUrl = null; +/** + * beforeFilter method + * + * @access public + * @return void + */ + function beforeFilter() { + $this->TestAuth->ajaxLogin = 'test_element'; + $this->TestAuth->userModel = 'AuthUser'; + $this->TestAuth->RequestHandler->ajaxLayout = 'ajax2'; + } +/** + * add method + * + * @access public + * @return void + */ + function add() { + if ($this->TestAuth->testStop !== true) { + echo 'Added Record'; + } + } +/** + * redirect method + * + * @param mixed $url + * @param mixed $status + * @param mixed $exit + * @access public + * @return void + */ + function redirect($url, $status = null, $exit = true) { + $this->testUrl = Router::url($url); + return false; + } +} +/** +* AuthTest class +* +* @package cake +* @subpackage cake.tests.cases.libs.controller.components +*/ +class AuthTest extends CakeTestCase { +/** + * name property + * + * @var string 'Auth' + * @access public + */ + var $name = 'Auth'; +/** + * fixtures property + * + * @var array + * @access public + */ + var $fixtures = array('core.uuid', 'core.auth_user', 'core.auth_user_custom_field', 'core.aro', 'core.aco', 'core.aros_aco', 'core.aco_action'); +/** + * initialized property + * + * @var bool false + * @access public + */ + var $initialized = false; +/** + * startTest method + * + * @access public + * @return void + */ + function startTest() { + $this->_server = $_SERVER; + $this->_env = $_ENV; + + $this->_securitySalt = Configure::read('Security.salt'); + Configure::write('Security.salt', 'JfIxfs2guVoUubWDYhG93b0qyJfIxfs2guwvniR2G0FgaC9mi'); + + $this->_acl = Configure::read('Acl'); + Configure::write('Acl.database', 'test_suite'); + Configure::write('Acl.classname', 'DbAcl'); + + $this->Controller =& new AuthTestController(); + $this->Controller->Component->init($this->Controller); + + ClassRegistry::addObject('view', new View($this->Controller)); + + $this->Controller->Session->del('Auth'); + $this->Controller->Session->del('Message.auth'); + + Router::reload(); + + $this->initialized = true; + } +/** + * endTest method + * + * @access public + * @return void + */ + function endTest() { + $_SERVER = $this->_server; + $_ENV = $this->_env; + Configure::write('Acl', $this->_acl); + Configure::write('Security.salt', $this->_securitySalt); + $this->Controller->Session->del('Auth'); + $this->Controller->Session->del('Message.auth'); + ClassRegistry::flush(); + unset($this->Controller, $this->AuthUser); + } +/** + * testNoAuth method + * + * @access public + * @return void + */ + function testNoAuth() { + $this->assertFalse($this->Controller->Auth->isAuthorized()); + } +/** + * testIsErrorOrTests + * + * @access public + * @return void + */ + function testIsErrorOrTests() { + $this->Controller->Auth->initialize($this->Controller); + + $this->Controller->name = 'CakeError'; + $this->assertTrue($this->Controller->Auth->startup($this->Controller)); + + $this->Controller->name = 'Post'; + $this->Controller->params['action'] = 'thisdoesnotexist'; + $this->assertTrue($this->Controller->Auth->startup($this->Controller)); + + $this->Controller->scaffold = null; + $this->Controller->params['action'] = 'index'; + $this->assertFalse($this->Controller->Auth->startup($this->Controller)); + } +/** + * testLogin method + * + * @access public + * @return void + */ + function testLogin() { + $this->AuthUser =& new AuthUser(); + $user['id'] = 1; + $user['username'] = 'mariano'; + $user['password'] = Security::hash(Configure::read('Security.salt') . 'cake'); + $this->AuthUser->save($user, false); + + $authUser = $this->AuthUser->find(); + + $this->Controller->data['AuthUser']['username'] = $authUser['AuthUser']['username']; + $this->Controller->data['AuthUser']['password'] = 'cake'; + + $this->Controller->params = Router::parse('auth_test/login'); + $this->Controller->params['url']['url'] = 'auth_test/login'; + + $this->Controller->Auth->initialize($this->Controller); + + $this->Controller->Auth->loginAction = 'auth_test/login'; + $this->Controller->Auth->userModel = 'AuthUser'; + + $this->Controller->Auth->startup($this->Controller); + $user = $this->Controller->Auth->user(); + $expected = array('AuthUser' => array( + 'id' => 1, 'username' => 'mariano', 'created' => '2007-03-17 01:16:23', 'updated' => date('Y-m-d H:i:s') + )); + $this->assertEqual($user, $expected); + $this->Controller->Session->del('Auth'); + + $this->Controller->data['AuthUser']['username'] = 'blah'; + $this->Controller->data['AuthUser']['password'] = ''; + + $this->Controller->Auth->startup($this->Controller); + + $user = $this->Controller->Auth->user(); + $this->assertFalse($user); + $this->Controller->Session->del('Auth'); + + $this->Controller->data['AuthUser']['username'] = 'now() or 1=1 --'; + $this->Controller->data['AuthUser']['password'] = ''; + + $this->Controller->Auth->startup($this->Controller); + + $user = $this->Controller->Auth->user(); + $this->assertFalse($user); + $this->Controller->Session->del('Auth'); + + $this->Controller->data['AuthUser']['username'] = 'now() or 1=1 # something'; + $this->Controller->data['AuthUser']['password'] = ''; + + $this->Controller->Auth->startup($this->Controller); + + $user = $this->Controller->Auth->user(); + $this->assertFalse($user); + $this->Controller->Session->del('Auth'); + + $this->Controller->Auth->userModel = 'UuidUser'; + $this->Controller->Auth->login('47c36f9c-bc00-4d17-9626-4e183ca6822b'); + + $user = $this->Controller->Auth->user(); + $expected = array('UuidUser' => array( + 'id' => '47c36f9c-bc00-4d17-9626-4e183ca6822b', 'title' => 'Unique record 1', 'count' => 2, 'created' => '2008-03-13 01:16:23', 'updated' => '2008-03-13 01:18:31' + )); + $this->assertEqual($user, $expected); + $this->Controller->Session->del('Auth'); + } +/** + * testAuthorizeFalse method + * + * @access public + * @return void + */ + function testAuthorizeFalse() { + $this->AuthUser =& new AuthUser(); + $user = $this->AuthUser->find(); + $this->Controller->Session->write('Auth', $user); + $this->Controller->Auth->userModel = 'AuthUser'; + $this->Controller->Auth->authorize = false; + $this->Controller->params = Router::parse('auth_test/add'); + $result = $this->Controller->Auth->startup($this->Controller); + $this->assertTrue($result); + + $this->Controller->Session->del('Auth'); + $result = $this->Controller->Auth->startup($this->Controller); + $this->assertFalse($result); + $this->assertTrue($this->Controller->Session->check('Message.auth')); + + $this->Controller->params = Router::parse('auth_test/camelCase'); + $result = $this->Controller->Auth->startup($this->Controller); + $this->assertFalse($result); + } +/** + * testAuthorizeController method + * + * @access public + * @return void + */ + function testAuthorizeController() { + $this->AuthUser =& new AuthUser(); + $user = $this->AuthUser->find(); + $this->Controller->Session->write('Auth', $user); + $this->Controller->Auth->userModel = 'AuthUser'; + $this->Controller->Auth->authorize = 'controller'; + $this->Controller->params = Router::parse('auth_test/add'); + $result = $this->Controller->Auth->startup($this->Controller); + $this->assertTrue($result); + + $this->Controller->params['testControllerAuth'] = 1; + $result = $this->Controller->Auth->startup($this->Controller); + $this->assertTrue($this->Controller->Session->check('Message.auth')); + $this->assertFalse($result); + + $this->Controller->Session->del('Auth'); + } +/** + * testAuthorizeModel method + * + * @access public + * @return void + */ + function testAuthorizeModel() { + $this->AuthUser =& new AuthUser(); + $user = $this->AuthUser->find(); + $this->Controller->Session->write('Auth', $user); + + $this->Controller->params['controller'] = 'auth_test'; + $this->Controller->params['action'] = 'add'; + $this->Controller->Auth->userModel = 'AuthUser'; + $this->Controller->Auth->initialize($this->Controller); + $this->Controller->Auth->authorize = array('model'=>'AuthUser'); + $result = $this->Controller->Auth->startup($this->Controller); + $this->assertTrue($result); + + $this->Controller->Session->del('Auth'); + $this->Controller->Auth->startup($this->Controller); + $this->assertTrue($this->Controller->Session->check('Message.auth')); + $result = $this->Controller->Auth->isAuthorized(); + $this->assertFalse($result); + } +/** + * testAuthorizeCrud method + * + * @access public + * @return void + */ + function testAuthorizeCrud() { + $this->AuthUser =& new AuthUser(); + $user = $this->AuthUser->find(); + $this->Controller->Session->write('Auth', $user); + + $this->Controller->params['controller'] = 'auth_test'; + $this->Controller->params['action'] = 'add'; + + $this->Controller->Acl->name = 'DbAclTest'; + + $this->Controller->Acl->Aro->id = null; + $this->Controller->Acl->Aro->create(array('alias' => 'Roles')); + $result = $this->Controller->Acl->Aro->save(); + $this->assertTrue($result); + + $parent = $this->Controller->Acl->Aro->id; + + $this->Controller->Acl->Aro->create(array('parent_id' => $parent, 'alias' => 'Admin')); + $result = $this->Controller->Acl->Aro->save(); + $this->assertTrue($result); + + $parent = $this->Controller->Acl->Aro->id; + + $this->Controller->Acl->Aro->create(array( + 'model' => 'AuthUser', 'parent_id' => $parent, 'foreign_key' => 1, 'alias'=> 'mariano' + )); + $result = $this->Controller->Acl->Aro->save(); + $this->assertTrue($result); + + $this->Controller->Acl->Aco->create(array('alias'=>'Root')); + $result = $this->Controller->Acl->Aco->save(); + $this->assertTrue($result); + + $parent = $this->Controller->Acl->Aco->id; + + $this->Controller->Acl->Aco->create(array('parent_id' => $parent, 'alias' => 'AuthTest')); + $result = $this->Controller->Acl->Aco->save(); + $this->assertTrue($result); + + $this->Controller->Acl->allow('Roles/Admin', 'Root'); + $this->Controller->Acl->allow('Roles/Admin', 'Root/AuthTest'); + + $this->Controller->Auth->initialize($this->Controller); + + $this->Controller->Auth->userModel = 'AuthUser'; + $this->Controller->Auth->authorize = 'crud'; + $this->Controller->Auth->actionPath = 'Root/'; + + $this->Controller->Auth->startup($this->Controller); + $this->assertTrue($this->Controller->Auth->isAuthorized()); + + $this->Controller->Session->del('Auth'); + $this->Controller->Auth->startup($this->Controller); + $this->assertTrue($this->Controller->Session->check('Message.auth')); + } +/** + * Tests that deny always takes precedence over allow + * + * @access public + * @return void + */ + function testAllowDenyAll() { + $this->Controller->Auth->initialize($this->Controller); + + $this->Controller->Auth->allow('*'); + $this->Controller->Auth->deny('add'); + + $this->Controller->params['action'] = 'delete'; + $this->assertTrue($this->Controller->Auth->startup($this->Controller)); + + $this->Controller->params['action'] = 'add'; + $this->assertFalse($this->Controller->Auth->startup($this->Controller)); + + $this->Controller->params['action'] = 'Add'; + $this->assertFalse($this->Controller->Auth->startup($this->Controller)); + } +/** + * test that allow() and allowedActions work with camelCase method names. + * + * @return void + **/ + function testAllowedActionsWithCamelCaseMethods() { + $url = '/auth_test/camelCase'; + $this->Controller->params = Router::parse($url); + $this->Controller->params['url']['url'] = Router::normalize($url); + $this->Controller->Auth->initialize($this->Controller); + $this->Controller->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login'); + $this->Controller->Auth->userModel = 'AuthUser'; + $this->Controller->Auth->allow('*'); + $result = $this->Controller->Auth->startup($this->Controller); + $this->assertTrue($result, 'startup() should return true, as action is allowed. %s'); + + $url = '/auth_test/camelCase'; + $this->Controller->params = Router::parse($url); + $this->Controller->params['url']['url'] = Router::normalize($url); + $this->Controller->Auth->initialize($this->Controller); + $this->Controller->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login'); + $this->Controller->Auth->userModel = 'AuthUser'; + $this->Controller->Auth->allowedActions = array('delete', 'camelCase', 'add'); + $result = $this->Controller->Auth->startup($this->Controller); + $this->assertTrue($result, 'startup() should return true, as action is allowed. %s'); + + $this->Controller->Auth->allowedActions = array('delete', 'add'); + $result = $this->Controller->Auth->startup($this->Controller); + $this->assertFalse($result, 'startup() should return false, as action is not allowed. %s'); + } +/** + * testLoginRedirect method + * + * @access public + * @return void + */ + function testLoginRedirect() { + if (isset($_SERVER['HTTP_REFERER'])) { + $backup = $_SERVER['HTTP_REFERER']; + } else { + $backup = null; + } + + $_SERVER['HTTP_REFERER'] = false; + + $this->Controller->Session->write('Auth', array( + 'AuthUser' => array('id' => '1', 'username' => 'nate') + )); + + $this->Controller->params = Router::parse('users/login'); + $this->Controller->params['url']['url'] = 'users/login'; + $this->Controller->Auth->initialize($this->Controller); + + $this->Controller->Auth->userModel = 'AuthUser'; + $this->Controller->Auth->loginRedirect = array( + 'controller' => 'pages', 'action' => 'display', 'welcome' + ); + $this->Controller->Auth->startup($this->Controller); + $expected = Router::normalize($this->Controller->Auth->loginRedirect); + $this->assertEqual($expected, $this->Controller->Auth->redirect()); + + $this->Controller->Session->del('Auth'); + + $this->Controller->params['url']['url'] = 'admin/'; + $this->Controller->Auth->initialize($this->Controller); + $this->Controller->Auth->userModel = 'AuthUser'; + $this->Controller->Auth->loginRedirect = null; + $this->Controller->Auth->startup($this->Controller); + $expected = Router::normalize('admin/'); + $this->assertTrue($this->Controller->Session->check('Message.auth')); + $this->assertEqual($expected, $this->Controller->Auth->redirect()); + + $this->Controller->Session->del('Auth'); + + //empty referer no session + $_SERVER['HTTP_REFERER'] = false; + $_ENV['HTTP_REFERER'] = false; + putenv('HTTP_REFERER='); + $url = '/posts/view/1'; + + $this->Controller->Session->write('Auth', array( + 'AuthUser' => array('id' => '1', 'username' => 'nate')) + ); + $this->Controller->testUrl = null; + $this->Controller->params = Router::parse($url); + array_push($this->Controller->methods, 'view', 'edit', 'index'); + + $this->Controller->Auth->initialize($this->Controller); + $this->Controller->Auth->authorize = 'controller'; + $this->Controller->params['testControllerAuth'] = true; + + $this->Controller->Auth->loginAction = array( + 'controller' => 'AuthTest', 'action' => 'login' + ); + $this->Controller->Auth->userModel = 'AuthUser'; + $this->Controller->Auth->startup($this->Controller); + $expected = Router::normalize('/'); + $this->assertEqual($expected, $this->Controller->testUrl); + + + $this->Controller->Session->del('Auth'); + $_SERVER['HTTP_REFERER'] = Router::url('/admin/', true); + + $this->Controller->Session->write('Auth', array( + 'AuthUser' => array('id'=>'1', 'username'=>'nate')) + ); + $this->Controller->params['url']['url'] = 'auth_test/login'; + $this->Controller->Auth->initialize($this->Controller); + $this->Controller->Auth->loginAction = 'auth_test/login'; + $this->Controller->Auth->userModel = 'AuthUser'; + $this->Controller->Auth->loginRedirect = false; + $this->Controller->Auth->startup($this->Controller); + $expected = Router::normalize('/admin'); + $this->assertEqual($expected, $this->Controller->Auth->redirect()); + + //Ticket #4750 + //named params + $this->Controller->Session->del('Auth'); + $url = '/posts/index/year:2008/month:feb'; + $this->Controller->params = Router::parse($url); + $this->Controller->params['url']['url'] = Router::normalize($url); + $this->Controller->Auth->initialize($this->Controller); + $this->Controller->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login'); + $this->Controller->Auth->userModel = 'AuthUser'; + $this->Controller->Auth->startup($this->Controller); + $expected = Router::normalize('posts/index/year:2008/month:feb'); + $this->assertEqual($expected, $this->Controller->Session->read('Auth.redirect')); + + //passed args + $this->Controller->Session->del('Auth'); + $url = '/posts/view/1'; + $this->Controller->params = Router::parse($url); + $this->Controller->params['url']['url'] = Router::normalize($url); + $this->Controller->Auth->initialize($this->Controller); + $this->Controller->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login'); + $this->Controller->Auth->userModel = 'AuthUser'; + $this->Controller->Auth->startup($this->Controller); + $expected = Router::normalize('posts/view/1'); + $this->assertEqual($expected, $this->Controller->Session->read('Auth.redirect')); + + // QueryString parameters + $_back = $_GET; + $_GET = array( + 'url' => '/posts/index/29', + 'print' => 'true', + 'refer' => 'menu' + ); + $this->Controller->Session->del('Auth'); + $url = '/posts/index/29?print=true&refer=menu'; + $this->Controller->params = Dispatcher::parseParams($url); + $this->Controller->Auth->initialize($this->Controller); + $this->Controller->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login'); + $this->Controller->Auth->userModel = 'AuthUser'; + $this->Controller->Auth->startup($this->Controller); + $expected = Router::normalize('posts/index/29?print=true&refer=menu'); + $this->assertEqual($expected, $this->Controller->Session->read('Auth.redirect')); + + $_GET = array( + 'url' => '/posts/index/29', + 'print' => 'true', + 'refer' => 'menu', + 'ext' => 'html' + ); + $this->Controller->Session->del('Auth'); + $url = '/posts/index/29?print=true&refer=menu'; + $this->Controller->params = Dispatcher::parseParams($url); + $this->Controller->Auth->initialize($this->Controller); + $this->Controller->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login'); + $this->Controller->Auth->userModel = 'AuthUser'; + $this->Controller->Auth->startup($this->Controller); + $expected = Router::normalize('posts/index/29?print=true&refer=menu'); + $this->assertEqual($expected, $this->Controller->Session->read('Auth.redirect')); + $_GET = $_back; + + //external authed action + $_SERVER['HTTP_REFERER'] = 'http://webmail.example.com/view/message'; + $this->Controller->Session->del('Auth'); + $url = '/posts/edit/1'; + $this->Controller->params = Router::parse($url); + $this->Controller->params['url']['url'] = Router::normalize($url); + $this->Controller->Auth->initialize($this->Controller); + $this->Controller->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login'); + $this->Controller->Auth->userModel = 'AuthUser'; + $this->Controller->Auth->startup($this->Controller); + $expected = Router::normalize('/posts/edit/1'); + $this->assertEqual($expected, $this->Controller->Session->read('Auth.redirect')); + + //external direct login link + $_SERVER['HTTP_REFERER'] = 'http://webmail.example.com/view/message'; + $this->Controller->Session->del('Auth'); + $url = '/AuthTest/login'; + $this->Controller->params = Router::parse($url); + $this->Controller->params['url']['url'] = Router::normalize($url); + $this->Controller->Auth->initialize($this->Controller); + $this->Controller->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login'); + $this->Controller->Auth->userModel = 'AuthUser'; + $this->Controller->Auth->startup($this->Controller); + $expected = Router::normalize('/'); + $this->assertEqual($expected, $this->Controller->Session->read('Auth.redirect')); + + $_SERVER['HTTP_REFERER'] = $backup; + $this->Controller->Session->del('Auth'); + } +/** + * Ensure that no redirect is performed when a 404 is reached + * And the user doesn't have a session. + * + * @return void + **/ + function testNoRedirectOn404() { + $this->Controller->Session->del('Auth'); + $this->Controller->Auth->initialize($this->Controller); + $this->Controller->params = Router::parse('auth_test/something_totally_wrong'); + $result = $this->Controller->Auth->startup($this->Controller); + $this->assertTrue($result, 'Auth redirected a missing action %s'); + } +/** + * testEmptyUsernameOrPassword method + * + * @access public + * @return void + */ + function testEmptyUsernameOrPassword() { + $this->AuthUser =& new AuthUser(); + $user['id'] = 1; + $user['username'] = 'mariano'; + $user['password'] = Security::hash(Configure::read('Security.salt') . 'cake'); + $this->AuthUser->save($user, false); + + $authUser = $this->AuthUser->find(); + + $this->Controller->data['AuthUser']['username'] = ''; + $this->Controller->data['AuthUser']['password'] = ''; + + $this->Controller->params = Router::parse('auth_test/login'); + $this->Controller->params['url']['url'] = 'auth_test/login'; + $this->Controller->Auth->initialize($this->Controller); + $this->Controller->Auth->loginAction = 'auth_test/login'; + $this->Controller->Auth->userModel = 'AuthUser'; + + $this->Controller->Auth->startup($this->Controller); + $user = $this->Controller->Auth->user(); + $this->assertTrue($this->Controller->Session->check('Message.auth')); + $this->assertEqual($user, false); + $this->Controller->Session->del('Auth'); + } +/** + * testInjection method + * + * @access public + * @return void + */ + function testInjection() { + $this->AuthUser =& new AuthUser(); + $this->AuthUser->id = 2; + $this->AuthUser->saveField('password', Security::hash(Configure::read('Security.salt') . 'cake')); + + $this->Controller->data['AuthUser']['username'] = 'nate'; + $this->Controller->data['AuthUser']['password'] = 'cake'; + $this->Controller->params = Router::parse('auth_test/login'); + $this->Controller->params['url']['url'] = 'auth_test/login'; + $this->Controller->Auth->initialize($this->Controller); + + $this->Controller->Auth->loginAction = 'auth_test/login'; + $this->Controller->Auth->userModel = 'AuthUser'; + $this->Controller->Auth->startup($this->Controller); + $this->assertTrue(is_array($this->Controller->Auth->user())); + + $this->Controller->Session->del($this->Controller->Auth->sessionKey); + + $this->Controller->data['AuthUser']['username'] = 'nate'; + $this->Controller->data['AuthUser']['password'] = 'cake1'; + $this->Controller->params['url']['url'] = 'auth_test/login'; + $this->Controller->Auth->initialize($this->Controller); + + $this->Controller->Auth->loginAction = 'auth_test/login'; + $this->Controller->Auth->userModel = 'AuthUser'; + $this->Controller->Auth->startup($this->Controller); + $this->assertTrue(is_null($this->Controller->Auth->user())); + + $this->Controller->Session->del($this->Controller->Auth->sessionKey); + + $this->Controller->data['AuthUser']['username'] = '> n'; + $this->Controller->data['AuthUser']['password'] = 'cake'; + $this->Controller->Auth->initialize($this->Controller); + + $this->Controller->Auth->startup($this->Controller); + $this->assertTrue(is_null($this->Controller->Auth->user())); + + unset($this->Controller->data['AuthUser']['password']); + $this->Controller->data['AuthUser']['username'] = "1'1"; + $this->Controller->Auth->initialize($this->Controller); + + $this->Controller->Auth->startup($this->Controller); + $this->assertTrue(is_null($this->Controller->Auth->user())); + + unset($this->Controller->data['AuthUser']['username']); + $this->Controller->data['AuthUser']['password'] = "1'1"; + $this->Controller->Auth->initialize($this->Controller); + + $this->Controller->Auth->startup($this->Controller); + $this->assertTrue(is_null($this->Controller->Auth->user())); + } +/** + * test Hashing of passwords + * + * @return void + **/ + function testHashPasswords() { + $this->Controller->Auth->userModel = 'AuthUser'; + + $data['AuthUser']['password'] = 'superSecret'; + $data['AuthUser']['username'] = 'superman@dailyplanet.com'; + $return = $this->Controller->Auth->hashPasswords($data); + $expected = $data; + $expected['AuthUser']['password'] = Security::hash($expected['AuthUser']['password'], null, true); + $this->assertEqual($return, $expected); + + $data['Wrong']['password'] = 'superSecret'; + $data['Wrong']['username'] = 'superman@dailyplanet.com'; + $data['AuthUser']['password'] = 'IcantTellYou'; + $return = $this->Controller->Auth->hashPasswords($data); + $expected = $data; + $expected['AuthUser']['password'] = Security::hash($expected['AuthUser']['password'], null, true); + $this->assertEqual($return, $expected); + + $xml = array( + 'User' => array( + 'username' => 'batman@batcave.com', + 'password' => 'bruceWayne', + ) + ); + $data = new Xml($xml); + $return = $this->Controller->Auth->hashPasswords($data); + $expected = $data; + $this->assertEqual($return, $expected); + } +/** + * testCustomRoute method + * + * @access public + * @return void + */ + function testCustomRoute() { + Router::reload(); + Router::connect( + '/:lang/:controller/:action/*', + array('lang' => null), + array('lang' => '[a-z]{2,3}') + ); + + $url = '/en/users/login'; + $this->Controller->params = Router::parse($url); + Router::setRequestInfo(array($this->Controller->passedArgs, array( + 'base' => null, 'here' => $url, 'webroot' => '/', 'passedArgs' => array(), + 'argSeparator' => ':', 'namedArgs' => array() + ))); + + $this->AuthUser =& new AuthUser(); + $user = array( + 'id' => 1, 'username' => 'felix', + 'password' => Security::hash(Configure::read('Security.salt') . 'cake' + )); + $user = $this->AuthUser->save($user, false); + + $this->Controller->data['AuthUser'] = array('username' => 'felix', 'password' => 'cake'); + $this->Controller->params['url']['url'] = substr($url, 1); + $this->Controller->Auth->initialize($this->Controller); + $this->Controller->Auth->loginAction = array('lang' => 'en', 'controller' => 'users', 'action' => 'login'); + $this->Controller->Auth->userModel = 'AuthUser'; + + $this->Controller->Auth->startup($this->Controller); + $user = $this->Controller->Auth->user(); + $this->assertTrue(!!$user); + + $this->Controller->Session->del('Auth'); + Router::reload(); + Router::connect('/', array('controller' => 'people', 'action' => 'login')); + $url = '/'; + $this->Controller->params = Router::parse($url); + Router::setRequestInfo(array($this->Controller->passedArgs, array( + 'base' => null, 'here' => $url, 'webroot' => '/', 'passedArgs' => array(), + 'argSeparator' => ':', 'namedArgs' => array() + ))); + $this->Controller->data['AuthUser'] = array('username' => 'felix', 'password' => 'cake'); + $this->Controller->params['url']['url'] = substr($url, 1); + $this->Controller->Auth->initialize($this->Controller); + $this->Controller->Auth->loginAction = array('controller' => 'people', 'action' => 'login'); + $this->Controller->Auth->userModel = 'AuthUser'; + + $this->Controller->Auth->startup($this->Controller); + $user = $this->Controller->Auth->user(); + $this->assertTrue(!!$user); + } +/** + * testCustomField method + * + * @access public + * @return void + */ + function testCustomField() { + Router::reload(); + + $this->AuthUserCustomField =& new AuthUserCustomField(); + $user = array( + 'id' => 1, 'email' => 'harking@example.com', + 'password' => Security::hash(Configure::read('Security.salt') . 'cake' + )); + $user = $this->AuthUserCustomField->save($user, false); + + Router::connect('/', array('controller' => 'people', 'action' => 'login')); + $url = '/'; + $this->Controller->params = Router::parse($url); + Router::setRequestInfo(array($this->Controller->passedArgs, array( + 'base' => null, 'here' => $url, 'webroot' => '/', 'passedArgs' => array(), + 'argSeparator' => ':', 'namedArgs' => array() + ))); + $this->Controller->data['AuthUserCustomField'] = array('email' => 'harking@example.com', 'password' => 'cake'); + $this->Controller->params['url']['url'] = substr($url, 1); + $this->Controller->Auth->initialize($this->Controller); + $this->Controller->Auth->fields = array('username' => 'email', 'password' => 'password'); + $this->Controller->Auth->loginAction = array('controller' => 'people', 'action' => 'login'); + $this->Controller->Auth->userModel = 'AuthUserCustomField'; + + $this->Controller->Auth->startup($this->Controller); + $user = $this->Controller->Auth->user(); + $this->assertTrue(!!$user); + } +/** + * testAdminRoute method + * + * @access public + * @return void + */ + function testAdminRoute() { + $admin = Configure::read('Routing.admin'); + Configure::write('Routing.admin', 'admin'); + Router::reload(); + + $url = '/admin/auth_test/add'; + $this->Controller->params = Router::parse($url); + $this->Controller->params['url']['url'] = ltrim($url, '/'); + Router::setRequestInfo(array( + array( + 'pass' => array(), 'action' => 'add', 'plugin' => null, + 'controller' => 'auth_test', 'admin' => true, + 'url' => array('url' => $this->Controller->params['url']['url']) + ), + array( + 'base' => null, 'here' => $url, + 'webroot' => '/', 'passedArgs' => array(), + ) + )); + $this->Controller->Auth->initialize($this->Controller); + + $this->Controller->Auth->loginAction = array( + 'admin' => true, 'controller' => 'auth_test', 'action' => 'login' + ); + $this->Controller->Auth->userModel = 'AuthUser'; + + $this->Controller->Auth->startup($this->Controller); + $this->assertEqual($this->Controller->testUrl, '/admin/auth_test/login'); + + Configure::write('Routing.admin', $admin); + } +/** + * testAjaxLogin method + * + * @access public + * @return void + */ + function testAjaxLogin() { + Configure::write('viewPaths', array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS)); + $_SERVER['HTTP_X_REQUESTED_WITH'] = "XMLHttpRequest"; + + if (!class_exists('dispatcher')) { + require CAKE . 'dispatcher.php'; + } + + ob_start(); + $Dispatcher =& new Dispatcher(); + $Dispatcher->dispatch('/ajax_auth/add', array('return' => 1)); + $result = ob_get_clean(); + $this->assertEqual("Ajax!\nthis is the test element", $result); + unset($_SERVER['HTTP_X_REQUESTED_WITH']); + } +/** + * testLoginActionRedirect method + * + * @access public + * @return void + */ + function testLoginActionRedirect() { + $admin = Configure::read('Routing.admin'); + Configure::write('Routing.admin', 'admin'); + Router::reload(); + + $url = '/admin/auth_test/login'; + $this->Controller->params = Router::parse($url); + $this->Controller->params['url']['url'] = ltrim($url, '/'); + Router::setRequestInfo(array( + array( + 'pass' => array(), 'action' => 'admin_login', 'plugin' => null, 'controller' => 'auth_test', + 'admin' => true, 'url' => array('url' => $this->Controller->params['url']['url']), + ), + array( + 'base' => null, 'here' => $url, + 'webroot' => '/', 'passedArgs' => array(), + ) + )); + + $this->Controller->Auth->initialize($this->Controller); + + $this->Controller->Auth->loginAction = array('admin' => true, 'controller' => 'auth_test', 'action' => 'login'); + $this->Controller->Auth->userModel = 'AuthUser'; + + $this->Controller->Auth->startup($this->Controller); + + $this->assertNull($this->Controller->testUrl); + + Configure::write('Routing.admin', $admin); + } +/** + * Tests that shutdown destroys the redirect session var + * + * @access public + * @return void + */ + function testShutDown() { + $this->Controller->Session->write('Auth.redirect', 'foo'); + $this->Controller->Auth->_loggedIn = true; + $this->Controller->Auth->shutdown($this->Controller); + $this->assertFalse($this->Controller->Session->read('Auth.redirect')); + } +} +?> diff --git a/cake/tests/cases/libs/controller/components/cookie.test.php b/cake/tests/cases/libs/controller/components/cookie.test.php new file mode 100755 index 0000000..5d8d87a --- /dev/null +++ b/cake/tests/cases/libs/controller/components/cookie.test.php @@ -0,0 +1,439 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * CookieComponentTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs.controller.components + * @since CakePHP(tm) v 1.2.0.5435 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +App::import('Core', array('Component', 'Controller', 'Cookie')); +/** + * CookieComponentTestController class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller.components + */ +class CookieComponentTestController extends Controller { +/** + * components property + * + * @var array + * @access public + */ + var $components = array('Cookie'); +/** + * beforeFilter method + * + * @access public + * @return void + */ + function beforeFilter() { + $this->Cookie->name = 'CakeTestCookie'; + $this->Cookie->time = 10; + $this->Cookie->path = '/'; + $this->Cookie->domain = ''; + $this->Cookie->secure = false; + $this->Cookie->key = 'somerandomhaskey'; + } +} +/** + * CookieComponentTest class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller.components + */ +class CookieComponentTest extends CakeTestCase { +/** + * Controller property + * + * @var CookieComponentTestController + * @access public + */ + var $Controller; +/** + * start + * + * @access public + * @return void + */ + function start() { + $this->Controller = new CookieComponentTestController(); + $this->Controller->constructClasses(); + $this->Controller->Component->initialize($this->Controller); + $this->Controller->beforeFilter(); + $this->Controller->Component->startup($this->Controller); + $this->Controller->Cookie->destroy(); + } +/** + * end + * + * @access public + * @return void + */ + function end() { + $this->Controller->Cookie->destroy(); + } +/** + * testCookieName + * + * @access public + * @return void + */ + function testCookieName() { + $this->assertEqual($this->Controller->Cookie->name, 'CakeTestCookie'); + } +/** + * testSettingEncryptedCookieData + * + * @access public + * @return void + */ + function testSettingEncryptedCookieData() { + $this->Controller->Cookie->write('Encrytped_array', array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!')); + $this->Controller->Cookie->write('Encrytped_multi_cookies.name', 'CakePHP'); + $this->Controller->Cookie->write('Encrytped_multi_cookies.version', '1.2.0.x'); + $this->Controller->Cookie->write('Encrytped_multi_cookies.tag', 'CakePHP Rocks!'); + } +/** + * testReadEncryptedCookieData + * + * @access public + * @return void + */ + function testReadEncryptedCookieData() { + $data = $this->Controller->Cookie->read('Encrytped_array'); + $expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!'); + $this->assertEqual($data, $expected); + + $data = $this->Controller->Cookie->read('Encrytped_multi_cookies'); + $expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!'); + $this->assertEqual($data, $expected); + } +/** + * testSettingPlainCookieData + * + * @access public + * @return void + */ + function testSettingPlainCookieData() { + $this->Controller->Cookie->write('Plain_array', array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!'), false); + $this->Controller->Cookie->write('Plain_multi_cookies.name', 'CakePHP', false); + $this->Controller->Cookie->write('Plain_multi_cookies.version', '1.2.0.x', false); + $this->Controller->Cookie->write('Plain_multi_cookies.tag', 'CakePHP Rocks!', false); + } +/** + * testReadPlainCookieData + * + * @access public + * @return void + */ + function testReadPlainCookieData() { + $data = $this->Controller->Cookie->read('Plain_array'); + $expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!'); + $this->assertEqual($data, $expected); + + $data = $this->Controller->Cookie->read('Plain_multi_cookies'); + $expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!'); + $this->assertEqual($data, $expected); + } +/** + * testWritePlainCookieArray + * + * @access public + * @return void + */ + function testWritePlainCookieArray() { + $this->Controller->Cookie->write(array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' => 'CakePHP Rocks!'), null, false); + + $this->assertEqual($this->Controller->Cookie->read('name'), 'CakePHP'); + $this->assertEqual($this->Controller->Cookie->read('version'), '1.2.0.x'); + $this->assertEqual($this->Controller->Cookie->read('tag'), 'CakePHP Rocks!'); + + $this->Controller->Cookie->del('name'); + $this->Controller->Cookie->del('version'); + $this->Controller->Cookie->del('tag'); + } +/** + * testReadingCookieValue + * + * @access public + * @return void + */ + function testReadingCookieValue() { + $data = $this->Controller->Cookie->read(); + $expected = array( + 'Encrytped_array' => array( + 'name' => 'CakePHP', + 'version' => '1.2.0.x', + 'tag' => 'CakePHP Rocks!'), + 'Encrytped_multi_cookies' => array( + 'name' => 'CakePHP', + 'version' => '1.2.0.x', + 'tag' => 'CakePHP Rocks!'), + 'Plain_array' => array( + 'name' => 'CakePHP', + 'version' => '1.2.0.x', + 'tag' => 'CakePHP Rocks!'), + 'Plain_multi_cookies' => array( + 'name' => 'CakePHP', + 'version' => '1.2.0.x', + 'tag' => 'CakePHP Rocks!')); + $this->assertEqual($data, $expected); + } +/** + * testDeleteCookieValue + * + * @access public + * @return void + */ + function testDeleteCookieValue() { + $this->Controller->Cookie->del('Encrytped_multi_cookies.name'); + $data = $this->Controller->Cookie->read('Encrytped_multi_cookies'); + $expected = array('version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!'); + $this->assertEqual($data, $expected); + + $this->Controller->Cookie->del('Encrytped_array'); + $data = $this->Controller->Cookie->read('Encrytped_array'); + $expected = array(); + $this->assertEqual($data, $expected); + + $this->Controller->Cookie->del('Plain_multi_cookies.name'); + $data = $this->Controller->Cookie->read('Plain_multi_cookies'); + $expected = array('version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!'); + $this->assertEqual($data, $expected); + + $this->Controller->Cookie->del('Plain_array'); + $data = $this->Controller->Cookie->read('Plain_array'); + $expected = array(); + $this->assertEqual($data, $expected); + } +/** + * testSettingCookiesWithArray + * + * @access public + * @return void + */ + function testSettingCookiesWithArray() { + $this->Controller->Cookie->destroy(); + + $this->Controller->Cookie->write(array('Encrytped_array' => array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!'))); + $this->Controller->Cookie->write(array('Encrytped_multi_cookies.name' => 'CakePHP')); + $this->Controller->Cookie->write(array('Encrytped_multi_cookies.version' => '1.2.0.x')); + $this->Controller->Cookie->write(array('Encrytped_multi_cookies.tag' => 'CakePHP Rocks!')); + + $this->Controller->Cookie->write(array('Plain_array' => array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!')), null, false); + $this->Controller->Cookie->write(array('Plain_multi_cookies.name' => 'CakePHP'), null, false); + $this->Controller->Cookie->write(array('Plain_multi_cookies.version' => '1.2.0.x'), null, false); + $this->Controller->Cookie->write(array('Plain_multi_cookies.tag' => 'CakePHP Rocks!'), null, false); + } +/** + * testReadingCookieArray + * + * @access public + * @return void + */ + function testReadingCookieArray() { + $data = $this->Controller->Cookie->read('Encrytped_array.name'); + $expected = 'CakePHP'; + $this->assertEqual($data, $expected); + + $data = $this->Controller->Cookie->read('Encrytped_array.version'); + $expected = '1.2.0.x'; + $this->assertEqual($data, $expected); + + $data = $this->Controller->Cookie->read('Encrytped_array.tag'); + $expected = 'CakePHP Rocks!'; + $this->assertEqual($data, $expected); + + $data = $this->Controller->Cookie->read('Encrytped_multi_cookies.name'); + $expected = 'CakePHP'; + $this->assertEqual($data, $expected); + + $data = $this->Controller->Cookie->read('Encrytped_multi_cookies.version'); + $expected = '1.2.0.x'; + $this->assertEqual($data, $expected); + + $data = $this->Controller->Cookie->read('Encrytped_multi_cookies.tag'); + $expected = 'CakePHP Rocks!'; + $this->assertEqual($data, $expected); + + $data = $this->Controller->Cookie->read('Plain_array.name'); + $expected = 'CakePHP'; + $this->assertEqual($data, $expected); + + $data = $this->Controller->Cookie->read('Plain_array.version'); + $expected = '1.2.0.x'; + $this->assertEqual($data, $expected); + + $data = $this->Controller->Cookie->read('Plain_array.tag'); + $expected = 'CakePHP Rocks!'; + $this->assertEqual($data, $expected); + + $data = $this->Controller->Cookie->read('Plain_multi_cookies.name'); + $expected = 'CakePHP'; + $this->assertEqual($data, $expected); + + $data = $this->Controller->Cookie->read('Plain_multi_cookies.version'); + $expected = '1.2.0.x'; + $this->assertEqual($data, $expected); + + $data = $this->Controller->Cookie->read('Plain_multi_cookies.tag'); + $expected = 'CakePHP Rocks!'; + $this->assertEqual($data, $expected); + } +/** + * testReadingCookieDataOnStartup + * + * @access public + * @return void + */ + function testReadingCookieDataOnStartup() { + $this->Controller->Cookie->destroy(); + + $data = $this->Controller->Cookie->read('Encrytped_array'); + $expected = array(); + $this->assertEqual($data, $expected); + + $data = $this->Controller->Cookie->read('Encrytped_multi_cookies'); + $expected = array(); + $this->assertEqual($data, $expected); + + $data = $this->Controller->Cookie->read('Plain_array'); + $expected = array(); + $this->assertEqual($data, $expected); + + $data = $this->Controller->Cookie->read('Plain_multi_cookies'); + $expected = array(); + $this->assertEqual($data, $expected); + + $_COOKIE['CakeTestCookie'] = array( + 'Encrytped_array' => $this->__encrypt(array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!')), + 'Encrytped_multi_cookies' => array( + 'name' => $this->__encrypt('CakePHP'), + 'version' => $this->__encrypt('1.2.0.x'), + 'tag' => $this->__encrypt('CakePHP Rocks!')), + 'Plain_array' => 'name|CakePHP,version|1.2.0.x,tag|CakePHP Rocks!', + 'Plain_multi_cookies' => array( + 'name' => 'CakePHP', + 'version' => '1.2.0.x', + 'tag' => 'CakePHP Rocks!')); + $this->Controller->Cookie->startup(); + + $data = $this->Controller->Cookie->read('Encrytped_array'); + $expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!'); + $this->assertEqual($data, $expected); + + $data = $this->Controller->Cookie->read('Encrytped_multi_cookies'); + $expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!'); + $this->assertEqual($data, $expected); + + $data = $this->Controller->Cookie->read('Plain_array'); + $expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!'); + $this->assertEqual($data, $expected); + + $data = $this->Controller->Cookie->read('Plain_multi_cookies'); + $expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!'); + $this->assertEqual($data, $expected); + $this->Controller->Cookie->destroy(); + unset($_COOKIE['CakeTestCookie']); + } +/** + * testReadingCookieDataWithoutStartup + * + * @access public + * @return void + */ + function testReadingCookieDataWithoutStartup() { + $data = $this->Controller->Cookie->read('Encrytped_array'); + $expected = array(); + $this->assertEqual($data, $expected); + + $data = $this->Controller->Cookie->read('Encrytped_multi_cookies'); + $expected = array(); + $this->assertEqual($data, $expected); + + $data = $this->Controller->Cookie->read('Plain_array'); + $expected = array(); + $this->assertEqual($data, $expected); + + $data = $this->Controller->Cookie->read('Plain_multi_cookies'); + $expected = array(); + $this->assertEqual($data, $expected); + + $_COOKIE['CakeTestCookie'] = array( + 'Encrytped_array' => $this->__encrypt(array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!')), + 'Encrytped_multi_cookies' => array( + 'name' => $this->__encrypt('CakePHP'), + 'version' => $this->__encrypt('1.2.0.x'), + 'tag' => $this->__encrypt('CakePHP Rocks!')), + 'Plain_array' => 'name|CakePHP,version|1.2.0.x,tag|CakePHP Rocks!', + 'Plain_multi_cookies' => array( + 'name' => 'CakePHP', + 'version' => '1.2.0.x', + 'tag' => 'CakePHP Rocks!')); + + $data = $this->Controller->Cookie->read('Encrytped_array'); + $expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!'); + $this->assertEqual($data, $expected); + + $data = $this->Controller->Cookie->read('Encrytped_multi_cookies'); + $expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!'); + $this->assertEqual($data, $expected); + + $data = $this->Controller->Cookie->read('Plain_array'); + $expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!'); + $this->assertEqual($data, $expected); + + $data = $this->Controller->Cookie->read('Plain_multi_cookies'); + $expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!'); + $this->assertEqual($data, $expected); + $this->Controller->Cookie->destroy(); + unset($_COOKIE['CakeTestCookie']); + } +/** + * encrypt method + * + * @param mixed $value + * @return string + * @access private + */ + function __encrypt($value) { + if (is_array($value)) { + $value = $this->__implode($value); + } + return "Q2FrZQ==." . base64_encode(Security::cipher($value, $this->Controller->Cookie->key)); + } +/** + * implode method + * + * @param array $value + * @return string + * @access private + */ + function __implode($array) { + $string = ''; + foreach ($array as $key => $value) { + $string .= ',' . $key . '|' . $value; + } + return substr($string, 1); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/controller/components/email.test.php b/cake/tests/cases/libs/controller/components/email.test.php new file mode 100755 index 0000000..8baf95e --- /dev/null +++ b/cake/tests/cases/libs/controller/components/email.test.php @@ -0,0 +1,670 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * EmailComponentTest file + * + * Series of tests for email component. + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.cake.tests.cases.libs.controller.components + * @since CakePHP(tm) v 1.2.0.5347 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +App::import('Component', 'Email'); +/** + * EmailTestComponent class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller.components + */ +class EmailTestComponent extends EmailComponent { +/** + * smtpSend method override for testing + * + * @access public + * @return mixed + */ + function smtpSend($data, $code = '250') { + return parent::__smtpSend($data, $code); + } +/** + * Convenience setter method for testing. + * + * @access public + * @return void + */ + function setConnectionSocket(&$socket) { + $this->__smtpConnection = $socket; + } +/** + * Convenience getter method for testing. + * + * @access public + * @return mixed + */ + function getConnectionSocket() { + return $this->__smtpConnection; + } +/** + * Convenience setter for testing. + * + * @access public + * @return void + */ + function setHeaders($headers) { + $this->__header += $headers; + } +/** + * Convenience getter for testing. + * + * @access public + * @return array + */ + function getHeaders() { + return $this->__header; + } +/** + * Convenience setter for testing. + * + * @access public + * @return void + */ + function setBoundary() { + $this->__createBoundary(); + } +/** + * Convenience getter for testing. + * + * @access public + * @return string + */ + function getBoundary() { + return $this->__boundary; + } +/** + * Convenience getter for testing. + * + * @access public + * @return string + */ + function getMessage() { + return $this->__message; + } +/** + * Convenience method for testing. + * + * @access public + * @return string + */ + function strip($content, $message = false) { + return parent::__strip($content, $message); + } +} +/** + * EmailTestController class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller.components + */ +class EmailTestController extends Controller { +/** + * name property + * + * @var string 'EmailTest' + * @access public + */ + var $name = 'EmailTest'; +/** + * uses property + * + * @var mixed null + * @access public + */ + var $uses = null; +/** + * components property + * + * @var array + * @access public + */ + var $components = array('EmailTest'); +/** + * pageTitle property + * + * @var string + * @access public + */ + var $pageTitle = 'EmailTest'; +} +/** + * EmailTest class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller.components + */ +class EmailComponentTest extends CakeTestCase { +/** + * Controller property + * + * @var EmailTestController + * @access public + */ + var $Controller; +/** + * name property + * + * @var string 'Email' + * @access public + */ + var $name = 'Email'; +/** + * setUp method + * + * @access public + * @return void + */ + function setUp() { + $this->_appEncoding = Configure::read('App.encoding'); + Configure::write('App.encoding', 'UTF-8'); + + $this->Controller =& new EmailTestController(); + + restore_error_handler(); + @$this->Controller->Component->init($this->Controller); + set_error_handler('simpleTestErrorHandler'); + + $this->Controller->EmailTest->initialize($this->Controller, array()); + ClassRegistry::addObject('view', new View($this->Controller)); + + $this->_viewPaths = Configure::read('viewPaths'); + Configure::write('viewPaths', array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS)); + + } +/** + * tearDown method + * + * @access public + * @return void + */ + function tearDown() { + Configure::write('App.encoding', $this->_appEncoding); + Configure::write('viewPaths', $this->_viewPaths); + $this->Controller->Session->del('Message'); + restore_error_handler(); + ClassRegistry::flush(); + } +/** + * testBadSmtpSend method + * + * @access public + * @return void + */ + function testBadSmtpSend() { + $this->Controller->EmailTest->smtpOptions['host'] = 'blah'; + $this->Controller->EmailTest->delivery = 'smtp'; + $this->assertFalse($this->Controller->EmailTest->send('Should not work')); + } +/** + * testSmtpSend method + * + * @access public + * @return void + */ + function testSmtpSend() { + if (!$this->skipIf(!@fsockopen('localhost', 25), '%s No SMTP server running on localhost')) { + return; + } + $this->Controller->EmailTest->reset(); + $this->Controller->EmailTest->to = 'postmaster@localhost'; + $this->Controller->EmailTest->from = 'noreply@example.com'; + $this->Controller->EmailTest->subject = 'Cake SMTP test'; + $this->Controller->EmailTest->replyTo = 'noreply@example.com'; + $this->Controller->EmailTest->template = null; + + $this->Controller->EmailTest->delivery = 'smtp'; + $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message')); + + $this->Controller->EmailTest->_debug = true; + $this->Controller->EmailTest->sendAs = 'text'; + $expect = <<<TEMPDOC +<pre>Host: localhost +Port: 25 +Timeout: 30 +To: postmaster@localhost +From: noreply@example.com +Subject: Cake SMTP test +Header: + +To: postmaster@localhost +From: noreply@example.com +Reply-To: noreply@example.com +Subject: Cake SMTP test +X-Mailer: CakePHP Email Component +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 7bitParameters: + +Message: + +This is the body of the message + +</pre> +TEMPDOC; + $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message')); + $this->assertEqual($this->Controller->Session->read('Message.email.message'), $this->__osFix($expect)); + } +/** + * testAuthenticatedSmtpSend method + * + * @access public + * @return void + */ + function testAuthenticatedSmtpSend() { + $this->skipIf(!@fsockopen('localhost', 25), '%s No SMTP server running on localhost'); + + $this->Controller->EmailTest->reset(); + $this->Controller->EmailTest->to = 'postmaster@localhost'; + $this->Controller->EmailTest->from = 'noreply@example.com'; + $this->Controller->EmailTest->subject = 'Cake SMTP test'; + $this->Controller->EmailTest->replyTo = 'noreply@example.com'; + $this->Controller->EmailTest->template = null; + $this->Controller->EmailTest->smtpOptions['username'] = 'test'; + $this->Controller->EmailTest->smtpOptions['password'] = 'testing'; + + $this->Controller->EmailTest->delivery = 'smtp'; + $result = $this->Controller->EmailTest->send('This is the body of the message'); + $code = substr($this->Controller->EmailTest->smtpError, 0, 3); + $this->skipIf(!$code, '%s Authentication not enabled on server'); + + $this->assertFalse($result); + $this->assertEqual($code, '535'); + } +/** + * testSendFormats method + * + * @access public + * @return void + */ + function testSendFormats() { + $this->Controller->EmailTest->reset(); + $this->Controller->EmailTest->to = 'postmaster@localhost'; + $this->Controller->EmailTest->from = 'noreply@example.com'; + $this->Controller->EmailTest->subject = 'Cake SMTP test'; + $this->Controller->EmailTest->replyTo = 'noreply@example.com'; + $this->Controller->EmailTest->template = null; + $this->Controller->EmailTest->delivery = 'debug'; + + $message = <<<MSGBLOC +<pre>To: postmaster@localhost +From: noreply@example.com +Subject: Cake SMTP test +Header: + +From: noreply@example.com +Reply-To: noreply@example.com +X-Mailer: CakePHP Email Component +Content-Type: {CONTENTTYPE} +Content-Transfer-Encoding: 7bitParameters: + +Message: + +This is the body of the message + +</pre> +MSGBLOC; + $this->Controller->EmailTest->sendAs = 'text'; + $expect = str_replace('{CONTENTTYPE}', 'text/plain; charset=UTF-8', $message); + $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message')); + $this->assertEqual($this->Controller->Session->read('Message.email.message'), $this->__osFix($expect)); + + $this->Controller->EmailTest->sendAs = 'html'; + $expect = str_replace('{CONTENTTYPE}', 'text/html; charset=UTF-8', $message); + $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message')); + $this->assertEqual($this->Controller->Session->read('Message.email.message'), $this->__osFix($expect)); + + // TODO: better test for format of message sent? + $this->Controller->EmailTest->sendAs = 'both'; + $expect = str_replace('{CONTENTTYPE}', 'multipart/alternative; boundary="alt-"', $message); + + $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message')); + $this->assertEqual($this->Controller->Session->read('Message.email.message'), $this->__osFix($expect)); + } +/** + * testTemplates method + * + * @access public + * @return void + */ + function testTemplates() { + $this->Controller->EmailTest->reset(); + $this->Controller->EmailTest->to = 'postmaster@localhost'; + $this->Controller->EmailTest->from = 'noreply@example.com'; + $this->Controller->EmailTest->subject = 'Cake SMTP test'; + $this->Controller->EmailTest->replyTo = 'noreply@example.com'; + + $this->Controller->EmailTest->delivery = 'debug'; + + $header = <<<HEADBLOC +To: postmaster@localhost +From: noreply@example.com +Subject: Cake SMTP test +Header: + +From: noreply@example.com +Reply-To: noreply@example.com +X-Mailer: CakePHP Email Component +Content-Type: {CONTENTTYPE} +Content-Transfer-Encoding: 7bitParameters: + +Message: + + +HEADBLOC; + + $this->Controller->EmailTest->layout = 'default'; + $this->Controller->EmailTest->template = 'default'; + + $text = <<<TEXTBLOC + +This is the body of the message + +This email was sent using the CakePHP Framework, http://cakephp.org. + + +TEXTBLOC; + + $html = <<<HTMLBLOC +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"> + +<html> +<head> + <title>EmailTest</title> +</head> + +<body> + <p> This is the body of the message</p><p> </p> + <p>This email was sent using the <a href="http://cakephp.org">CakePHP Framework</a></p> +</body> +</html> + +HTMLBLOC; + + $this->Controller->EmailTest->sendAs = 'text'; + $expect = '<pre>' . str_replace('{CONTENTTYPE}', 'text/plain; charset=UTF-8', $header) . $text . "\n" . '</pre>'; + $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message')); + $this->assertEqual($this->Controller->Session->read('Message.email.message'), $this->__osFix($expect)); + + $this->Controller->EmailTest->sendAs = 'html'; + $expect = '<pre>' . str_replace('{CONTENTTYPE}', 'text/html; charset=UTF-8', $header) . $html . "\n" . '</pre>'; + $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message')); + $this->assertEqual($this->Controller->Session->read('Message.email.message'), $this->__osFix($expect)); + + $this->Controller->EmailTest->sendAs = 'both'; + $expect = str_replace('{CONTENTTYPE}', 'multipart/alternative; boundary="alt-"', $header); + $expect .= '--alt-' . "\n" . 'Content-Type: text/plain; charset=UTF-8' . "\n" . 'Content-Transfer-Encoding: 7bit' . "\n\n" . $text . "\n\n"; + $expect .= '--alt-' . "\n" . 'Content-Type: text/html; charset=UTF-8' . "\n" . 'Content-Transfer-Encoding: 7bit' . "\n\n" . $html . "\n\n"; + $expect = '<pre>' . $expect . '--alt---' . "\n\n" . '</pre>'; + + $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message')); + $this->assertEqual($this->Controller->Session->read('Message.email.message'), $this->__osFix($expect)); + + $html = <<<HTMLBLOC +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"> + +<html> +<head> + <title>EmailTest</title> +</head> + +<body> + <p> This is the body of the message</p><p> </p> + <p>This email was sent using the CakePHP Framework</p> +</body> +</html> + +HTMLBLOC; + + $this->Controller->EmailTest->sendAs = 'html'; + $expect = '<pre>' . str_replace('{CONTENTTYPE}', 'text/html; charset=UTF-8', $header) . $html . "\n" . '</pre>'; + $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message', 'default', 'thin')); + $this->assertEqual($this->Controller->Session->read('Message.email.message'), $this->__osFix($expect)); + + return; + + $text = <<<TEXTBLOC + +This element has some text that is just too wide to comply with email +standards. +This is the body of the message + +This email was sent using the CakePHP Framework, http://cakephp.org. + + +TEXTBLOC; + + $this->Controller->EmailTest->sendAs = 'text'; + $expect = '<pre>' . str_replace('{CONTENTTYPE}', 'text/plain; charset=UTF-8', $header) . $text . "\n" . '</pre>'; + $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message', 'wide', 'default')); + $this->assertEqual($this->Controller->Session->read('Message.email.message'), $this->__osFix($expect)); + } +/** + * testSmtpSendSocket method + * + * @access public + * @return void + */ + function testSmtpSendSocket() { + $this->skipIf(!@fsockopen('localhost', 25), '%s No SMTP server running on localhost'); + + $this->Controller->EmailTest->reset(); + $socket =& new CakeSocket(array_merge(array('protocol'=>'smtp'), $this->Controller->EmailTest->smtpOptions)); + $this->Controller->EmailTest->setConnectionSocket($socket); + + $this->assertTrue($this->Controller->EmailTest->getConnectionSocket()); + + $response = $this->Controller->EmailTest->smtpSend('HELO', '250'); + $this->assertPattern('/501 Syntax: HELO hostname/', $this->Controller->EmailTest->smtpError); + + $this->Controller->EmailTest->reset(); + $response = $this->Controller->EmailTest->smtpSend('HELO somehostname', '250'); + $this->assertNoPattern('/501 Syntax: HELO hostname/', $this->Controller->EmailTest->smtpError); + } +/** + * testSendDebug method + * + * @access public + * @return void + */ + function testSendDebug() { + $this->Controller->EmailTest->reset(); + $this->Controller->EmailTest->to = 'postmaster@localhost'; + $this->Controller->EmailTest->from = 'noreply@example.com'; + $this->Controller->EmailTest->subject = 'Cake SMTP test'; + $this->Controller->EmailTest->replyTo = 'noreply@example.com'; + $this->Controller->EmailTest->template = null; + + $this->Controller->EmailTest->delivery = 'debug'; + $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message')); + } +/** + * testContentStripping method + * + * @access public + * @return void + */ + function testContentStripping() { + $content = "Previous content\n--alt-\nContent-TypeContent-Type:: text/html; charsetcharset==utf-8\nContent-Transfer-Encoding: 7bit"; + $content .= "\n\n<p>My own html content</p>"; + + $result = $this->Controller->EmailTest->strip($content, true); + $expected = "Previous content\n--alt-\n text/html; utf-8\n 7bit\n\n<p>My own html content</p>"; + $this->assertEqual($result, $expected); + + $content = '<p>Some HTML content with an <a href="mailto:test@example.com">email link</a>'; + $result = $this->Controller->EmailTest->strip($content, true); + $expected = $content; + $this->assertEqual($result, $expected); + + $content = '<p>Some HTML content with an '; + $content .= '<a href="mailto:test@example.com,test2@example.com">email link</a>'; + $result = $this->Controller->EmailTest->strip($content, true); + $expected = $content; + $this->assertEqual($result, $expected); + + } +/** + * testMultibyte method + * + * @access public + * @return void + */ + function testMultibyte() { + $this->Controller->EmailTest->reset(); + $this->Controller->EmailTest->to = 'postmaster@localhost'; + $this->Controller->EmailTest->from = 'noreply@example.com'; + $this->Controller->EmailTest->subject = 'هذه رسالة بعنوان طويل مرسل للمستلم'; + $this->Controller->EmailTest->replyTo = 'noreply@example.com'; + $this->Controller->EmailTest->template = null; + $this->Controller->EmailTest->delivery = 'debug'; + + $subject = '=?UTF-8?B?2YfYsNmHINix2LPYp9mE2Kkg2KjYudmG2YjYp9mGINi32YjZitmEINmF2LE=?=' . "\r\n" . ' =?UTF-8?B?2LPZhCDZhNmE2YXYs9iq2YTZhQ==?='; + + $this->Controller->EmailTest->sendAs = 'text'; + $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message')); + preg_match('/Subject: (.*)Header:/s', $this->Controller->Session->read('Message.email.message'), $matches); + $this->assertEqual(trim($matches[1]), $subject); + + $this->Controller->EmailTest->sendAs = 'html'; + $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message')); + preg_match('/Subject: (.*)Header:/s', $this->Controller->Session->read('Message.email.message'), $matches); + $this->assertEqual(trim($matches[1]), $subject); + + $this->Controller->EmailTest->sendAs = 'both'; + $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message')); + preg_match('/Subject: (.*)Header:/s', $this->Controller->Session->read('Message.email.message'), $matches); + $this->assertEqual(trim($matches[1]), $subject); + } +/** + * undocumented function + * + * @return void + * @access public + */ + function testSendAsIsNotIgnoredIfAttachmentsPresent() { + $this->Controller->EmailTest->reset(); + $this->Controller->EmailTest->to = 'postmaster@localhost'; + $this->Controller->EmailTest->from = 'noreply@example.com'; + $this->Controller->EmailTest->subject = 'Attachment Test'; + $this->Controller->EmailTest->replyTo = 'noreply@example.com'; + $this->Controller->EmailTest->template = null; + $this->Controller->EmailTest->delivery = 'debug'; + $this->Controller->EmailTest->attachments = array(__FILE__); + $body = '<p>This is the body of the message</p>'; + + $this->Controller->EmailTest->sendAs = 'html'; + $this->assertTrue($this->Controller->EmailTest->send($body)); + $msg = $this->Controller->Session->read('Message.email.message'); + $this->assertNoPattern('/text\/plain/', $msg); + $this->assertPattern('/text\/html/', $msg); + + $this->Controller->EmailTest->sendAs = 'text'; + $this->assertTrue($this->Controller->EmailTest->send($body)); + $msg = $this->Controller->Session->read('Message.email.message'); + $this->assertPattern('/text\/plain/', $msg); + $this->assertNoPattern('/text\/html/', $msg); + + $this->Controller->EmailTest->sendAs = 'both'; + $this->assertTrue($this->Controller->EmailTest->send($body)); + $msg = $this->Controller->Session->read('Message.email.message'); + + $this->assertNoPattern('/text\/plain/', $msg); + $this->assertNoPattern('/text\/html/', $msg); + $this->assertPattern('/multipart\/alternative/', $msg); + } +/** + * undocumented function + * + * @return void + * @access public + */ + function testNoDoubleNewlinesInHeaders() { + $this->Controller->EmailTest->reset(); + $this->Controller->EmailTest->to = 'postmaster@localhost'; + $this->Controller->EmailTest->from = 'noreply@example.com'; + $this->Controller->EmailTest->subject = 'Attachment Test'; + $this->Controller->EmailTest->replyTo = 'noreply@example.com'; + $this->Controller->EmailTest->template = null; + $this->Controller->EmailTest->delivery = 'debug'; + $body = '<p>This is the body of the message</p>'; + + $this->Controller->EmailTest->sendAs = 'both'; + $this->assertTrue($this->Controller->EmailTest->send($body)); + $msg = $this->Controller->Session->read('Message.email.message'); + + $this->assertNoPattern('/\n\nContent-Transfer-Encoding/', $msg); + $this->assertPattern('/\nContent-Transfer-Encoding/', $msg); + } +/** + * testReset method + * + * @access public + * @return void + */ + function testReset() { + $this->Controller->EmailTest->template = 'test_template'; + $this->Controller->EmailTest->to = 'test.recipient@example.com'; + $this->Controller->EmailTest->from = 'test.sender@example.com'; + $this->Controller->EmailTest->replyTo = 'test.replyto@example.com'; + $this->Controller->EmailTest->return = 'test.return@example.com'; + $this->Controller->EmailTest->cc = array('cc1@example.com', 'cc2@example.com'); + $this->Controller->EmailTest->bcc = array('bcc1@example.com', 'bcc2@example.com'); + $this->Controller->EmailTest->subject = 'Test subject'; + $this->Controller->EmailTest->additionalParams = 'X-additional-header'; + $this->Controller->EmailTest->delivery = 'smtp'; + $this->Controller->EmailTest->smtpOptions['host'] = 'blah'; + $this->Controller->EmailTest->attachments = array('attachment1', 'attachment2'); + + $this->assertFalse($this->Controller->EmailTest->send('Should not work')); + + $this->Controller->EmailTest->reset(); + + $this->assertNull($this->Controller->EmailTest->template); + $this->assertNull($this->Controller->EmailTest->to); + $this->assertNull($this->Controller->EmailTest->from); + $this->assertNull($this->Controller->EmailTest->replyTo); + $this->assertNull($this->Controller->EmailTest->return); + $this->assertIdentical($this->Controller->EmailTest->cc, array()); + $this->assertIdentical($this->Controller->EmailTest->bcc, array()); + $this->assertNull($this->Controller->EmailTest->subject); + $this->assertNull($this->Controller->EmailTest->additionalParams); + $this->assertIdentical($this->Controller->EmailTest->getHeaders(), array()); + $this->assertNull($this->Controller->EmailTest->getBoundary()); + $this->assertIdentical($this->Controller->EmailTest->getMessage(), array()); + $this->assertNull($this->Controller->EmailTest->smtpError); + $this->assertIdentical($this->Controller->EmailTest->attachments, array()); + } +/** + * osFix method + * + * @param string $string + * @access private + * @return string + */ + function __osFix($string) { + return str_replace(array("\r\n", "\r"), "\n", $string); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/controller/components/request_handler.test.php b/cake/tests/cases/libs/controller/components/request_handler.test.php new file mode 100755 index 0000000..f0e638d --- /dev/null +++ b/cake/tests/cases/libs/controller/components/request_handler.test.php @@ -0,0 +1,542 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * RequestHandlerComponentTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs.controller.components + * @since CakePHP(tm) v 1.2.0.5435 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +App::import('Core', array('Controller')); +App::import('Component', array('RequestHandler')); + +Mock::generatePartial('RequestHandlerComponent', 'NoStopRequestHandler', array('_stop')); +/** + * RequestHandlerTestController class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller.components + */ +class RequestHandlerTestController extends Controller { +/** + * name property + * + * @var string + * @access public + **/ + var $name = 'RequestHandlerTest'; +/** + * uses property + * + * @var mixed null + * @access public + */ + var $uses = null; +/** + * construct method + * + * @param array $params + * @access private + * @return void + */ + function __construct($params = array()) { + foreach ($params as $key => $val) { + $this->{$key} = $val; + } + parent::__construct(); + } +/** + * test method for ajax redirection + * + * @return void + **/ + function destination() { + $this->viewPath = 'posts'; + $this->render('index'); + } +} +/** + * RequestHandlerTestDisabledController class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller.components + */ +class RequestHandlerTestDisabledController extends Controller { +/** + * uses property + * + * @var mixed null + * @access public + */ + var $uses = null; +/** + * construct method + * + * @param array $params + * @access private + * @return void + */ + function __construct($params = array()) { + foreach ($params as $key => $val) { + $this->{$key} = $val; + } + parent::__construct(); + } +/** + * beforeFilter method + * + * @return void + * @access public + */ + function beforeFilter() { + $this->RequestHandler->enabled = false; + } +} +/** + * RequestHandlerComponentTest class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller.components + */ +class RequestHandlerComponentTest extends CakeTestCase { +/** + * Controller property + * + * @var RequestHandlerTestController + * @access public + */ + var $Controller; +/** + * RequestHandler property + * + * @var RequestHandlerComponent + * @access public + */ + var $RequestHandler; +/** + * startTest method + * + * @access public + * @return void + */ + function startTest() { + $this->_init(); + } +/** + * init method + * + * @access protected + * @return void + */ + function _init() { + $this->Controller = new RequestHandlerTestController(array('components' => array('RequestHandler'))); + $this->Controller->constructClasses(); + $this->RequestHandler =& $this->Controller->RequestHandler; + } +/** + * endTest method + * + * @access public + * @return void + */ + function endTest() { + unset($this->RequestHandler); + unset($this->Controller); + if (!headers_sent()) { + header('Content-type: text/html'); //reset content type. + } + } +/** + * testInitializeCallback method + * + * @access public + * @return void + */ + function testInitializeCallback() { + $this->assertNull($this->RequestHandler->ext); + + $this->_init(); + $this->Controller->params['url']['ext'] = 'rss'; + $this->RequestHandler->initialize($this->Controller); + $this->assertEqual($this->RequestHandler->ext, 'rss'); + } +/** + * testDisabling method + * + * @access public + * @return void + */ + function testDisabling() { + $_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'; + $this->_init(); + $this->Controller->Component->initialize($this->Controller); + $this->Controller->beforeFilter(); + $this->Controller->Component->startup($this->Controller); + $this->assertEqual($this->Controller->params, array('isAjax' => true)); + + $this->Controller = new RequestHandlerTestDisabledController(array('components' => array('RequestHandler'))); + $this->Controller->constructClasses(); + $this->Controller->Component->initialize($this->Controller); + $this->Controller->beforeFilter(); + $this->Controller->Component->startup($this->Controller); + $this->assertEqual($this->Controller->params, array()); + unset($_SERVER['HTTP_X_REQUESTED_WITH']); + } +/** + * testAutoResponseType method + * + * @access public + * @return void + */ + function testAutoResponseType() { + $this->Controller->ext = '.thtml'; + $this->Controller->params['url']['ext'] = 'rss'; + $this->RequestHandler->initialize($this->Controller); + $this->RequestHandler->startup($this->Controller); + $this->assertEqual($this->Controller->ext, '.ctp'); + } +/** + * testStartupCallback method + * + * @access public + * @return void + */ + function testStartupCallback() { + $_SERVER['REQUEST_METHOD'] = 'PUT'; + $_SERVER['CONTENT_TYPE'] = 'application/xml'; + $this->RequestHandler->startup($this->Controller); + $this->assertTrue(is_object($this->Controller->data)); + $this->assertEqual(strtolower(get_class($this->Controller->data)), 'xml'); + } +/** + * testStartupCallback with charset. + * + * @return void + **/ + function testStartupCallbackCharset() { + $_SERVER['REQUEST_METHOD'] = 'PUT'; + $_SERVER['CONTENT_TYPE'] = 'application/xml; charset=UTF-8'; + $this->RequestHandler->startup($this->Controller); + $this->assertTrue(is_object($this->Controller->data)); + $this->assertEqual(strtolower(get_class($this->Controller->data)), 'xml'); + } +/** + * testNonAjaxRedirect method + * + * @access public + * @return void + */ + function testNonAjaxRedirect() { + $this->RequestHandler->initialize($this->Controller); + $this->RequestHandler->startup($this->Controller); + $this->assertNull($this->RequestHandler->beforeRedirect($this->Controller, '/')); + } +/** + * testRenderAs method + * + * @access public + * @return void + */ + function testRenderAs() { + $this->assertFalse(in_array('Xml', $this->Controller->helpers)); + $this->RequestHandler->renderAs($this->Controller, 'xml'); + $this->assertTrue(in_array('Xml', $this->Controller->helpers)); + } +/** + * test that calling renderAs() more than once continues to work. + * + * @link #6466 + * @return void + **/ + function testRenderAsCalledTwice() { + $this->RequestHandler->renderAs($this->Controller, 'xml'); + $this->assertEqual($this->Controller->viewPath, 'request_handler_test/xml'); + $this->assertEqual($this->Controller->layoutPath, 'xml'); + + $this->assertTrue(in_array('Xml', $this->Controller->helpers)); + + $this->RequestHandler->renderAs($this->Controller, 'js'); + $this->assertEqual($this->Controller->viewPath, 'request_handler_test/js'); + $this->assertEqual($this->Controller->layoutPath, 'js'); + $this->assertTrue(in_array('Js', $this->Controller->helpers)); + } +/** + * testRequestClientTypes method + * + * @access public + * @return void + */ + function testRequestClientTypes() { + $this->assertFalse($this->RequestHandler->isFlash()); + $_SERVER['HTTP_USER_AGENT'] = 'Shockwave Flash'; + $this->assertTrue($this->RequestHandler->isFlash()); + unset($_SERVER['HTTP_USER_AGENT'], $_SERVER['HTTP_X_REQUESTED_WITH']); + + $this->assertFalse($this->RequestHandler->isAjax()); + $_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'; + $_SERVER['HTTP_X_PROTOTYPE_VERSION'] = '1.5'; + $this->assertTrue($this->RequestHandler->isAjax()); + $this->assertEqual($this->RequestHandler->getAjaxVersion(), '1.5'); + + unset($_SERVER['HTTP_X_REQUESTED_WITH'], $_SERVER['HTTP_X_PROTOTYPE_VERSION']); + $this->assertFalse($this->RequestHandler->isAjax()); + $this->assertFalse($this->RequestHandler->getAjaxVersion()); + } +/** + * Tests the detection of various Flash versions + * + * @access public + * @return void + */ + function testFlashDetection() { + $_agent = env('HTTP_USER_AGENT'); + $_SERVER['HTTP_USER_AGENT'] = 'Shockwave Flash'; + $this->assertTrue($this->RequestHandler->isFlash()); + + $_SERVER['HTTP_USER_AGENT'] = 'Adobe Flash'; + $this->assertTrue($this->RequestHandler->isFlash()); + + $_SERVER['HTTP_USER_AGENT'] = 'Adobe Flash Player 9'; + $this->assertTrue($this->RequestHandler->isFlash()); + + $_SERVER['HTTP_USER_AGENT'] = 'Adobe Flash Player 10'; + $this->assertTrue($this->RequestHandler->isFlash()); + + $_SERVER['HTTP_USER_AGENT'] = 'Shock Flash'; + $this->assertFalse($this->RequestHandler->isFlash()); + + $_SERVER['HTTP_USER_AGENT'] = $_agent; + } +/** + * testRequestContentTypes method + * + * @access public + * @return void + */ + function testRequestContentTypes() { + $_SERVER['REQUEST_METHOD'] = 'GET'; + $this->assertNull($this->RequestHandler->requestedWith()); + + $_SERVER['REQUEST_METHOD'] = 'POST'; + $_SERVER['CONTENT_TYPE'] = 'application/json'; + $this->assertEqual($this->RequestHandler->requestedWith(), 'json'); + + $result = $this->RequestHandler->requestedWith(array('json', 'xml')); + $this->assertEqual($result, 'json'); + + $result =$this->RequestHandler->requestedWith(array('rss', 'atom')); + $this->assertFalse($result); + + $_SERVER['HTTP_ACCEPT'] = 'text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,*/*'; + $this->_init(); + $this->assertTrue($this->RequestHandler->isXml()); + $this->assertFalse($this->RequestHandler->isAtom()); + $this->assertFalse($this->RequestHandler->isRSS()); + + $_SERVER['HTTP_ACCEPT'] = 'application/atom+xml,text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,*/*'; + $this->_init(); + $this->assertTrue($this->RequestHandler->isAtom()); + $this->assertFalse($this->RequestHandler->isRSS()); + + $_SERVER['HTTP_ACCEPT'] = 'application/rss+xml,text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,*/*'; + $this->_init(); + $this->assertFalse($this->RequestHandler->isAtom()); + $this->assertTrue($this->RequestHandler->isRSS()); + + $this->assertFalse($this->RequestHandler->isWap()); + $_SERVER['HTTP_ACCEPT'] = 'text/vnd.wap.wml,text/html,text/plain,image/png,*/*'; + $this->_init(); + $this->assertTrue($this->RequestHandler->isWap()); + + $_SERVER['HTTP_ACCEPT'] = 'application/rss+xml,text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,*/*'; + } +/** + * testResponseContentType method + * + * @access public + * @return void + */ + function testResponseContentType() { + $this->assertNull($this->RequestHandler->responseType()); + $this->assertTrue($this->RequestHandler->respondAs('atom')); + $this->assertEqual($this->RequestHandler->responseType(), 'atom'); + } +/** + * testMobileDeviceDetection method + * + * @access public + * @return void + */ + function testMobileDeviceDetection() { + $this->assertFalse($this->RequestHandler->isMobile()); + $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A543a Safari/419.3'; + $this->assertTrue($this->RequestHandler->isMobile()); + } +/** + * testRequestProperties method + * + * @access public + * @return void + */ + function testRequestProperties() { + $_SERVER['HTTPS'] = 'on'; + $this->assertTrue($this->RequestHandler->isSSL()); + + unset($_SERVER['HTTPS']); + $this->assertFalse($this->RequestHandler->isSSL()); + + $_ENV['SCRIPT_URI'] = 'https://localhost/'; + $s = $_SERVER; + $_SERVER = array(); + $this->assertTrue($this->RequestHandler->isSSL()); + $_SERVER = $s; + } +/** + * testRequestMethod method + * + * @access public + * @return void + */ + function testRequestMethod() { + $_SERVER['REQUEST_METHOD'] = 'GET'; + $this->assertTrue($this->RequestHandler->isGet()); + $this->assertFalse($this->RequestHandler->isPost()); + $this->assertFalse($this->RequestHandler->isPut()); + $this->assertFalse($this->RequestHandler->isDelete()); + + $_SERVER['REQUEST_METHOD'] = 'POST'; + $this->assertFalse($this->RequestHandler->isGet()); + $this->assertTrue($this->RequestHandler->isPost()); + $this->assertFalse($this->RequestHandler->isPut()); + $this->assertFalse($this->RequestHandler->isDelete()); + + $_SERVER['REQUEST_METHOD'] = 'PUT'; + $this->assertFalse($this->RequestHandler->isGet()); + $this->assertFalse($this->RequestHandler->isPost()); + $this->assertTrue($this->RequestHandler->isPut()); + $this->assertFalse($this->RequestHandler->isDelete()); + + $_SERVER['REQUEST_METHOD'] = 'DELETE'; + $this->assertFalse($this->RequestHandler->isGet()); + $this->assertFalse($this->RequestHandler->isPost()); + $this->assertFalse($this->RequestHandler->isPut()); + $this->assertTrue($this->RequestHandler->isDelete()); + } +/** + * testClientContentPreference method + * + * @access public + * @return void + */ + function testClientContentPreference() { + $_SERVER['HTTP_ACCEPT'] = 'text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,*/*'; + $this->_init(); + $this->assertNotEqual($this->RequestHandler->prefers(), 'rss'); + $this->RequestHandler->ext = 'rss'; + $this->assertEqual($this->RequestHandler->prefers(), 'rss'); + $this->assertFalse($this->RequestHandler->prefers('xml')); + $this->assertEqual($this->RequestHandler->prefers(array('js', 'xml', 'xhtml')), 'xml'); + $this->assertTrue($this->RequestHandler->accepts('xml')); + + $_SERVER['HTTP_ACCEPT'] = 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5'; + $this->_init(); + $this->assertEqual($this->RequestHandler->prefers(), 'xml'); + $this->assertEqual($this->RequestHandler->accepts(array('js', 'xml', 'html')), 'xml'); + $this->assertFalse($this->RequestHandler->accepts(array('gif', 'jpeg', 'foo'))); + + $_SERVER['HTTP_ACCEPT'] = '*/*;q=0.5'; + $this->_init(); + $this->assertEqual($this->RequestHandler->prefers(), 'html'); + $this->assertFalse($this->RequestHandler->prefers('rss')); + $this->assertFalse($this->RequestHandler->accepts('rss')); + } +/** + * testCustomContent method + * + * @access public + * @return void + */ + function testCustomContent() { + $_SERVER['HTTP_ACCEPT'] = 'text/x-mobile,text/html;q=0.9,text/plain;q=0.8,*/*;q=0.5'; + $this->_init(); + $this->RequestHandler->setContent('mobile', 'text/x-mobile'); + $this->RequestHandler->startup($this->Controller); + $this->assertEqual($this->RequestHandler->prefers(), 'mobile'); + + $this->_init(); + $this->RequestHandler->setContent(array('mobile' => 'text/x-mobile')); + $this->RequestHandler->startup($this->Controller); + $this->assertEqual($this->RequestHandler->prefers(), 'mobile'); + } +/** + * testClientProperties method + * + * @access public + * @return void + */ + function testClientProperties() { + $_SERVER['HTTP_HOST'] = 'localhost:80'; + $this->assertEqual($this->RequestHandler->getReferrer(), 'localhost'); + $_SERVER['HTTP_HOST'] = null; + $_SERVER['HTTP_X_FORWARDED_HOST'] = 'cakephp.org'; + $this->assertEqual($this->RequestHandler->getReferrer(), 'cakephp.org'); + + $_SERVER['HTTP_X_FORWARDED_FOR'] = '192.168.1.5, 10.0.1.1, proxy.com'; + $_SERVER['HTTP_CLIENT_IP'] = '192.168.1.2'; + $_SERVER['REMOTE_ADDR'] = '192.168.1.3'; + $this->assertEqual($this->RequestHandler->getClientIP(false), '192.168.1.5'); + $this->assertEqual($this->RequestHandler->getClientIP(), '192.168.1.2'); + + unset($_SERVER['HTTP_X_FORWARDED_FOR']); + $this->assertEqual($this->RequestHandler->getClientIP(), '192.168.1.2'); + + unset($_SERVER['HTTP_CLIENT_IP']); + $this->assertEqual($this->RequestHandler->getClientIP(), '192.168.1.3'); + + $_SERVER['HTTP_CLIENTADDRESS'] = '10.0.1.2, 10.0.1.1'; + $this->assertEqual($this->RequestHandler->getClientIP(), '10.0.1.2'); + } +/** + * test that ajax requests involving redirects trigger requestAction instead. + * + * @return void + **/ + function testAjaxRedirectAsRequestAction() { + $_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'; + $this->_init(); + $_paths = Configure::read('viewPaths'); + $testDir = array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS); + Configure::write('viewPaths', array_merge($testDir, $_paths)); + + $this->Controller->RequestHandler = new NoStopRequestHandler($this); + $this->Controller->RequestHandler->expectOnce('_stop'); + + ob_start(); + $this->Controller->RequestHandler->beforeRedirect( + $this->Controller, array('controller' => 'request_handler_test', 'action' => 'destination') + ); + $result = ob_get_clean(); + $this->assertPattern('/posts index/', $result, 'RequestAction redirect failed.'); + + Configure::write('viewPaths', $_paths); + unset($_SERVER['HTTP_X_REQUESTED_WITH']); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/controller/components/security.test.php b/cake/tests/cases/libs/controller/components/security.test.php new file mode 100755 index 0000000..f744f2d --- /dev/null +++ b/cake/tests/cases/libs/controller/components/security.test.php @@ -0,0 +1,1105 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * SecurityComponentTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs.controller.components + * @since CakePHP(tm) v 1.2.0.5435 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +App::import('Component', 'Security'); +/** +* TestSecurityComponent +* +* @package cake +* @subpackage cake.tests.cases.libs.controller.components +*/ +class TestSecurityComponent extends SecurityComponent { +/** + * validatePost method + * + * @param Controller $controller + * @return unknown + */ + function validatePost(&$controller) { + return $this->_validatePost($controller); + } +} +/** +* SecurityTestController +* +* @package cake +* @subpackage cake.tests.cases.libs.controller.components +*/ +class SecurityTestController extends Controller { +/** + * name property + * + * @var string 'SecurityTest' + * @access public + */ + var $name = 'SecurityTest'; +/** + * components property + * + * @var array + * @access public + */ + var $components = array('TestSecurity'); +/** + * failed property + * + * @var bool false + * @access public + */ + var $failed = false; +/** + * Used for keeping track of headers in test + * + * @var array + * @access public + */ + var $testHeaders = array(); +/** + * fail method + * + * @access public + * @return void + */ + function fail() { + $this->failed = true; + } +/** + * redirect method + * + * @param mixed $option + * @param mixed $code + * @param mixed $exit + * @access public + * @return void + */ + function redirect($option, $code, $exit) { + return $code; + } +/** + * Conveinence method for header() + * + * @param string $status + * @return void + * @access public + */ + function header($status) { + $this->testHeaders[] = $status; + } +} +/** + * SecurityComponentTest class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller.components + */ +class SecurityComponentTest extends CakeTestCase { +/** + * Controller property + * + * @var SecurityTestController + * @access public + */ + var $Controller; +/** + * oldSalt property + * + * @var string + * @access public + */ + var $oldSalt; +/** + * setUp method + * + * @access public + * @return void + */ + function setUp() { + $this->Controller =& new SecurityTestController(); + $this->Controller->Component->init($this->Controller); + $this->Controller->Security =& $this->Controller->TestSecurity; + $this->Controller->Security->blackHoleCallback = 'fail'; + $this->oldSalt = Configure::read('Security.salt'); + Configure::write('Security.salt', 'foo!'); + } +/** + * Tear-down method. Resets environment state. + * + * @access public + * @return void + */ + function tearDown() { + Configure::write('Security.salt', $this->oldSalt); + $this->Controller->Session->del('_Token'); + unset($this->Controller->Security); + unset($this->Controller->Component); + unset($this->Controller); + } +/** + * testStartup method + * + * @access public + * @return void + */ + function testStartup() { + $this->Controller->Security->startup($this->Controller); + $result = $this->Controller->params['_Token']['key']; + $this->assertNotNull($result); + $this->assertTrue($this->Controller->Session->check('_Token')); + } +/** + * testRequirePostFail method + * + * @access public + * @return void + */ + function testRequirePostFail() { + $_SERVER['REQUEST_METHOD'] = 'GET'; + $this->Controller->action = 'posted'; + $this->Controller->Security->requirePost('posted'); + $this->Controller->Security->startup($this->Controller); + $this->assertTrue($this->Controller->failed); + } +/** + * testRequirePostSucceed method + * + * @access public + * @return void + */ + function testRequirePostSucceed() { + $_SERVER['REQUEST_METHOD'] = 'POST'; + $this->Controller->action = 'posted'; + $this->Controller->Security->requirePost('posted'); + $this->Controller->Security->startup($this->Controller); + $this->assertFalse($this->Controller->failed); + } +/** + * testRequireSecureFail method + * + * @access public + * @return void + */ + function testRequireSecureFail() { + $_SERVER['REQUEST_METHOD'] = 'POST'; + $this->Controller->action = 'posted'; + $this->Controller->Security->requireSecure('posted'); + $this->Controller->Security->startup($this->Controller); + $this->assertTrue($this->Controller->failed); + } +/** + * testRequireSecureSucceed method + * + * @access public + * @return void + */ + function testRequireSecureSucceed() { + $_SERVER['REQUEST_METHOD'] = 'Secure'; + $this->Controller->action = 'posted'; + $_SERVER['HTTPS'] = 'on'; + $this->Controller->Security->requireSecure('posted'); + $this->Controller->Security->startup($this->Controller); + $this->assertFalse($this->Controller->failed); + } +/** + * testRequireAuthFail method + * + * @access public + * @return void + */ + function testRequireAuthFail() { + $_SERVER['REQUEST_METHOD'] = 'AUTH'; + $this->Controller->action = 'posted'; + $this->Controller->data = array('username' => 'willy', 'password' => 'somePass'); + $this->Controller->Security->requireAuth('posted'); + $this->Controller->Security->startup($this->Controller); + $this->assertTrue($this->Controller->failed); + + $this->Controller->Session->write('_Token', array('allowedControllers' => array())); + $this->Controller->data = array('username' => 'willy', 'password' => 'somePass'); + $this->Controller->action = 'posted'; + $this->Controller->Security->requireAuth('posted'); + $this->Controller->Security->startup($this->Controller); + $this->assertTrue($this->Controller->failed); + + $this->Controller->Session->write('_Token', array( + 'allowedControllers' => array('SecurityTest'), 'allowedActions' => array('posted2') + )); + $this->Controller->data = array('username' => 'willy', 'password' => 'somePass'); + $this->Controller->action = 'posted'; + $this->Controller->Security->requireAuth('posted'); + $this->Controller->Security->startup($this->Controller); + $this->assertTrue($this->Controller->failed); + } +/** + * testRequireAuthSucceed method + * + * @access public + * @return void + */ + function testRequireAuthSucceed() { + $_SERVER['REQUEST_METHOD'] = 'AUTH'; + $this->Controller->action = 'posted'; + $this->Controller->Security->requireAuth('posted'); + $this->Controller->Security->startup($this->Controller); + $this->assertFalse($this->Controller->failed); + + $this->Controller->Security->Session->write('_Token', serialize(array( + 'allowedControllers' => array('SecurityTest'), 'allowedActions' => array('posted') + ))); + $this->Controller->params['controller'] = 'SecurityTest'; + $this->Controller->params['action'] = 'posted'; + + $this->Controller->data = array( + 'username' => 'willy', 'password' => 'somePass', '_Token' => '' + ); + $this->Controller->action = 'posted'; + $this->Controller->Security->requireAuth('posted'); + $this->Controller->Security->startup($this->Controller); + $this->assertFalse($this->Controller->failed); + } +/** + * testRequirePostSucceedWrongMethod method + * + * @access public + * @return void + */ + function testRequirePostSucceedWrongMethod() { + $_SERVER['REQUEST_METHOD'] = 'GET'; + $this->Controller->action = 'getted'; + $this->Controller->Security->requirePost('posted'); + $this->Controller->Security->startup($this->Controller); + $this->assertFalse($this->Controller->failed); + } +/** + * testRequireGetFail method + * + * @access public + * @return void + */ + function testRequireGetFail() { + $_SERVER['REQUEST_METHOD'] = 'POST'; + $this->Controller->action = 'getted'; + $this->Controller->Security->requireGet('getted'); + $this->Controller->Security->startup($this->Controller); + $this->assertTrue($this->Controller->failed); + } +/** + * testRequireGetSucceed method + * + * @access public + * @return void + */ + function testRequireGetSucceed() { + $_SERVER['REQUEST_METHOD'] = 'GET'; + $this->Controller->action = 'getted'; + $this->Controller->Security->requireGet('getted'); + $this->Controller->Security->startup($this->Controller); + $this->assertFalse($this->Controller->failed); + } +/** + * testRequireLogin method + * + * @access public + * @return void + */ + function testRequireLogin() { + $this->Controller->action = 'posted'; + $this->Controller->Security->requireLogin( + 'posted', + array('type' => 'basic', 'users' => array('admin' => 'password')) + ); + $_SERVER['PHP_AUTH_USER'] = 'admin'; + $_SERVER['PHP_AUTH_PW'] = 'password'; + $this->Controller->Security->startup($this->Controller); + $this->assertFalse($this->Controller->failed); + + + $this->Controller->action = 'posted'; + $this->Controller->Security->requireLogin( + 'posted', + array('type' => 'basic', 'users' => array('admin' => 'password')) + ); + $_SERVER['PHP_AUTH_USER'] = 'admin2'; + $_SERVER['PHP_AUTH_PW'] = 'password'; + $this->Controller->Security->startup($this->Controller); + $this->assertTrue($this->Controller->failed); + + $this->Controller->action = 'posted'; + $this->Controller->Security->requireLogin( + 'posted', + array('type' => 'basic', 'users' => array('admin' => 'password')) + ); + $_SERVER['PHP_AUTH_USER'] = 'admin'; + $_SERVER['PHP_AUTH_PW'] = 'password2'; + $this->Controller->Security->startup($this->Controller); + $this->assertTrue($this->Controller->failed); + } +/** + * testDigestAuth method + * + * @access public + * @return void + */ + function testDigestAuth() { + $skip = $this->skipIf((version_compare(PHP_VERSION, '5.1') == -1) XOR (!function_exists('apache_request_headers')), + "%s Cannot run Digest Auth test for PHP versions < 5.1" + ); + + if ($skip) { + return; + } + + $this->Controller->action = 'posted'; + $_SERVER['PHP_AUTH_DIGEST'] = $digest = <<<DIGEST + Digest username="Mufasa", + realm="testrealm@host.com", + nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", + uri="/dir/index.html", + qop=auth, + nc=00000001, + cnonce="0a4f113b", + response="460d0d3c6867c2f1ab85b1ada1aece48", + opaque="5ccc069c403ebaf9f0171e9517f40e41" +DIGEST; + $this->Controller->Security->requireLogin('posted', array( + 'type' => 'digest', 'users' => array('Mufasa' => 'password'), + 'realm' => 'testrealm@host.com' + )); + $this->Controller->Security->startup($this->Controller); + $this->assertFalse($this->Controller->failed); + } +/** + * testRequireGetSucceedWrongMethod method + * + * @access public + * @return void + */ + function testRequireGetSucceedWrongMethod() { + $_SERVER['REQUEST_METHOD'] = 'POST'; + $this->Controller->action = 'posted'; + $this->Controller->Security->requireGet('getted'); + $this->Controller->Security->startup($this->Controller); + $this->assertFalse($this->Controller->failed); + } +/** + * testRequirePutFail method + * + * @access public + * @return void + */ + function testRequirePutFail() { + $_SERVER['REQUEST_METHOD'] = 'POST'; + $this->Controller->action = 'putted'; + $this->Controller->Security->requirePut('putted'); + $this->Controller->Security->startup($this->Controller); + $this->assertTrue($this->Controller->failed); + } +/** + * testRequirePutSucceed method + * + * @access public + * @return void + */ + function testRequirePutSucceed() { + $_SERVER['REQUEST_METHOD'] = 'PUT'; + $this->Controller->action = 'putted'; + $this->Controller->Security->requirePut('putted'); + $this->Controller->Security->startup($this->Controller); + $this->assertFalse($this->Controller->failed); + } +/** + * testRequirePutSucceedWrongMethod method + * + * @access public + * @return void + */ + function testRequirePutSucceedWrongMethod() { + $_SERVER['REQUEST_METHOD'] = 'POST'; + $this->Controller->action = 'posted'; + $this->Controller->Security->requirePut('putted'); + $this->Controller->Security->startup($this->Controller); + $this->assertFalse($this->Controller->failed); + } +/** + * testRequireDeleteFail method + * + * @access public + * @return void + */ + function testRequireDeleteFail() { + $_SERVER['REQUEST_METHOD'] = 'POST'; + $this->Controller->action = 'deleted'; + $this->Controller->Security->requireDelete('deleted'); + $this->Controller->Security->startup($this->Controller); + $this->assertTrue($this->Controller->failed); + } +/** + * testRequireDeleteSucceed method + * + * @access public + * @return void + */ + function testRequireDeleteSucceed() { + $_SERVER['REQUEST_METHOD'] = 'DELETE'; + $this->Controller->action = 'deleted'; + $this->Controller->Security->requireDelete('deleted'); + $this->Controller->Security->startup($this->Controller); + $this->assertFalse($this->Controller->failed); + } +/** + * testRequireDeleteSucceedWrongMethod method + * + * @access public + * @return void + */ + function testRequireDeleteSucceedWrongMethod() { + $_SERVER['REQUEST_METHOD'] = 'POST'; + $this->Controller->action = 'posted'; + $this->Controller->Security->requireDelete('deleted'); + $this->Controller->Security->startup($this->Controller); + $this->assertFalse($this->Controller->failed); + } +/** + * testRequireLoginSettings method + * + * @access public + * @return void + */ + function testRequireLoginSettings() { + $this->Controller->Security->requireLogin( + 'add', 'edit', + array('type' => 'basic', 'users' => array('admin' => 'password')) + ); + $this->assertEqual($this->Controller->Security->requireLogin, array('add', 'edit')); + $this->assertEqual($this->Controller->Security->loginUsers, array('admin' => 'password')); + } +/** + * testRequireLoginAllActions method + * + * @access public + * @return void + */ + function testRequireLoginAllActions() { + $this->Controller->Security->requireLogin( + array('type' => 'basic', 'users' => array('admin' => 'password')) + ); + $this->assertEqual($this->Controller->Security->requireLogin, array('*')); + $this->assertEqual($this->Controller->Security->loginUsers, array('admin' => 'password')); + } +/** + * Simple hash validation test + * + * @access public + * @return void + */ + function testValidatePost() { + $this->Controller->Security->startup($this->Controller); + $key = $this->Controller->params['_Token']['key']; + $fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877%3An%3A1%3A%7Bv%3A0%3B'; + $fields .= 'f%3A11%3A%22Zbqry.inyvq%22%3B%7D'; + + $this->Controller->data = array( + 'Model' => array('username' => 'nate', 'password' => 'foo', 'valid' => '0'), + '_Token' => compact('key', 'fields') + ); + $this->assertTrue($this->Controller->Security->validatePost($this->Controller)); + } +/** + * Tests validation of checkbox arrays + * + * @access public + * @return void + */ + function testValidatePostArray() { + $this->Controller->Security->startup($this->Controller); + $key = $this->Controller->params['_Token']['key']; + $fields = 'f7d573650a295b94e0938d32b323fde775e5f32b%3An%3A0%3A%7B%7D'; + + $this->Controller->data = array( + 'Model' => array('multi_field' => array('1', '3')), + '_Token' => compact('key', 'fields') + ); + $this->assertTrue($this->Controller->Security->validatePost($this->Controller)); + } +/** + * testValidatePostNoModel method + * + * @access public + * @return void + */ + function testValidatePostNoModel() { + $this->Controller->Security->startup($this->Controller); + $key = $this->Controller->params['_Token']['key']; + $fields = '540ac9c60d323c22bafe997b72c0790f39a8bdef%3An%3A0%3A%7B%7D'; + + $this->Controller->data = array( + 'anything' => 'some_data', + '_Token' => compact('key', 'fields') + ); + + $result = $this->Controller->Security->validatePost($this->Controller); + $this->assertTrue($result); + } +/** + * testValidatePostSimple method + * + * @access public + * @return void + */ + function testValidatePostSimple() { + $this->Controller->Security->startup($this->Controller); + $key = $this->Controller->params['_Token']['key']; + $fields = '69f493434187b867ea14b901fdf58b55d27c935d%3An%3A0%3A%7B%7D'; + + $this->Controller->data = $data = array( + 'Model' => array('username' => '', 'password' => ''), + '_Token' => compact('key', 'fields') + ); + + $result = $this->Controller->Security->validatePost($this->Controller); + $this->assertTrue($result); + } +/** + * Tests hash validation for multiple records, including locked fields + * + * @access public + * @return void + */ + function testValidatePostComplex() { + $this->Controller->Security->startup($this->Controller); + $key = $this->Controller->params['_Token']['key']; + $fields = 'c9118120e680a7201b543f562e5301006ccfcbe2%3An%3A2%3A%7Bv%3A0%3Bf%3A14%3A%'; + $fields .= '22Nqqerffrf.0.vq%22%3Bv%3A1%3Bf%3A14%3A%22Nqqerffrf.1.vq%22%3B%7D'; + + $this->Controller->data = array( + 'Addresses' => array( + '0' => array( + 'id' => '123456', 'title' => '', 'first_name' => '', 'last_name' => '', + 'address' => '', 'city' => '', 'phone' => '', 'primary' => '' + ), + '1' => array( + 'id' => '654321', 'title' => '', 'first_name' => '', 'last_name' => '', + 'address' => '', 'city' => '', 'phone' => '', 'primary' => '' + ) + ), + '_Token' => compact('key', 'fields') + ); + $result = $this->Controller->Security->validatePost($this->Controller); + $this->assertTrue($result); + } +/** + * test ValidatePost with multiple select elements. + * + * @return void + **/ + function testValidatePostMultipleSelect() { + $this->Controller->Security->startup($this->Controller); + $key = $this->Controller->params['_Token']['key']; + $fields = '422cde416475abc171568be690a98cad20e66079%3An%3A0%3A%7B%7D'; + + $this->Controller->data = array( + 'Tag' => array('Tag' => array(1, 2)), + '_Token' => compact('key', 'fields'), + ); + $result = $this->Controller->Security->validatePost($this->Controller); + $this->assertTrue($result); + + $this->Controller->data = array( + 'Tag' => array('Tag' => array(1, 2, 3)), + '_Token' => compact('key', 'fields'), + ); + $result = $this->Controller->Security->validatePost($this->Controller); + $this->assertTrue($result); + + $this->Controller->data = array( + 'Tag' => array('Tag' => array(1, 2, 3, 4)), + '_Token' => compact('key', 'fields'), + ); + $result = $this->Controller->Security->validatePost($this->Controller); + $this->assertTrue($result); + + $fields = '19464422eafe977ee729c59222af07f983010c5f%3An%3A0%3A%7B%7D'; + $this->Controller->data = array( + 'User.password' => 'bar', 'User.name' => 'foo', 'User.is_valid' => '1', + 'Tag' => array('Tag' => array(1)), '_Token' => compact('key', 'fields'), + ); + $result = $this->Controller->Security->validatePost($this->Controller); + $this->assertTrue($result); + } +/** + * testValidatePostCheckbox method + * + * First block tests un-checked checkbox + * Second block tests checked checkbox + * + * @access public + * @return void + */ + function testValidatePostCheckbox() { + $this->Controller->Security->startup($this->Controller); + $key = $this->Controller->params['_Token']['key']; + $fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877%3An%3A1%3A%7Bv%3A0%'; + $fields .= '3Bf%3A11%3A%22Zbqry.inyvq%22%3B%7D'; + + $this->Controller->data = array( + 'Model' => array('username' => '', 'password' => '', 'valid' => '0'), + '_Token' => compact('key', 'fields') + ); + + $result = $this->Controller->Security->validatePost($this->Controller); + $this->assertTrue($result); + + $fields = '874439ca69f89b4c4a5f50fb9c36ff56a28f5d42%3An%3A0%3A%7B%7D'; + + $this->Controller->data = array( + 'Model' => array('username' => '', 'password' => '', 'valid' => '0'), + '_Token' => compact('key', 'fields') + ); + + $result = $this->Controller->Security->validatePost($this->Controller); + $this->assertTrue($result); + + + $this->Controller->data = array(); + $this->Controller->Security->startup($this->Controller); + $key = $this->Controller->params['_Token']['key']; + + $this->Controller->data = $data = array( + 'Model' => array('username' => '', 'password' => '', 'valid' => '0'), + '_Token' => compact('key', 'fields') + ); + + $result = $this->Controller->Security->validatePost($this->Controller); + $this->assertTrue($result); + } +/** + * testValidatePostHidden method + * + * @access public + * @return void + */ + function testValidatePostHidden() { + $this->Controller->Security->startup($this->Controller); + $key = $this->Controller->params['_Token']['key']; + $fields = '51ccd8cb0997c7b3d4523ecde5a109318405ef8c%3An%3A2%3A%7Bv%3A0%3Bf%3A12%3A'; + $fields .= '%22Zbqry.uvqqra%22%3Bv%3A1%3Bf%3A18%3A%22Zbqry.bgure_uvqqra%22%3B%7D'; + + $this->Controller->data = array( + 'Model' => array( + 'username' => '', 'password' => '', 'hidden' => '0', + 'other_hidden' => 'some hidden value' + ), + '_Token' => compact('key', 'fields') + ); + $result = $this->Controller->Security->validatePost($this->Controller); + $this->assertTrue($result); + } +/** + * testValidatePostWithDisabledFields method + * + * @access public + * @return void + */ + function testValidatePostWithDisabledFields() { + $this->Controller->Security->disabledFields = array('Model.username', 'Model.password'); + $this->Controller->Security->startup($this->Controller); + $key = $this->Controller->params['_Token']['key']; + $fields = 'ef1082968c449397bcd849f963636864383278b1%3An%3A1%3A%7Bv%'; + $fields .= '3A0%3Bf%3A12%3A%22Zbqry.uvqqra%22%3B%7D'; + + $this->Controller->data = array( + 'Model' => array( + 'username' => '', 'password' => '', 'hidden' => '0' + ), + '_Token' => compact('fields', 'key') + ); + + $result = $this->Controller->Security->validatePost($this->Controller); + $this->assertTrue($result); + } +/** + * testValidateHiddenMultipleModel method + * + * @access public + * @return void + */ + function testValidateHiddenMultipleModel() { + $this->Controller->Security->startup($this->Controller); + $key = $this->Controller->params['_Token']['key']; + $fields = 'a2d01072dc4660eea9d15007025f35a7a5b58e18%3An%3A3%3A%7Bv%3A0%3Bf%3A11'; + $fields .= '%3A%22Zbqry.inyvq%22%3Bv%3A1%3Bf%3A12%3A%22Zbqry2.inyvq%22%3Bv%3A2%'; + $fields .= '3Bf%3A12%3A%22Zbqry3.inyvq%22%3B%7D'; + + $this->Controller->data = array( + 'Model' => array('username' => '', 'password' => '', 'valid' => '0'), + 'Model2' => array('valid' => '0'), + 'Model3' => array('valid' => '0'), + '_Token' => compact('key', 'fields') + ); + $result = $this->Controller->Security->validatePost($this->Controller); + $this->assertTrue($result); + } +/** + * testLoginValidation method + * + * @access public + * @return void + */ + function testLoginValidation() { + + } +/** + * testValidateHasManyModel method + * + * @access public + * @return void + */ + function testValidateHasManyModel() { + $this->Controller->Security->startup($this->Controller); + $key = $this->Controller->params['_Token']['key']; + $fields = '51e3b55a6edd82020b3f29c9ae200e14bbeb7ee5%3An%3A4%3A%7Bv%3A0%3Bf%3A14%3A%2'; + $fields .= '2Zbqry.0.uvqqra%22%3Bv%3A1%3Bf%3A13%3A%22Zbqry.0.inyvq%22%3Bv%3A2%3Bf%3'; + $fields .= 'A14%3A%22Zbqry.1.uvqqra%22%3Bv%3A3%3Bf%3A13%3A%22Zbqry.1.inyvq%22%3B%7D'; + + $this->Controller->data = array( + 'Model' => array( + array( + 'username' => 'username', 'password' => 'password', + 'hidden' => 'value', 'valid' => '0' + ), + array( + 'username' => 'username', 'password' => 'password', + 'hidden' => 'value', 'valid' => '0' + ) + ), + '_Token' => compact('key', 'fields') + ); + + $result = $this->Controller->Security->validatePost($this->Controller); + $this->assertTrue($result); + } +/** + * testValidateHasManyRecordsPass method + * + * @access public + * @return void + */ + function testValidateHasManyRecordsPass() { + $this->Controller->Security->startup($this->Controller); + $key = $this->Controller->params['_Token']['key']; + $fields = '7a203edb3d345bbf38fe0dccae960da8842e11d7%3An%3A4%3A%7Bv%3A0%3Bf%3A12%3A%2'; + $fields .= '2Nqqerff.0.vq%22%3Bv%3A1%3Bf%3A17%3A%22Nqqerff.0.cevznel%22%3Bv%3A2%3Bf%'; + $fields .= '3A12%3A%22Nqqerff.1.vq%22%3Bv%3A3%3Bf%3A17%3A%22Nqqerff.1.cevznel%22%3B%7D'; + + $this->Controller->data = array( + 'Address' => array( + 0 => array( + 'id' => '123', + 'title' => 'home', + 'first_name' => 'Bilbo', + 'last_name' => 'Baggins', + 'address' => '23 Bag end way', + 'city' => 'the shire', + 'phone' => 'N/A', + 'primary' => '1', + ), + 1 => array( + 'id' => '124', + 'title' => 'home', + 'first_name' => 'Frodo', + 'last_name' => 'Baggins', + 'address' => '50 Bag end way', + 'city' => 'the shire', + 'phone' => 'N/A', + 'primary' => '1' + ) + ), + '_Token' => compact('key', 'fields') + ); + + $result = $this->Controller->Security->validatePost($this->Controller); + $this->assertTrue($result); + } +/** + * testValidateHasManyRecords method + * + * validatePost should fail, hidden fields have been changed. + * + * @access public + * @return void + */ + function testValidateHasManyRecordsFail() { + $this->Controller->Security->startup($this->Controller); + $key = $this->Controller->params['_Token']['key']; + $fields = '7a203edb3d345bbf38fe0dccae960da8842e11d7%3An%3A4%3A%7Bv%3A0%3Bf%3A12%3A%2'; + $fields .= '2Nqqerff.0.vq%22%3Bv%3A1%3Bf%3A17%3A%22Nqqerff.0.cevznel%22%3Bv%3A2%3Bf%'; + $fields .= '3A12%3A%22Nqqerff.1.vq%22%3Bv%3A3%3Bf%3A17%3A%22Nqqerff.1.cevznel%22%3B%7D'; + + $this->Controller->data = array( + 'Address' => array( + 0 => array( + 'id' => '123', + 'title' => 'home', + 'first_name' => 'Bilbo', + 'last_name' => 'Baggins', + 'address' => '23 Bag end way', + 'city' => 'the shire', + 'phone' => 'N/A', + 'primary' => '5', + ), + 1 => array( + 'id' => '124', + 'title' => 'home', + 'first_name' => 'Frodo', + 'last_name' => 'Baggins', + 'address' => '50 Bag end way', + 'city' => 'the shire', + 'phone' => 'N/A', + 'primary' => '1' + ) + ), + '_Token' => compact('key', 'fields') + ); + + $result = $this->Controller->Security->validatePost($this->Controller); + $this->assertFalse($result); + } +/** + * testLoginRequest method + * + * @access public + * @return void + */ + function testLoginRequest() { + $this->Controller->Security->startup($this->Controller); + $realm = 'cakephp.org'; + $options = array('realm' => $realm, 'type' => 'basic'); + $result = $this->Controller->Security->loginRequest($options); + $expected = 'WWW-Authenticate: Basic realm="'.$realm.'"'; + $this->assertEqual($result, $expected); + + $this->Controller->Security->startup($this->Controller); + $options = array('realm' => $realm, 'type' => 'digest'); + $result = $this->Controller->Security->loginRequest($options); + $this->assertPattern('/realm="'.$realm.'"/', $result); + $this->assertPattern('/qop="auth"/', $result); + } +/** + * testGenerateDigestResponseHash method + * + * @access public + * @return void + */ + function testGenerateDigestResponseHash() { + $this->Controller->Security->startup($this->Controller); + $realm = 'cakephp.org'; + $loginData = array('realm' => $realm, 'users' => array('Willy Smith' => 'password')); + $this->Controller->Security->requireLogin($loginData); + + $data = array( + 'username' => 'Willy Smith', + 'password' => 'password', + 'nonce' => String::uuid(), + 'nc' => 1, + 'cnonce' => 1, + 'realm' => $realm, + 'uri' => 'path_to_identifier', + 'qop' => 'testme' + ); + $_SERVER['REQUEST_METHOD'] = 'POST'; + + $result = $this->Controller->Security->generateDigestResponseHash($data); + $expected = md5( + md5($data['username'] . ':' . $loginData['realm'] . ':' . $data['password']) . ':' . + $data['nonce'] . ':' . $data['nc'] . ':' . $data['cnonce'] . ':' . $data['qop'] . ':' . + md5(env('REQUEST_METHOD') . ':' . $data['uri']) + ); + $this->assertIdentical($result, $expected); + } +/** + * testLoginCredentials method + * + * @access public + * @return void + */ + function testLoginCredentials() { + $this->Controller->Security->startup($this->Controller); + $_SERVER['PHP_AUTH_USER'] = $user = 'Willy Test'; + $_SERVER['PHP_AUTH_PW'] = $pw = 'some password for the nice test'; + + $result = $this->Controller->Security->loginCredentials('basic'); + $expected = array('username' => $user, 'password' => $pw); + $this->assertIdentical($result, $expected); + + if (version_compare(PHP_VERSION, '5.1') != -1) { + $_SERVER['PHP_AUTH_DIGEST'] = $digest = <<<DIGEST + Digest username="Mufasa", + realm="testrealm@host.com", + nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", + uri="/dir/index.html", + qop=auth, + nc=00000001, + cnonce="0a4f113b", + response="6629fae49393a05397450978507c4ef1", + opaque="5ccc069c403ebaf9f0171e9517f40e41" +DIGEST; + $expected = array( + 'username' => 'Mufasa', + 'nonce' => 'dcd98b7102dd2f0e8b11d0f600bfb0c093', + 'uri' => '/dir/index.html', + 'qop' => 'auth', + 'nc' => '00000001', + 'cnonce' => '0a4f113b', + 'response' => '6629fae49393a05397450978507c4ef1', + 'opaque' => '5ccc069c403ebaf9f0171e9517f40e41' + ); + $result = $this->Controller->Security->loginCredentials('digest'); + $this->assertIdentical($result, $expected); + } + } +/** + * testParseDigestAuthData method + * + * @access public + * @return void + */ + function testParseDigestAuthData() { + $this->Controller->Security->startup($this->Controller); + $digest = <<<DIGEST + Digest username="Mufasa", + realm="testrealm@host.com", + nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", + uri="/dir/index.html", + qop=auth, + nc=00000001, + cnonce="0a4f113b", + response="6629fae49393a05397450978507c4ef1", + opaque="5ccc069c403ebaf9f0171e9517f40e41" +DIGEST; + $expected = array( + 'username' => 'Mufasa', + 'nonce' => 'dcd98b7102dd2f0e8b11d0f600bfb0c093', + 'uri' => '/dir/index.html', + 'qop' => 'auth', + 'nc' => '00000001', + 'cnonce' => '0a4f113b', + 'response' => '6629fae49393a05397450978507c4ef1', + 'opaque' => '5ccc069c403ebaf9f0171e9517f40e41' + ); + $result = $this->Controller->Security->parseDigestAuthData($digest); + $this->assertIdentical($result, $expected); + + $result = $this->Controller->Security->parseDigestAuthData(''); + $this->assertNull($result); + } +/** + * testFormDisabledFields method + * + * @access public + * @return void + */ + function testFormDisabledFields() { + $this->Controller->Security->startup($this->Controller); + $key = $this->Controller->params['_Token']['key']; + $fields = '11842060341b9d0fc3808b90ba29fdea7054d6ad%3An%3A0%3A%7B%7D'; + + $this->Controller->data = array( + 'MyModel' => array('name' => 'some data'), + '_Token' => compact('key', 'fields') + ); + $result = $this->Controller->Security->validatePost($this->Controller); + $this->assertFalse($result); + + $this->Controller->Security->startup($this->Controller); + $this->Controller->Security->disabledFields = array('MyModel.name'); + $key = $this->Controller->params['_Token']['key']; + + $this->Controller->data = array( + 'MyModel' => array('name' => 'some data'), + '_Token' => compact('key', 'fields') + ); + + $result = $this->Controller->Security->validatePost($this->Controller); + $this->assertTrue($result); + } +/** + * testRadio method + * + * @access public + * @return void + */ + function testRadio() { + $this->Controller->Security->startup($this->Controller); + $key = $this->Controller->params['_Token']['key']; + $fields = '575ef54ca4fc8cab468d6d898e9acd3a9671c17e%3An%3A0%3A%7B%7D'; + + $this->Controller->data = array( + '_Token' => compact('key', 'fields') + ); + $result = $this->Controller->Security->validatePost($this->Controller); + $this->assertFalse($result); + + $this->Controller->data = array( + '_Token' => compact('key', 'fields'), + 'Test' => array('test' => '') + ); + $result = $this->Controller->Security->validatePost($this->Controller); + $this->assertTrue($result); + + $this->Controller->data = array( + '_Token' => compact('key', 'fields'), + 'Test' => array('test' => '1') + ); + $result = $this->Controller->Security->validatePost($this->Controller); + $this->assertTrue($result); + + $this->Controller->data = array( + '_Token' => compact('key', 'fields'), + 'Test' => array('test' => '2') + ); + $result = $this->Controller->Security->validatePost($this->Controller); + $this->assertTrue($result); + } +/** + * testInvalidAuthHeaders method + * + * @access public + * @return void + */ + function testInvalidAuthHeaders() { + $this->Controller->Security->blackHoleCallback = null; + $_SERVER['PHP_AUTH_USER'] = 'admin'; + $_SERVER['PHP_AUTH_PW'] = 'password'; + $realm = 'cakephp.org'; + $loginData = array('type' => 'basic', 'realm' => $realm); + $this->Controller->Security->requireLogin($loginData); + $this->Controller->Security->startup($this->Controller); + + $expected = 'WWW-Authenticate: Basic realm="'.$realm.'"'; + $this->assertEqual(count($this->Controller->testHeaders), 1); + $this->assertEqual(current($this->Controller->testHeaders), $expected); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/controller/components/session.test.php b/cake/tests/cases/libs/controller/components/session.test.php new file mode 100755 index 0000000..b79f043 --- /dev/null +++ b/cake/tests/cases/libs/controller/components/session.test.php @@ -0,0 +1,372 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * SessionComponentTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs.controller.components + * @since CakePHP(tm) v 1.2.0.5436 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +App::import('Core', array('Controller', 'Object')); +App::import('Component', 'Session'); +/** + * SessionTestController class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller.components + */ +class SessionTestController extends Controller { +/** + * uses property + * + * @var array + * @access public + */ + var $uses = array(); +/** + * session_id method + * + * @return string + * @access public + */ + function session_id() { + return $this->Session->id(); + } +} +/** + * OrangeSessionTestController class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller.components + */ +class OrangeSessionTestController extends Controller { +/** + * uses property + * + * @var array + * @access public + */ + var $uses = array(); +/** + * session_id method + * + * @return string + * @access public + */ + function session_id() { + return $this->Session->id(); + } +} +/** + * SessionComponentTest class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller.components + */ +class SessionComponentTest extends CakeTestCase { +/** + * setUp method + * + * @access public + * @return void + */ + function setUp() { + $this->_session = Configure::read('Session'); + } +/** + * tearDown method + * + * @access public + * @return void + */ + function tearDown() { + Configure::write('Session', $this->_session); + } +/** + * testSessionAutoStart method + * + * @access public + * @return void + */ + function testSessionAutoStart() { + Configure::write('Session.start', false); + $Session =& new SessionComponent(); + $this->assertFalse($Session->__active); + $this->assertFalse($Session->__started); + $Session->startup(new SessionTestController()); + + Configure::write('Session.start', true); + $Session =& new SessionComponent(); + $this->assertTrue($Session->__active); + $this->assertFalse($Session->__started); + $Session->startup(new SessionTestController()); + $this->assertTrue(isset($_SESSION)); + + $Object = new Object(); + $Session =& new SessionComponent(); + $Session->start(); + $expected = $Session->id(); + + $result = $Object->requestAction('/session_test/session_id'); + $this->assertEqual($result, $expected); + + $result = $Object->requestAction('/orange_session_test/session_id'); + $this->assertEqual($result, $expected); + } +/** + * testSessionInitialize method + * + * @access public + * @return void + */ + function testSessionInitialize() { + $Session =& new SessionComponent(); + + $this->assertEqual($Session->__bare, 0); + + $Session->initialize(new SessionTestController()); + $this->assertEqual($Session->__bare, 0); + + $sessionController =& new SessionTestController(); + $sessionController->params['bare'] = 1; + $Session->initialize($sessionController); + $this->assertEqual($Session->__bare, 1); + } +/** + * testSessionActivate method + * + * @access public + * @return void + */ + function testSessionActivate() { + $Session =& new SessionComponent(); + + $this->assertTrue($Session->__active); + $this->assertNull($Session->activate()); + $this->assertTrue($Session->__active); + + Configure::write('Session.start', false); + $Session =& new SessionComponent(); + $this->assertFalse($Session->__active); + $this->assertNull($Session->activate()); + $this->assertTrue($Session->__active); + Configure::write('Session.start', true); + $Session->destroy(); + } +/** + * testSessionValid method + * + * @access public + * @return void + */ + function testSessionValid() { + $Session =& new SessionComponent(); + + $this->assertTrue($Session->valid()); + + $Session->_userAgent = 'rweerw'; + $this->assertFalse($Session->valid()); + + Configure::write('Session.start', false); + $Session =& new SessionComponent(); + $this->assertFalse($Session->__active); + $this->assertFalse($Session->valid()); + Configure::write('Session.start', true); + + $Session =& new SessionComponent(); + $Session->time = $Session->read('Config.time') + 1; + $this->assertFalse($Session->valid()); + + Configure::write('Session.checkAgent', false); + $Session =& new SessionComponent(); + $Session->time = $Session->read('Config.time') + 1; + $this->assertFalse($Session->valid()); + Configure::write('Session.checkAgent', true); + } +/** + * testSessionError method + * + * @access public + * @return void + */ + function testSessionError() { + $Session =& new SessionComponent(); + + $this->assertFalse($Session->error()); + + Configure::write('Session.start', false); + $Session =& new SessionComponent(); + $this->assertFalse($Session->__active); + $this->assertFalse($Session->error()); + Configure::write('Session.start', true); + } +/** + * testSessionReadWrite method + * + * @access public + * @return void + */ + function testSessionReadWrite() { + $Session =& new SessionComponent(); + + $this->assertFalse($Session->read('Test')); + + $this->assertTrue($Session->write('Test', 'some value')); + $this->assertEqual($Session->read('Test'), 'some value'); + $this->assertFalse($Session->write('Test.key', 'some value')); + $Session->del('Test'); + + $this->assertTrue($Session->write('Test.key.path', 'some value')); + $this->assertEqual($Session->read('Test.key.path'), 'some value'); + $this->assertEqual($Session->read('Test.key'), array('path' => 'some value')); + $this->assertTrue($Session->write('Test.key.path2', 'another value')); + $this->assertEqual($Session->read('Test.key'), array('path' => 'some value', 'path2' => 'another value')); + $Session->del('Test'); + + $array = array('key1' => 'val1', 'key2' => 'val2', 'key3' => 'val3'); + $this->assertTrue($Session->write('Test', $array)); + $this->assertEqual($Session->read('Test'), $array); + $Session->del('Test'); + + $this->assertFalse($Session->write(array('Test'), 'some value')); + $this->assertTrue($Session->write(array('Test' => 'some value'))); + $this->assertEqual($Session->read('Test'), 'some value'); + $Session->del('Test'); + + Configure::write('Session.start', false); + $Session =& new SessionComponent(); + $this->assertFalse($Session->write('Test', 'some value')); + $Session->write('Test', 'some value'); + $this->assertFalse($Session->read('Test')); + Configure::write('Session.start', true); + } +/** + * testSessionDel method + * + * @access public + * @return void + */ + function testSessionDel() { + $Session =& new SessionComponent(); + + $this->assertFalse($Session->del('Test')); + + $Session->write('Test', 'some value'); + $this->assertTrue($Session->del('Test')); + + Configure::write('Session.start', false); + $Session =& new SessionComponent(); + $Session->write('Test', 'some value'); + $this->assertFalse($Session->del('Test')); + Configure::write('Session.start', true); + } +/** + * testSessionDelete method + * + * @access public + * @return void + */ + function testSessionDelete() { + $Session =& new SessionComponent(); + + $this->assertFalse($Session->delete('Test')); + + $Session->write('Test', 'some value'); + $this->assertTrue($Session->delete('Test')); + + Configure::write('Session.start', false); + $Session =& new SessionComponent(); + $Session->write('Test', 'some value'); + $this->assertFalse($Session->delete('Test')); + Configure::write('Session.start', true); + } +/** + * testSessionCheck method + * + * @access public + * @return void + */ + function testSessionCheck() { + $Session =& new SessionComponent(); + + $this->assertFalse($Session->check('Test')); + + $Session->write('Test', 'some value'); + $this->assertTrue($Session->check('Test')); + $Session->delete('Test'); + + Configure::write('Session.start', false); + $Session =& new SessionComponent(); + $Session->write('Test', 'some value'); + $this->assertFalse($Session->check('Test')); + Configure::write('Session.start', true); + } +/** + * testSessionFlash method + * + * @access public + * @return void + */ + function testSessionFlash() { + $Session =& new SessionComponent(); + + $this->assertNull($Session->read('Message.flash')); + + $Session->setFlash('This is a test message'); + $this->assertEqual($Session->read('Message.flash'), array('message' => 'This is a test message', 'layout' => 'default', 'params' => array())); + + $Session->setFlash('This is a test message', 'test', array('name' => 'Joel Moss')); + $this->assertEqual($Session->read('Message.flash'), array('message' => 'This is a test message', 'layout' => 'test', 'params' => array('name' => 'Joel Moss'))); + + $Session->setFlash('This is a test message', 'default', array(), 'myFlash'); + $this->assertEqual($Session->read('Message.myFlash'), array('message' => 'This is a test message', 'layout' => 'default', 'params' => array())); + + $Session->setFlash('This is a test message', 'non_existing_layout'); + $this->assertEqual($Session->read('Message.myFlash'), array('message' => 'This is a test message', 'layout' => 'default', 'params' => array())); + + $Session->del('Message'); + } +/** + * testSessionId method + * + * @access public + * @return void + */ + function testSessionId() { + unset($_SESSION); + $Session =& new SessionComponent(); + $this->assertNull($Session->id()); + } +/** + * testSessionDestroy method + * + * @access public + * @return void + */ + function testSessionDestroy() { + $Session =& new SessionComponent(); + + $Session->write('Test', 'some value'); + $this->assertEqual($Session->read('Test'), 'some value'); + $Session->destroy('Test'); + $this->assertNull($Session->read('Test')); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/controller/controller.test.php b/cake/tests/cases/libs/controller/controller.test.php new file mode 100755 index 0000000..8c7b320 --- /dev/null +++ b/cake/tests/cases/libs/controller/controller.test.php @@ -0,0 +1,1148 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * ControllerTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs.controller + * @since CakePHP(tm) v 1.2.0.5436 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +App::import('Core', 'Controller'); +App::import('Component', 'Security'); +App::import('Component', 'Cookie'); +/** + * AppController class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +if (!class_exists('AppController')) { + /** + * AppController class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ + class AppController extends Controller { + /** + * helpers property + * + * @var array + * @access public + */ + var $helpers = array('Html', 'Javascript'); + /** + * uses property + * + * @var array + * @access public + */ + var $uses = array('ControllerPost'); + /** + * components property + * + * @var array + * @access public + */ + var $components = array('Cookie'); + } +} elseif (!defined('APP_CONTROLLER_EXISTS')) { + define('APP_CONTROLLER_EXISTS', true); +} +/** + * ControllerPost class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class ControllerPost extends CakeTestModel { +/** + * name property + * + * @var string 'ControllerPost' + * @access public + */ + var $name = 'ControllerPost'; +/** + * useTable property + * + * @var string 'posts' + * @access public + */ + var $useTable = 'posts'; +/** + * invalidFields property + * + * @var array + * @access public + */ + var $invalidFields = array('name' => 'error_msg'); +/** + * lastQuery property + * + * @var mixed null + * @access public + */ + var $lastQuery = null; +/** + * beforeFind method + * + * @param mixed $query + * @access public + * @return void + */ + function beforeFind($query) { + $this->lastQuery = $query; + } +/** + * find method + * + * @param mixed $type + * @param array $options + * @access public + * @return void + */ + function find($type, $options = array()) { + if ($type == 'popular') { + $conditions = array($this->name . '.' . $this->primaryKey .' > ' => '1'); + $options = Set::merge($options, compact('conditions')); + return parent::find('all', $options); + } + return parent::find($type, $options); + } +} +/** + * ControllerPostsController class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class ControllerCommentsController extends AppController { +/** + * name property + * + * @var string 'ControllerPost' + * @access public + */ + var $name = 'ControllerComments'; +} +/** + * ControllerComment class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class ControllerComment extends CakeTestModel { +/** + * name property + * + * @var string 'ControllerComment' + * @access public + */ + var $name = 'Comment'; +/** + * useTable property + * + * @var string 'comments' + * @access public + */ + var $useTable = 'comments'; +/** + * data property + * + * @var array + * @access public + */ + var $data = array('name' => 'Some Name'); +/** + * alias property + * + * @var string 'ControllerComment' + * @access public + */ + var $alias = 'ControllerComment'; +} +/** + * ControllerAlias class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class ControllerAlias extends CakeTestModel { +/** + * name property + * + * @var string 'ControllerAlias' + * @access public + */ + var $name = 'ControllerAlias'; +/** + * alias property + * + * @var string 'ControllerSomeAlias' + * @access public + */ + var $alias = 'ControllerSomeAlias'; +/** + * useTable property + * + * @var string 'posts' + * @access public + */ + var $useTable = 'posts'; +} +/** + * ControllerPaginateModel class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class ControllerPaginateModel extends CakeTestModel { +/** + * name property + * + * @var string + * @access public + */ + var $name = 'ControllerPaginateModel'; +/** + * useTable property + * + * @var string' + * @access public + */ + var $useTable = 'comments'; +/** + * paginate method + * + * @return void + * @access public + **/ + function paginate($conditions, $fields, $order, $limit, $page, $recursive, $extra) { + $this->extra = $extra; + } +/** + * paginateCount + * + * @access public + * @return void + */ + function paginateCount($conditions, $recursive, $extra) { + $this->extraCount = $extra; + } +} +/** + * NameTest class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class NameTest extends CakeTestModel { +/** + * name property + * @var string 'Name' + * @access public + */ + var $name = 'Name'; +/** + * useTable property + * @var string 'names' + * @access public + */ + var $useTable = 'comments'; +/** + * alias property + * + * @var string 'ControllerComment' + * @access public + */ + var $alias = 'Name'; +} +/** + * TestController class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class TestController extends AppController { +/** + * name property + * @var string 'Name' + * @access public + */ + var $name = 'TestController'; +/** + * helpers property + * + * @var array + * @access public + */ + var $helpers = array('Xml'); +/** + * components property + * + * @var array + * @access public + */ + var $components = array('Security'); +/** + * uses property + * + * @var array + * @access public + */ + var $uses = array('ControllerComment', 'ControllerAlias'); +/** + * index method + * + * @param mixed $testId + * @param mixed $test2Id + * @access public + * @return void + */ + function index($testId, $test2Id) { + $this->data['testId'] = $testId; + $this->data['test2Id'] = $test2Id; + } +} +/** + * TestComponent class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class TestComponent extends Object { +/** + * beforeRedirect method + * + * @access public + * @return void + */ + function beforeRedirect() { + } +} +/** + * AnotherTestController class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class AnotherTestController extends AppController { +/** + * name property + * @var string 'Name' + * @access public + */ + var $name = 'AnotherTest'; +/** + * uses property + * + * @var array + * @access public + */ + var $uses = null; +} +/** + * ControllerTest class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class ControllerTest extends CakeTestCase { +/** + * fixtures property + * + * @var array + * @access public + */ + var $fixtures = array('core.post', 'core.comment', 'core.name'); +/** + * testConstructClasses method + * + * @access public + * @return void + */ + function testConstructClasses() { + $Controller =& new Controller(); + $Controller->modelClass = 'ControllerPost'; + $Controller->passedArgs[] = '1'; + $Controller->constructClasses(); + $this->assertEqual($Controller->ControllerPost->id, 1); + + unset($Controller); + + $Controller =& new Controller(); + $Controller->uses = array('ControllerPost', 'ControllerComment'); + $Controller->passedArgs[] = '1'; + $Controller->constructClasses(); + $this->assertTrue(is_a($Controller->ControllerPost, 'ControllerPost')); + $this->assertTrue(is_a($Controller->ControllerComment, 'ControllerComment')); + + $this->assertEqual($Controller->ControllerComment->name, 'Comment'); + + unset($Controller); + + $_back = array( + 'pluginPaths' => Configure::read('pluginPaths'), + ); + Configure::write('pluginPaths', array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)); + + $Controller =& new Controller(); + $Controller->uses = array('TestPlugin.TestPluginPost'); + $Controller->constructClasses(); + + $this->assertEqual($Controller->modelClass, 'TestPluginPost'); + $this->assertTrue(isset($Controller->TestPluginPost)); + $this->assertTrue(is_a($Controller->TestPluginPost, 'TestPluginPost')); + + Configure::write('pluginPaths', $_back['pluginPaths']); + unset($Controller); + } +/** + * testAliasName method + * + * @access public + * @return void + */ + function testAliasName() { + $Controller =& new Controller(); + $Controller->uses = array('NameTest'); + $Controller->constructClasses(); + + $this->assertEqual($Controller->NameTest->name, 'Name'); + $this->assertEqual($Controller->NameTest->alias, 'Name'); + + unset($Controller); + } +/** + * testPersistent method + * + * @access public + * @return void + */ + function testPersistent() { + Configure::write('Cache.disable', false); + $Controller =& new Controller(); + $Controller->modelClass = 'ControllerPost'; + $Controller->persistModel = true; + $Controller->constructClasses(); + $this->assertTrue(file_exists(CACHE . 'persistent' . DS .'controllerpost.php')); + $this->assertTrue(is_a($Controller->ControllerPost, 'ControllerPost')); + @unlink(CACHE . 'persistent' . DS . 'controllerpost.php'); + @unlink(CACHE . 'persistent' . DS . 'controllerpostregistry.php'); + + unset($Controller); + Configure::write('Cache.disable', true); + } +/** + * testPaginate method + * + * @access public + * @return void + */ + function testPaginate() { + $Controller =& new Controller(); + $Controller->uses = array('ControllerPost', 'ControllerComment'); + $Controller->passedArgs[] = '1'; + $Controller->params['url'] = array(); + $Controller->constructClasses(); + + $results = Set::extract($Controller->paginate('ControllerPost'), '{n}.ControllerPost.id'); + $this->assertEqual($results, array(1, 2, 3)); + + $results = Set::extract($Controller->paginate('ControllerComment'), '{n}.ControllerComment.id'); + $this->assertEqual($results, array(1, 2, 3, 4, 5, 6)); + + $Controller->modelClass = null; + + $Controller->uses[0] = 'Plugin.ControllerPost'; + $results = Set::extract($Controller->paginate(), '{n}.ControllerPost.id'); + $this->assertEqual($results, array(1, 2, 3)); + + $Controller->passedArgs = array('page' => '-1'); + $results = Set::extract($Controller->paginate('ControllerPost'), '{n}.ControllerPost.id'); + $this->assertEqual($Controller->params['paging']['ControllerPost']['page'], 1); + $this->assertEqual($results, array(1, 2, 3)); + + $Controller->passedArgs = array('sort' => 'ControllerPost.id', 'direction' => 'asc'); + $results = Set::extract($Controller->paginate('ControllerPost'), '{n}.ControllerPost.id'); + $this->assertEqual($Controller->params['paging']['ControllerPost']['page'], 1); + $this->assertEqual($results, array(1, 2, 3)); + + $Controller->passedArgs = array('sort' => 'ControllerPost.id', 'direction' => 'desc'); + $results = Set::extract($Controller->paginate('ControllerPost'), '{n}.ControllerPost.id'); + $this->assertEqual($Controller->params['paging']['ControllerPost']['page'], 1); + $this->assertEqual($results, array(3, 2, 1)); + + $Controller->passedArgs = array('sort' => 'id', 'direction' => 'desc'); + $results = Set::extract($Controller->paginate('ControllerPost'), '{n}.ControllerPost.id'); + $this->assertEqual($Controller->params['paging']['ControllerPost']['page'], 1); + $this->assertEqual($results, array(3, 2, 1)); + + $Controller->passedArgs = array('sort' => 'NotExisting.field', 'direction' => 'desc'); + $results = Set::extract($Controller->paginate('ControllerPost'), '{n}.ControllerPost.id'); + $this->assertEqual($Controller->params['paging']['ControllerPost']['page'], 1, 'Invalid field in query %s'); + $this->assertEqual($results, array(1, 2, 3)); + + $Controller->passedArgs = array('sort' => 'ControllerPost.author_id', 'direction' => 'allYourBase'); + $results = Set::extract($Controller->paginate('ControllerPost'), '{n}.ControllerPost.id'); + $this->assertEqual($Controller->ControllerPost->lastQuery['order'][0], array('ControllerPost.author_id' => 'asc')); + $this->assertEqual($results, array(1, 3, 2)); + + $Controller->passedArgs = array('page' => '1 " onclick="alert(\'xss\');">'); + $Controller->paginate = array('limit' => 1); + $Controller->paginate('ControllerPost'); + $this->assertIdentical($Controller->params['paging']['ControllerPost']['page'], 1, 'XSS exploit opened %s'); + $this->assertIdentical($Controller->params['paging']['ControllerPost']['options']['page'], 1, 'XSS exploit opened %s'); + } +/** + * testPaginateExtraParams method + * + * @access public + * @return void + */ + function testPaginateExtraParams() { + $Controller =& new Controller(); + $Controller->uses = array('ControllerPost', 'ControllerComment'); + $Controller->passedArgs[] = '1'; + $Controller->params['url'] = array(); + $Controller->constructClasses(); + + $Controller->passedArgs = array('page' => '-1', 'contain' => array('ControllerComment')); + $result = $Controller->paginate('ControllerPost'); + $this->assertEqual($Controller->params['paging']['ControllerPost']['page'], 1); + $this->assertEqual(Set::extract($result, '{n}.ControllerPost.id'), array(1, 2, 3)); + $this->assertTrue(!isset($Controller->ControllerPost->lastQuery['contain'])); + + $Controller->passedArgs = array('page' => '-1'); + $Controller->paginate = array('ControllerPost' => array('contain' => array('ControllerComment'))); + $result = $Controller->paginate('ControllerPost'); + $this->assertEqual($Controller->params['paging']['ControllerPost']['page'], 1); + $this->assertEqual(Set::extract($result, '{n}.ControllerPost.id'), array(1, 2, 3)); + $this->assertTrue(isset($Controller->ControllerPost->lastQuery['contain'])); + + $Controller->paginate = array('ControllerPost' => array('popular', 'fields' => array('id', 'title'))); + $result = $Controller->paginate('ControllerPost'); + $this->assertEqual(Set::extract($result, '{n}.ControllerPost.id'), array(2, 3)); + $this->assertEqual($Controller->ControllerPost->lastQuery['conditions'], array('ControllerPost.id > ' => '1')); + + $Controller->passedArgs = array('limit' => 12); + $Controller->paginate = array('limit' => 30); + $result = $Controller->paginate('ControllerPost'); + $paging = $Controller->params['paging']['ControllerPost']; + + $this->assertEqual($Controller->ControllerPost->lastQuery['limit'], 12); + $this->assertEqual($paging['options']['limit'], 12); + + $Controller =& new Controller(); + $Controller->uses = array('ControllerPaginateModel'); + $Controller->params['url'] = array(); + $Controller->constructClasses(); + $Controller->paginate = array( + 'ControllerPaginateModel' => array('contain' => array('ControllerPaginateModel'), 'group' => 'Comment.author_id') + ); + $result = $Controller->paginate('ControllerPaginateModel'); + $expected = array('contain' => array('ControllerPaginateModel'), 'group' => 'Comment.author_id'); + $this->assertEqual($Controller->ControllerPaginateModel->extra, $expected); + $this->assertEqual($Controller->ControllerPaginateModel->extraCount, $expected); + + $Controller->paginate = array( + 'ControllerPaginateModel' => array('foo', 'contain' => array('ControllerPaginateModel'), 'group' => 'Comment.author_id') + ); + $Controller->paginate('ControllerPaginateModel'); + $expected = array('contain' => array('ControllerPaginateModel'), 'group' => 'Comment.author_id', 'type' => 'foo'); + $this->assertEqual($Controller->ControllerPaginateModel->extra, $expected); + $this->assertEqual($Controller->ControllerPaginateModel->extraCount, $expected); + } +/** + * testPaginatePassedArgs method + * + * @return void + * @access public + */ + function testPaginatePassedArgs() { + $Controller =& new Controller(); + $Controller->uses = array('ControllerPost'); + $Controller->passedArgs[] = array('1', '2', '3'); + $Controller->params['url'] = array(); + $Controller->constructClasses(); + + $Controller->paginate = array( + 'fields' => array(), + 'order' => '', + 'limit' => 5, + 'page' => 1, + 'recursive' => -1 + ); + $conditions = array(); + $Controller->paginate('ControllerPost',$conditions); + + $expected = array( + 'fields' => array(), + 'order' => '', + 'limit' => 5, + 'page' => 1, + 'recursive' => -1, + 'conditions' => array() + ); + $this->assertEqual($Controller->params['paging']['ControllerPost']['options'],$expected); + } +/** + * Test that special paginate types are called and that the type param doesn't leak out into defaults or options. + * + * @return void + **/ + function testPaginateSpecialType() { + $Controller =& new Controller(); + $Controller->uses = array('ControllerPost', 'ControllerComment'); + $Controller->passedArgs[] = '1'; + $Controller->params['url'] = array(); + $Controller->constructClasses(); + + $Controller->paginate = array('ControllerPost' => array('popular', 'fields' => array('id', 'title'))); + $result = $Controller->paginate('ControllerPost'); + + $this->assertEqual(Set::extract($result, '{n}.ControllerPost.id'), array(2, 3)); + $this->assertEqual($Controller->ControllerPost->lastQuery['conditions'], array('ControllerPost.id > ' => '1')); + $this->assertFalse(isset($Controller->params['paging']['ControllerPost']['defaults'][0])); + $this->assertFalse(isset($Controller->params['paging']['ControllerPost']['options'][0])); + } +/** + * testDefaultPaginateParams method + * + * @access public + * @return void + */ + function testDefaultPaginateParams() { + $Controller =& new Controller(); + $Controller->modelClass = 'ControllerPost'; + $Controller->params['url'] = array(); + $Controller->paginate = array('order' => 'ControllerPost.id DESC'); + $Controller->constructClasses(); + $results = Set::extract($Controller->paginate('ControllerPost'), '{n}.ControllerPost.id'); + $this->assertEqual($Controller->params['paging']['ControllerPost']['defaults']['order'], 'ControllerPost.id DESC'); + $this->assertEqual($Controller->params['paging']['ControllerPost']['options']['order'], 'ControllerPost.id DESC'); + $this->assertEqual($results, array(3, 2, 1)); + } +/** + * testFlash method + * + * @access public + * @return void + */ + function testFlash() { + $Controller =& new Controller(); + $Controller->flash('this should work', '/flash'); + $result = $Controller->output; + + $expected = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + <html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> + <title>this should work</title> + <style><!-- + P { text-align:center; font:bold 1.1em sans-serif } + A { color:#444; text-decoration:none } + A:HOVER { text-decoration: underline; color:#44E } + --></style> + </head> + <body> + <p><a href="/flash">this should work</a></p> + </body> + </html>'; + $result = str_replace(array("\t", "\r\n", "\n"), "", $result); + $expected = str_replace(array("\t", "\r\n", "\n"), "", $expected); + $this->assertEqual($result, $expected); + } +/** + * testControllerSet method + * + * @access public + * @return void + */ + function testControllerSet() { + $Controller =& new Controller(); + $Controller->set('variable_with_underscores', null); + $this->assertTrue(array_key_exists('variable_with_underscores', $Controller->viewVars)); + + $Controller->viewVars = array(); + $viewVars = array('ModelName' => array('id' => 1, 'name' => 'value')); + $Controller->set($viewVars); + $this->assertTrue(array_key_exists('modelName', $Controller->viewVars)); + + $Controller->viewVars = array(); + $Controller->set('variable_with_underscores', 'value'); + $this->assertTrue(array_key_exists('variable_with_underscores', $Controller->viewVars)); + + $Controller->viewVars = array(); + $viewVars = array('ModelName' => 'name'); + $Controller->set($viewVars); + $this->assertTrue(array_key_exists('modelName', $Controller->viewVars)); + + $Controller->set('title', 'someTitle'); + $this->assertIdentical($Controller->pageTitle, 'someTitle'); + + $Controller->viewVars = array(); + $expected = array('ModelName' => 'name', 'ModelName2' => 'name2'); + $Controller->set(array('ModelName', 'ModelName2'), array('name', 'name2')); + $this->assertIdentical($Controller->viewVars, $expected); + } +/** + * testRender method + * + * @access public + * @return void + */ + function testRender() { + Configure::write('viewPaths', array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS, TEST_CAKE_CORE_INCLUDE_PATH . 'libs' . DS . 'view' . DS)); + + $Controller =& new Controller(); + $Controller->viewPath = 'posts'; + + $result = $Controller->render('index'); + $this->assertPattern('/posts index/', $result); + + $result = $Controller->render('/elements/test_element'); + $this->assertPattern('/this is the test element/', $result); + + $Controller = new TestController(); + $Controller->constructClasses(); + $Controller->ControllerComment->validationErrors = array('title' => 'tooShort'); + $expected = $Controller->ControllerComment->validationErrors; + + ClassRegistry::flush(); + $Controller->viewPath = 'posts'; + $result = $Controller->render('index'); + $View = ClassRegistry::getObject('view'); + $this->assertTrue(isset($View->validationErrors['ControllerComment'])); + $this->assertEqual($expected, $View->validationErrors['ControllerComment']); + + $Controller->ControllerComment->validationErrors = array(); + ClassRegistry::flush(); + } +/** + * testToBeInheritedGuardmethods method + * + * @access public + * @return void + */ + function testToBeInheritedGuardmethods() { + $Controller =& new Controller(); + $this->assertTrue($Controller->_beforeScaffold('')); + $this->assertTrue($Controller->_afterScaffoldSave('')); + $this->assertTrue($Controller->_afterScaffoldSaveError('')); + $this->assertFalse($Controller->_scaffoldError('')); + } +/** + * testRedirect method + * + * @access public + * @return void + */ + function testRedirect() { + $codes = array( + 100 => "Continue", + 101 => "Switching Protocols", + 200 => "OK", + 201 => "Created", + 202 => "Accepted", + 203 => "Non-Authoritative Information", + 204 => "No Content", + 205 => "Reset Content", + 206 => "Partial Content", + 300 => "Multiple Choices", + 301 => "Moved Permanently", + 302 => "Found", + 303 => "See Other", + 304 => "Not Modified", + 305 => "Use Proxy", + 307 => "Temporary Redirect", + 400 => "Bad Request", + 401 => "Unauthorized", + 402 => "Payment Required", + 403 => "Forbidden", + 404 => "Not Found", + 405 => "Method Not Allowed", + 406 => "Not Acceptable", + 407 => "Proxy Authentication Required", + 408 => "Request Time-out", + 409 => "Conflict", + 410 => "Gone", + 411 => "Length Required", + 412 => "Precondition Failed", + 413 => "Request Entity Too Large", + 414 => "Request-URI Too Large", + 415 => "Unsupported Media Type", + 416 => "Requested range not satisfiable", + 417 => "Expectation Failed", + 500 => "Internal Server Error", + 501 => "Not Implemented", + 502 => "Bad Gateway", + 503 => "Service Unavailable", + 504 => "Gateway Time-out" + ); + + Mock::generatePartial('Controller', 'MockController', array('header')); + Mock::generate('TestComponent', 'MockTestComponent'); + Mock::generate('TestComponent', 'MockTestBComponent'); + + App::import('Helper', 'Cache'); + + foreach ($codes as $code => $msg) { + $MockController =& new MockController(); + $MockController->Component =& new Component(); + $MockController->Component->init($MockController); + $MockController->expectAt(0, 'header', array("HTTP/1.1 {$code} {$msg}")); + $MockController->expectAt(1, 'header', array('Location: http://cakephp.org')); + $MockController->expectCallCount('header', 2); + $MockController->redirect('http://cakephp.org', (int)$code, false); + $this->assertFalse($MockController->autoRender); + } + foreach ($codes as $code => $msg) { + $MockController =& new MockController(); + $MockController->Component =& new Component(); + $MockController->Component->init($MockController); + $MockController->expectAt(0, 'header', array("HTTP/1.1 {$code} {$msg}")); + $MockController->expectAt(1, 'header', array('Location: http://cakephp.org')); + $MockController->expectCallCount('header', 2); + $MockController->redirect('http://cakephp.org', $msg, false); + $this->assertFalse($MockController->autoRender); + } + + $MockController =& new MockController(); + $MockController->Component =& new Component(); + $MockController->Component->init($MockController); + $MockController->expectAt(0, 'header', array('Location: http://www.example.org/users/login')); + $MockController->expectCallCount('header', 1); + $MockController->redirect('http://www.example.org/users/login', null, false); + + $MockController =& new MockController(); + $MockController->Component =& new Component(); + $MockController->Component->init($MockController); + $MockController->expectAt(0, 'header', array('HTTP/1.1 301 Moved Permanently')); + $MockController->expectAt(1, 'header', array('Location: http://www.example.org/users/login')); + $MockController->expectCallCount('header', 2); + $MockController->redirect('http://www.example.org/users/login', 301, false); + + $MockController =& new MockController(); + $MockController->components = array('MockTest'); + $MockController->Component =& new Component(); + $MockController->Component->init($MockController); + $MockController->MockTest->setReturnValue('beforeRedirect', null); + $MockController->expectAt(0, 'header', array('HTTP/1.1 301 Moved Permanently')); + $MockController->expectAt(1, 'header', array('Location: http://cakephp.org')); + $MockController->expectCallCount('header', 2); + $MockController->redirect('http://cakephp.org', 301, false); + + $MockController =& new MockController(); + $MockController->components = array('MockTest'); + $MockController->Component =& new Component(); + $MockController->Component->init($MockController); + $MockController->MockTest->setReturnValue('beforeRedirect', 'http://book.cakephp.org'); + $MockController->expectAt(0, 'header', array('HTTP/1.1 301 Moved Permanently')); + $MockController->expectAt(1, 'header', array('Location: http://book.cakephp.org')); + $MockController->expectCallCount('header', 2); + $MockController->redirect('http://cakephp.org', 301, false); + + $MockController =& new MockController(); + $MockController->components = array('MockTest'); + $MockController->Component =& new Component(); + $MockController->Component->init($MockController); + $MockController->MockTest->setReturnValue('beforeRedirect', false); + $MockController->expectNever('header'); + $MockController->redirect('http://cakephp.org', 301, false); + + $MockController =& new MockController(); + $MockController->components = array('MockTest', 'MockTestB'); + $MockController->Component =& new Component(); + $MockController->Component->init($MockController); + $MockController->MockTest->setReturnValue('beforeRedirect', 'http://book.cakephp.org'); + $MockController->MockTestB->setReturnValue('beforeRedirect', 'http://bakery.cakephp.org'); + $MockController->expectAt(0, 'header', array('HTTP/1.1 301 Moved Permanently')); + $MockController->expectAt(1, 'header', array('Location: http://bakery.cakephp.org')); + $MockController->expectCallCount('header', 2); + $MockController->redirect('http://cakephp.org', 301, false); + } +/** + * testMergeVars method + * + * @access public + * @return void + */ + function testMergeVars() { + if ($this->skipIf(defined('APP_CONTROLLER_EXISTS'), '%s Need a non-existent AppController')) { + return; + } + + $TestController =& new TestController(); + $TestController->constructClasses(); + + $testVars = get_class_vars('TestController'); + $appVars = get_class_vars('AppController'); + + $components = is_array($appVars['components']) + ? array_merge($appVars['components'], $testVars['components']) + : $testVars['components']; + if (!in_array('Session', $components)) { + $components[] = 'Session'; + } + $helpers = is_array($appVars['helpers']) + ? array_merge($appVars['helpers'], $testVars['helpers']) + : $testVars['helpers']; + $uses = is_array($appVars['uses']) + ? array_merge($appVars['uses'], $testVars['uses']) + : $testVars['uses']; + + $this->assertEqual(count(array_diff($TestController->helpers, $helpers)), 0); + $this->assertEqual(count(array_diff($TestController->uses, $uses)), 0); + $this->assertEqual(count(array_diff_assoc(Set::normalize($TestController->components), Set::normalize($components))), 0); + + $TestController =& new AnotherTestController(); + $TestController->constructClasses(); + + $appVars = get_class_vars('AppController'); + $testVars = get_class_vars('AnotherTestController'); + + + $this->assertTrue(in_array('ControllerPost', $appVars['uses'])); + $this->assertNull($testVars['uses']); + + $this->assertFalse(isset($TestController->ControllerPost)); + + + $TestController =& new ControllerCommentsController(); + $TestController->constructClasses(); + + $appVars = get_class_vars('AppController'); + $testVars = get_class_vars('ControllerCommentsController'); + + + $this->assertTrue(in_array('ControllerPost', $appVars['uses'])); + $this->assertEqual(array('ControllerPost'), $testVars['uses']); + + $this->assertTrue(isset($TestController->ControllerPost)); + $this->assertTrue(isset($TestController->ControllerComment)); + } +/** + * test that options from child classes replace those in the parent classes. + * + * @access public + * @return void + **/ + function testChildComponentOptionsSupercedeParents() { + if ($this->skipIf(defined('APP_CONTROLLER_EXISTS'), '%s Need a non-existent AppController')) { + return; + } + $TestController =& new TestController(); + $expected = array('foo'); + $TestController->components = array('Cookie' => $expected); + $TestController->constructClasses(); + $this->assertEqual($TestController->components['Cookie'], $expected); + } +/** + * Ensure that __mergeVars is not being greedy and merging with + * AppController when you make an instance of Controller + * + * @return void + **/ + function testMergeVarsNotGreedy() { + $Controller =& new Controller(); + $Controller->components = array(); + $Controller->uses = array(); + $Controller->constructClasses(); + + $this->assertTrue(isset($Controller->Session)); + } +/** + * testReferer method + * + * @access public + * @return void + */ + function testReferer() { + $Controller =& new Controller(); + $_SERVER['HTTP_REFERER'] = 'http://cakephp.org'; + $result = $Controller->referer(null, false); + $expected = 'http://cakephp.org'; + $this->assertIdentical($result, $expected); + + $_SERVER['HTTP_REFERER'] = ''; + $result = $Controller->referer('http://cakephp.org', false); + $expected = 'http://cakephp.org'; + $this->assertIdentical($result, $expected); + + $_SERVER['HTTP_REFERER'] = ''; + $result = $Controller->referer(null, false); + $expected = '/'; + $this->assertIdentical($result, $expected); + + $_SERVER['HTTP_REFERER'] = FULL_BASE_URL.$Controller->webroot.'/some/path'; + $result = $Controller->referer(null, false); + $expected = '/some/path'; + $this->assertIdentical($result, $expected); + + $Controller->webroot .= '/'; + $_SERVER['HTTP_REFERER'] = FULL_BASE_URL.$Controller->webroot.'/some/path'; + $result = $Controller->referer(null, false); + $expected = '/some/path'; + $this->assertIdentical($result, $expected); + + $_SERVER['HTTP_REFERER'] = FULL_BASE_URL.$Controller->webroot.'some/path'; + $result = $Controller->referer(null, false); + $expected = '/some/path'; + $this->assertIdentical($result, $expected); + + $Controller->webroot = '/recipe/'; + + $_SERVER['HTTP_REFERER'] = FULL_BASE_URL.$Controller->webroot.'recipes/add'; + $result = $Controller->referer(); + $expected = '/recipes/add'; + $this->assertIdentical($result, $expected); + } +/** + * testSetAction method + * + * @access public + * @return void + */ + function testSetAction() { + $TestController =& new TestController(); + $TestController->setAction('index', 1, 2); + $expected = array('testId' => 1, 'test2Id' => 2); + $this->assertidentical($TestController->data, $expected); + } +/** + * testUnimplementedIsAuthorized method + * + * @access public + * @return void + */ + function testUnimplementedIsAuthorized() { + $TestController =& new TestController(); + $TestController->isAuthorized(); + $this->assertError(); + } +/** + * testValidateErrors method + * + * @access public + * @return void + */ + function testValidateErrors() { + $TestController =& new TestController(); + $TestController->constructClasses(); + $this->assertFalse($TestController->validateErrors()); + $this->assertEqual($TestController->validate(), 0); + + $TestController->ControllerComment->invalidate('some_field', 'error_message'); + $TestController->ControllerComment->invalidate('some_field2', 'error_message2'); + $comment = new ControllerComment; + $comment->set('someVar', 'data'); + $result = $TestController->validateErrors($comment); + $expected = array('some_field' => 'error_message', 'some_field2' => 'error_message2'); + $this->assertIdentical($result, $expected); + $this->assertEqual($TestController->validate($comment), 2); + } +/** + * testPostConditions method + * + * @access public + * @return void + */ + function testPostConditions() { + $Controller =& new Controller(); + + + $data = array( + 'Model1' => array('field1' => '23'), + 'Model2' => array('field2' => 'string'), + 'Model3' => array('field3' => '23'), + ); + $expected = array( + 'Model1.field1' => '23', + 'Model2.field2' => 'string', + 'Model3.field3' => '23', + ); + $result = $Controller->postConditions($data); + $this->assertIdentical($result, $expected); + + + $data = array(); + $Controller->data = array( + 'Model1' => array('field1' => '23'), + 'Model2' => array('field2' => 'string'), + 'Model3' => array('field3' => '23'), + ); + $expected = array( + 'Model1.field1' => '23', + 'Model2.field2' => 'string', + 'Model3.field3' => '23', + ); + $result = $Controller->postConditions($data); + $this->assertIdentical($result, $expected); + + + $data = array(); + $Controller->data = array(); + $result = $Controller->postConditions($data); + $this->assertNull($result); + + + $data = array(); + $Controller->data = array( + 'Model1' => array('field1' => '23'), + 'Model2' => array('field2' => 'string'), + 'Model3' => array('field3' => '23'), + ); + $ops = array( + 'Model1.field1' => '>', + 'Model2.field2' => 'LIKE', + 'Model3.field3' => '<=', + ); + $expected = array( + 'Model1.field1 >' => '23', + 'Model2.field2 LIKE' => "%string%", + 'Model3.field3 <=' => '23', + ); + $result = $Controller->postConditions($data, $ops); + $this->assertIdentical($result, $expected); + } +/** + * testRequestHandlerPrefers method + * + * @access public + * @return void + */ + function testRequestHandlerPrefers(){ + Configure::write('debug', 2); + $Controller =& new Controller(); + $Controller->components = array("RequestHandler"); + $Controller->modelClass='ControllerPost'; + $Controller->params['url']['ext'] = 'rss'; + $Controller->constructClasses(); + $Controller->Component->initialize($Controller); + $Controller->beforeFilter(); + $Controller->Component->startup($Controller); + + $this->assertEqual($Controller->RequestHandler->prefers(), 'rss'); + unset($Controller); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/controller/controller_merge_vars.test.php b/cake/tests/cases/libs/controller/controller_merge_vars.test.php new file mode 100755 index 0000000..b405d3e --- /dev/null +++ b/cake/tests/cases/libs/controller/controller_merge_vars.test.php @@ -0,0 +1,222 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * Controller Merge vars Test file + * + * Isolated from the Controller and Component test as to not pollute their AppController class + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs.controller + * @since CakePHP(tm) v 1.2.3 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +if (!class_exists('AppController')) { + /** + * Test case AppController requred + * + * @package cake.tests.cases.libs.controller + **/ + class AppController extends Controller { + /** + * components + * + * @var array + **/ + var $components = array('MergeVar' => array('flag', 'otherFlag', 'redirect' => false)); + /** + * helpers + * + * @var array + **/ + var $helpers = array('MergeVar' => array('format' => 'html', 'terse')); + } +} elseif (!defined('APP_CONTROLLER_EXISTS')) { + define('APP_CONTROLLER_EXISTS', true); +} + +/** + * MergeVar Component + * + * @package cake.tests.cases.libs.controller + **/ +class MergeVarComponent extends Object { + +} + +/** + * Additional controller for testing + * + * @package cake.tests.cases.libs.controller + **/ +class MergeVariablesController extends AppController { +/** + * name + * + * @var string + **/ + var $name = 'MergeVariables'; +/** + * uses + * + * @var arrays + **/ + var $uses = array(); +} + +/** + * MergeVarPlugin App Controller + * + * @package cake.tests.cases.libs.controller + **/ +class MergeVarPluginAppController extends AppController { +/** + * components + * + * @var array + **/ + var $components = array('Auth' => array('setting' => 'val', 'otherVal')); +/** + * helpers + * + * @var array + **/ + var $helpers = array('Javascript'); +} + +/** + * MergePostsController + * + * @package cake.tests.cases.libs.controller + **/ +class MergePostsController extends MergeVarPluginAppController { +/** + * name + * + * @var string + **/ + var $name = 'MergePosts'; +/** + * uses + * + * @var array + **/ + var $uses = array(); +} + + +/** + * Test Case for Controller Merging of Vars. + * + * @package cake.tests.cases.libs.controller + **/ +class ControllerMergeVarsTestCase extends CakeTestCase { + +/** + * end test + * + * @return void + **/ + function endTest() { + ClassRegistry::flush(); + } +/** + * test that component settings are not duplicated when merging component settings + * + * @return void + **/ + function testComponentParamMergingNoDuplication() { + $Controller =& new MergeVariablesController(); + $Controller->constructClasses(); + + $expected = array('MergeVar' => array('flag', 'otherFlag', 'redirect' => false)); + $this->assertEqual($Controller->components, $expected, 'Duplication of settings occured. %s'); + } +/** + * test component merges with redeclared components + * + * @return void + **/ + function testComponentMergingWithRedeclarations() { + $Controller =& new MergeVariablesController(); + $Controller->components['MergeVar'] = array('remote', 'redirect' => true); + $Controller->constructClasses(); + + $expected = array('MergeVar' => array('flag', 'otherFlag', 'redirect' => true, 'remote')); + $this->assertEqual($Controller->components, $expected, 'Merging of settings is wrong. %s'); + } +/** + * test merging of helpers array, ensure no duplication occurs + * + * @return void + **/ + function testHelperSettingMergingNoDuplication() { + $Controller =& new MergeVariablesController(); + $Controller->constructClasses(); + + $expected = array('MergeVar' => array('format' => 'html', 'terse')); + $this->assertEqual($Controller->helpers, $expected, 'Duplication of settings occured. %s'); + } +/** + * test merging of vars with plugin + * + * @return void + **/ + function testMergeVarsWithPlugin() { + $Controller =& new MergePostsController(); + $Controller->components = array('Email' => array('ports' => 'open')); + $Controller->plugin = 'MergeVarPlugin'; + $Controller->constructClasses(); + + $expected = array( + 'MergeVar' => array('flag', 'otherFlag', 'redirect' => false), + 'Auth' => array('setting' => 'val', 'otherVal'), + 'Email' => array('ports' => 'open') + ); + $this->assertEqual($Controller->components, $expected, 'Components are unexpected %s'); + + $expected = array( + 'Javascript', + 'MergeVar' => array('format' => 'html', 'terse') + ); + $this->assertEqual($Controller->helpers, $expected, 'Helpers are unexpected %s'); + + $Controller =& new MergePostsController(); + $Controller->components = array(); + $Controller->plugin = 'MergeVarPlugin'; + $Controller->constructClasses(); + + $expected = array( + 'MergeVar' => array('flag', 'otherFlag', 'redirect' => false), + 'Auth' => array('setting' => 'val', 'otherVal'), + ); + $this->assertEqual($Controller->components, $expected, 'Components are unexpected %s'); + } +/** + * Ensure that __mergeVars is not being greedy and merging with + * AppController when you make an instance of Controller + * + * @return void + **/ + function testMergeVarsNotGreedy() { + $Controller =& new Controller(); + $Controller->components = array(); + $Controller->uses = array(); + $Controller->constructClasses(); + + $this->assertTrue(isset($Controller->Session)); + } +} \ No newline at end of file diff --git a/cake/tests/cases/libs/controller/pages_controller.test.php b/cake/tests/cases/libs/controller/pages_controller.test.php new file mode 100755 index 0000000..66980e7 --- /dev/null +++ b/cake/tests/cases/libs/controller/pages_controller.test.php @@ -0,0 +1,86 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * PagesControllerTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs.controller + * @since CakePHP(tm) v 1.2.0.5436 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +if (!class_exists('AppController')) { + require_once LIBS . 'controller' . DS . 'app_controller.php'; +} elseif (!defined('APP_CONTROLLER_EXISTS')) { + define('APP_CONTROLLER_EXISTS', true); +} +App::import('Core', array('Controller', 'PagesController')); +/** + * PagesControllerTest class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class PagesControllerTest extends CakeTestCase { +/** + * setUp method + * + * @access public + * @return void + */ + function setUp() { + $this->_viewPaths = Configure::read('viewPaths'); + } +/** + * tearDown method + * + * @access public + * @return void + */ + function tearDown() { + Configure::write('viewPaths', $this->_viewPaths); + } +/** + * testDisplay method + * + * @access public + * @return void + */ + function testDisplay() { + if ($this->skipIf(defined('APP_CONTROLLER_EXISTS'), '%s Need a non-existent AppController')) { + return; + } + + Configure::write('viewPaths', array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS, TEST_CAKE_CORE_INCLUDE_PATH . 'libs' . DS . 'view' . DS)); + $Pages =& new PagesController(); + + $Pages->viewPath = 'posts'; + $Pages->display('index'); + $this->assertPattern('/posts index/', $Pages->output); + $this->assertEqual($Pages->viewVars['page'], 'index'); + $this->assertEqual($Pages->pageTitle, 'Index'); + + $Pages->viewPath = 'themed'; + $Pages->display('test_theme', 'posts', 'index'); + $this->assertPattern('/posts index themed view/', $Pages->output); + $this->assertEqual($Pages->viewVars['page'], 'test_theme'); + $this->assertEqual($Pages->viewVars['subpage'], 'posts'); + $this->assertEqual($Pages->pageTitle, 'Index'); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/controller/scaffold.test.php b/cake/tests/cases/libs/controller/scaffold.test.php new file mode 100755 index 0000000..72cab3e --- /dev/null +++ b/cake/tests/cases/libs/controller/scaffold.test.php @@ -0,0 +1,785 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * ScaffoldTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs.controller + * @since CakePHP(tm) v 1.2.0.5436 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +App::import('Core', 'Scaffold'); +/** + * ScaffoldMockController class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class ScaffoldMockController extends Controller { +/** + * name property + * + * @var string 'ScaffoldMock' + * @access public + */ + var $name = 'ScaffoldMock'; +/** + * scaffold property + * + * @var mixed + * @access public + */ + var $scaffold; +} +/** + * ScaffoldMockControllerWithFields class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class ScaffoldMockControllerWithFields extends Controller { +/** + * name property + * + * @var string 'ScaffoldMock' + * @access public + */ + var $name = 'ScaffoldMock'; +/** + * scaffold property + * + * @var mixed + * @access public + */ + var $scaffold; +/** + * function _beforeScaffold + * + * @param string method + */ + function _beforeScaffold($method) { + $this->set('scaffoldFields', array('title')); + return true; + } +} +/** + * TestScaffoldMock class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class TestScaffoldMock extends Scaffold { +/** + * Overload __scaffold + * + * @param unknown_type $params + */ + function __scaffold($params) { + $this->_params = $params; + } +/** + * Get Params from the Controller. + * + * @return unknown + */ + function getParams() { + return $this->_params; + } +} +/** + * ScaffoldMock class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class ScaffoldMock extends CakeTestModel { +/** + * useTable property + * + * @var string 'posts' + * @access public + */ + var $useTable = 'articles'; +/** + * belongsTo property + * + * @var array + * @access public + */ + var $belongsTo = array( + 'User' => array( + 'className' => 'ScaffoldUser', + 'foreignKey' => 'user_id', + ) + ); +/** + * hasMany property + * + * @var array + * @access public + */ + var $hasMany = array( + 'Comment' => array( + 'className' => 'ScaffoldComment', + 'foreignKey' => 'article_id', + ) + ); +/** + * hasAndBelongsToMany property + * + * @var string + **/ + var $hasAndBelongsToMany = array( + 'ScaffoldTag' => array( + 'className' => 'ScaffoldTag', + 'foreignKey' => 'post_id', + 'associationForeignKey' => 'tag_id', + 'joinTable' => 'posts_tags' + ) + ); +} +/** + * ScaffoldUser class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class ScaffoldUser extends CakeTestModel { +/** + * useTable property + * + * @var string 'posts' + * @access public + */ + var $useTable = 'users'; +/** + * hasMany property + * + * @var array + * @access public + */ + var $hasMany = array( + 'Article' => array( + 'className' => 'ScaffoldMock', + 'foreignKey' => 'article_id', + ) + ); +} +/** + * ScaffoldComment class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class ScaffoldComment extends CakeTestModel { +/** + * useTable property + * + * @var string 'posts' + * @access public + */ + var $useTable = 'comments'; +/** + * belongsTo property + * + * @var array + * @access public + */ + var $belongsTo = array( + 'Article' => array( + 'className' => 'ScaffoldMock', + 'foreignKey' => 'article_id', + ) + ); +} +/** + * ScaffoldTag class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class ScaffoldTag extends CakeTestModel { +/** + * useTable property + * + * @var string 'posts' + * @access public + */ + var $useTable = 'tags'; +} +/** + * TestScaffoldView class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class TestScaffoldView extends ScaffoldView { +/** + * testGetFilename method + * + * @param mixed $action + * @access public + * @return void + */ + function testGetFilename($action) { + return $this->_getViewFileName($action); + } +} +/** + * ScaffoldViewTest class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class ScaffoldViewTest extends CakeTestCase { +/** + * fixtures property + * + * @var array + * @access public + */ + var $fixtures = array('core.article', 'core.user', 'core.comment', 'core.posts_tag', 'core.tag'); +/** + * startTest method + * + * @access public + * @return void + */ + function startTest() { + $this->Controller =& new ScaffoldMockController(); + } +/** + * endTest method + * + * @access public + * @return void + */ + function endTest() { + unset($this->Controller); + } +/** + * testGetViewFilename method + * + * @access public + * @return void + */ + function testGetViewFilename() { + $_admin = Configure::read('Routing.admin'); + Configure::write('Routing.admin', 'admin'); + + $this->Controller->action = 'index'; + $ScaffoldView =& new TestScaffoldView($this->Controller); + $result = $ScaffoldView->testGetFilename('index'); + $expected = TEST_CAKE_CORE_INCLUDE_PATH . 'libs' . DS . 'view' . DS . 'scaffolds' . DS . 'index.ctp'; + $this->assertEqual($result, $expected); + + $result = $ScaffoldView->testGetFilename('edit'); + $expected = TEST_CAKE_CORE_INCLUDE_PATH . 'libs' . DS . 'view' . DS . 'scaffolds' . DS . 'edit.ctp'; + $this->assertEqual($result, $expected); + + $result = $ScaffoldView->testGetFilename('add'); + $expected = TEST_CAKE_CORE_INCLUDE_PATH . 'libs' . DS . 'view' . DS . 'scaffolds' . DS . 'edit.ctp'; + $this->assertEqual($result, $expected); + + $result = $ScaffoldView->testGetFilename('view'); + $expected = TEST_CAKE_CORE_INCLUDE_PATH . 'libs' . DS . 'view' . DS . 'scaffolds' . DS . 'view.ctp'; + $this->assertEqual($result, $expected); + + $result = $ScaffoldView->testGetFilename('admin_index'); + $expected = TEST_CAKE_CORE_INCLUDE_PATH . 'libs' . DS . 'view' . DS . 'scaffolds' . DS . 'index.ctp'; + $this->assertEqual($result, $expected); + + $result = $ScaffoldView->testGetFilename('admin_view'); + $expected = TEST_CAKE_CORE_INCLUDE_PATH . 'libs' . DS . 'view' . DS . 'scaffolds' . DS . 'view.ctp'; + $this->assertEqual($result, $expected); + + $result = $ScaffoldView->testGetFilename('admin_edit'); + $expected = TEST_CAKE_CORE_INCLUDE_PATH . 'libs' . DS . 'view' . DS . 'scaffolds' . DS . 'edit.ctp'; + $this->assertEqual($result, $expected); + + $result = $ScaffoldView->testGetFilename('admin_add'); + $expected = TEST_CAKE_CORE_INCLUDE_PATH . 'libs' . DS . 'view' . DS . 'scaffolds' . DS . 'edit.ctp'; + $this->assertEqual($result, $expected); + + $result = $ScaffoldView->testGetFilename('error'); + $expected = 'cake' . DS . 'libs' . DS . 'view' . DS . 'errors' . DS . 'scaffold_error.ctp'; + $this->assertEqual($result, $expected); + + $_back = array( + 'viewPaths' => Configure::read('viewPaths'), + 'pluginPaths' => Configure::read('pluginPaths'), + ); + Configure::write('viewPaths', array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS)); + Configure::write('pluginPaths', array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)); + + $Controller =& new ScaffoldMockController(); + $Controller->scaffold = 'admin'; + $Controller->viewPath = 'posts'; + $Controller->action = 'admin_edit'; + $ScaffoldView =& new TestScaffoldView($Controller); + $result = $ScaffoldView->testGetFilename('admin_edit'); + $expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' .DS . 'views' . DS . 'posts' . DS . 'scaffold.edit.ctp'; + $this->assertEqual($result, $expected); + + $result = $ScaffoldView->testGetFilename('edit'); + $expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' .DS . 'views' . DS . 'posts' . DS . 'scaffold.edit.ctp'; + $this->assertEqual($result, $expected); + + $Controller =& new ScaffoldMockController(); + $Controller->scaffold = 'admin'; + $Controller->viewPath = 'tests'; + $Controller->plugin = 'test_plugin'; + $Controller->action = 'admin_add'; + $ScaffoldView =& new TestScaffoldView($Controller); + $result = $ScaffoldView->testGetFilename('admin_add'); + $expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' + . DS .'test_plugin' . DS . 'views' . DS . 'tests' . DS . 'scaffold.edit.ctp'; + $this->assertEqual($result, $expected); + + $result = $ScaffoldView->testGetFilename('add'); + $expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' + . DS .'test_plugin' . DS . 'views' . DS . 'tests' . DS . 'scaffold.edit.ctp'; + $this->assertEqual($result, $expected); + + Configure::write('viewPaths', $_back['viewPaths']); + Configure::write('pluginPaths', $_back['pluginPaths']); + Configure::write('Routing.admin', $_admin); + } +/** + * test default index scaffold generation + * + * @access public + * @return void + **/ + function testIndexScaffold() { + $this->Controller->action = 'index'; + $this->Controller->here = '/scaffold_mock'; + $this->Controller->webroot = '/'; + $params = array( + 'plugin' => null, + 'pass' => array(), + 'form' => array(), + 'named' => array(), + 'url' => array('url' =>'scaffold_mock'), + 'controller' => 'scaffold_mock', + 'action' => 'index', + ); + //set router. + Router::reload(); + Router::setRequestInfo(array($params, array('base' => '/', 'here' => '/scaffold_mock', 'webroot' => '/'))); + $this->Controller->params = $params; + $this->Controller->controller = 'scaffold_mock'; + $this->Controller->base = '/'; + $this->Controller->constructClasses(); + ob_start(); + new Scaffold($this->Controller, $params); + $result = ob_get_clean(); + + $this->assertPattern('#<h2>Scaffold Mock</h2>#', $result); + $this->assertPattern('#<table cellpadding="0" cellspacing="0">#', $result); + //TODO: add testing for table generation + $this->assertPattern('#<a href="/scaffold_users/view/1">1</a>#', $result); //belongsTo links + $this->assertPattern('#<li><a href="/scaffold_mock/add/">New Scaffold Mock</a></li>#', $result); + $this->assertPattern('#<li><a href="/scaffold_users/">List Scaffold Users</a></li>#', $result); + $this->assertPattern('#<li><a href="/scaffold_comments/add/">New Comment</a></li>#', $result); + } +/** + * test default view scaffold generation + * + * @access public + * @return void + **/ + function testViewScaffold() { + $this->Controller->action = 'view'; + $this->Controller->here = '/scaffold_mock'; + $this->Controller->webroot = '/'; + $params = array( + 'plugin' => null, + 'pass' => array(1), + 'form' => array(), + 'named' => array(), + 'url' => array('url' =>'scaffold_mock'), + 'controller' => 'scaffold_mock', + 'action' => 'view', + ); + //set router. + Router::reload(); + Router::setRequestInfo(array($params, array('base' => '/', 'here' => '/scaffold_mock', 'webroot' => '/'))); + $this->Controller->params = $params; + $this->Controller->controller = 'scaffold_mock'; + $this->Controller->base = '/'; + $this->Controller->constructClasses(); + + ob_start(); + new Scaffold($this->Controller, $params); + $result = ob_get_clean(); + + $this->assertPattern('/<h2>View Scaffold Mock<\/h2>/', $result); + $this->assertPattern('/<dl>/', $result); + //TODO: add specific tests for fields. + $this->assertPattern('/<a href="\/scaffold_users\/view\/1">1<\/a>/', $result); //belongsTo links + $this->assertPattern('/<li><a href="\/scaffold_mock\/edit\/1">Edit Scaffold Mock<\/a>\s<\/li>/', $result); + $this->assertPattern('/<li><a href="\/scaffold_mock\/delete\/1"[^>]*>Delete Scaffold Mock<\/a>\s*<\/li>/', $result); + //check related table + $this->assertPattern('/<div class="related">\s*<h3>Related Scaffold Comments<\/h3>\s*<table cellpadding="0" cellspacing="0">/', $result); + $this->assertPattern('/<li><a href="\/scaffold_comments\/add\/">New Comment<\/a><\/li>/', $result); + } +/** + * test default view scaffold generation + * + * @access public + * @return void + **/ + function testEditScaffold() { + $this->Controller->action = 'edit'; + $this->Controller->here = '/scaffold_mock'; + $this->Controller->webroot = '/'; + $params = array( + 'plugin' => null, + 'pass' => array(1), + 'form' => array(), + 'named' => array(), + 'url' => array('url' =>'scaffold_mock'), + 'controller' => 'scaffold_mock', + 'action' => 'edit', + ); + //set router. + Router::reload(); + Router::setRequestInfo(array($params, array('base' => '/', 'here' => '/scaffold_mock', 'webroot' => '/'))); + $this->Controller->params = $params; + $this->Controller->controller = 'scaffold_mock'; + $this->Controller->base = '/'; + $this->Controller->constructClasses(); + ob_start(); + new Scaffold($this->Controller, $params); + $result = ob_get_clean(); + + $this->assertPattern('/<form id="ScaffoldMockEditForm" method="post" action="\/scaffold_mock\/edit\/1">/', $result); + $this->assertPattern('/<legend>Edit Scaffold Mock<\/legend>/', $result); + + $this->assertPattern('/input type="hidden" name="data\[ScaffoldMock\]\[id\]" value="1" id="ScaffoldMockId"/', $result); + $this->assertPattern('/input name="data\[ScaffoldMock\]\[user_id\]" type="text" maxlength="11" value="1" id="ScaffoldMockUserId"/', $result); + $this->assertPattern('/input name="data\[ScaffoldMock\]\[title\]" type="text" maxlength="255" value="First Article" id="ScaffoldMockTitle"/', $result); + $this->assertPattern('/input name="data\[ScaffoldMock\]\[published\]" type="text" maxlength="1" value="Y" id="ScaffoldMockPublished"/', $result); + $this->assertPattern('/textarea name="data\[ScaffoldMock\]\[body\]" cols="30" rows="6" id="ScaffoldMockBody"/', $result); + $this->assertPattern('/<li><a href="\/scaffold_mock\/delete\/1"[^>]*>Delete<\/a>\s*<\/li>/', $result); + } +/** + * Test Admin Index Scaffolding. + * + * @access public + * @return void + **/ + function testAdminIndexScaffold() { + $_backAdmin = Configure::read('Routing.admin'); + + Configure::write('Routing.admin', 'admin'); + $params = array( + 'plugin' => null, + 'pass' => array(), + 'form' => array(), + 'named' => array(), + 'prefix' => 'admin', + 'url' => array('url' =>'admin/scaffold_mock'), + 'controller' => 'scaffold_mock', + 'action' => 'admin_index', + 'admin' => 1, + ); + //reset, and set router. + Router::reload(); + Router::setRequestInfo(array($params, array('base' => '/', 'here' => '/admin/scaffold_mock', 'webroot' => '/'))); + $this->Controller->params = $params; + $this->Controller->controller = 'scaffold_mock'; + $this->Controller->base = '/'; + $this->Controller->action = 'admin_index'; + $this->Controller->here = '/tests/admin/scaffold_mock'; + $this->Controller->webroot = '/'; + $this->Controller->scaffold = 'admin'; + $this->Controller->constructClasses(); + + ob_start(); + $Scaffold = new Scaffold($this->Controller, $params); + $result = ob_get_clean(); + + $this->assertPattern('/<h2>Scaffold Mock<\/h2>/', $result); + $this->assertPattern('/<table cellpadding="0" cellspacing="0">/', $result); + //TODO: add testing for table generation + $this->assertPattern('/<li><a href="\/admin\/scaffold_mock\/add\/">New Scaffold Mock<\/a><\/li>/', $result); + + Configure::write('Routing.admin', $_backAdmin); + } +/** + * Test Admin Index Scaffolding. + * + * @access public + * @return void + **/ + function testAdminEditScaffold() { + $_backAdmin = Configure::read('Routing.admin'); + + Configure::write('Routing.admin', 'admin'); + $params = array( + 'plugin' => null, + 'pass' => array(), + 'form' => array(), + 'named' => array(), + 'prefix' => 'admin', + 'url' => array('url' =>'admin/scaffold_mock/edit'), + 'controller' => 'scaffold_mock', + 'action' => 'admin_edit', + 'admin' => 1, + ); + //reset, and set router. + Router::reload(); + Router::setRequestInfo(array($params, array('base' => '/', 'here' => '/admin/scaffold_mock/edit', 'webroot' => '/'))); + $this->Controller->params = $params; + $this->Controller->controller = 'scaffold_mock'; + $this->Controller->base = '/'; + $this->Controller->action = 'admin_index'; + $this->Controller->here = '/tests/admin/scaffold_mock'; + $this->Controller->webroot = '/'; + $this->Controller->scaffold = 'admin'; + $this->Controller->constructClasses(); + + ob_start(); + $Scaffold = new Scaffold($this->Controller, $params); + $result = ob_get_clean(); + + $this->assertPattern('#admin/scaffold_mock/edit/1#', $result); + $this->assertPattern('#Scaffold Mock#', $result); + + Configure::write('Routing.admin', $_backAdmin); + } +} +/** + * Scaffold Test class + * + * @package cake + * @subpackage cake.tests.cases.libs.controller + */ +class ScaffoldTest extends CakeTestCase { +/** + * Controller property + * + * @var SecurityTestController + * @access public + */ + var $Controller; +/** + * fixtures property + * + * @var array + * @access public + */ + var $fixtures = array('core.article', 'core.user', 'core.comment', 'core.posts_tag', 'core.tag'); +/** + * startTest method + * + * @access public + * @return void + */ + function startTest() { + $this->Controller =& new ScaffoldMockController(); + } +/** + * endTest method + * + * @access public + * @return void + */ + function endTest() { + unset($this->Controller); + } +/** + * Test the correct Generation of Scaffold Params. + * This ensures that the correct action and view will be generated + * + * @access public + * @return void + */ + function testScaffoldParams() { + $this->Controller->action = 'admin_edit'; + $this->Controller->here = '/admin/scaffold_mock/edit'; + $this->Controller->webroot = '/'; + $params = array( + 'plugin' => null, + 'pass' => array(), + 'form' => array(), + 'named' => array(), + 'url' => array('url' =>'admin/scaffold_mock/edit'), + 'controller' => 'scaffold_mock', + 'action' => 'admin_edit', + 'admin' => true, + ); + //set router. + Router::setRequestInfo(array($params, array('base' => '/', 'here' => 'admin/scaffold_mock', 'webroot' => '/'))); + + $this->Controller->params = $params; + $this->Controller->controller = 'scaffold_mock'; + $this->Controller->base = '/'; + $this->Controller->constructClasses(); + $Scaffold =& new TestScaffoldMock($this->Controller, $params); + $result = $Scaffold->getParams(); + $this->assertEqual($result['action'], 'admin_edit'); + } +/** + * test that the proper names and variable values are set by Scaffold + * + * @return void + **/ + function testScaffoldVariableSetting() { + $this->Controller->action = 'admin_edit'; + $this->Controller->here = '/admin/scaffold_mock/edit'; + $this->Controller->webroot = '/'; + $params = array( + 'plugin' => null, + 'pass' => array(), + 'form' => array(), + 'named' => array(), + 'url' => array('url' =>'admin/scaffold_mock/edit'), + 'controller' => 'scaffold_mock', + 'action' => 'admin_edit', + 'admin' => true, + ); + //set router. + Router::setRequestInfo(array($params, array('base' => '/', 'here' => 'admin/scaffold_mock', 'webroot' => '/'))); + + $this->Controller->params = $params; + $this->Controller->controller = 'scaffold_mock'; + $this->Controller->base = '/'; + $this->Controller->constructClasses(); + $Scaffold =& new TestScaffoldMock($this->Controller, $params); + $result = $Scaffold->controller->viewVars; + + $this->assertEqual($result['singularHumanName'], 'Scaffold Mock'); + $this->assertEqual($result['pluralHumanName'], 'Scaffold Mock'); + $this->assertEqual($result['modelClass'], 'ScaffoldMock'); + $this->assertEqual($result['primaryKey'], 'id'); + $this->assertEqual($result['displayField'], 'title'); + $this->assertEqual($result['singularVar'], 'scaffoldMock'); + $this->assertEqual($result['pluralVar'], 'scaffoldMock'); + $this->assertEqual($result['scaffoldFields'], array('id', 'user_id', 'title', 'body', 'published', 'created', 'updated')); + } +/** + * test that scaffold outputs flash messages when sessions are unset. + * + * @return void + **/ + function testScaffoldFlashMessages() { + $this->Controller->action = 'edit'; + $this->Controller->here = '/scaffold_mock'; + $this->Controller->webroot = '/'; + $params = array( + 'plugin' => null, + 'pass' => array(1), + 'form' => array(), + 'named' => array(), + 'url' => array('url' =>'scaffold_mock'), + 'controller' => 'scaffold_mock', + 'action' => 'edit', + ); + //set router. + Router::reload(); + Router::setRequestInfo(array($params, array('base' => '/', 'here' => '/scaffold_mock', 'webroot' => '/'))); + $this->Controller->params = $params; + $this->Controller->controller = 'scaffold_mock'; + $this->Controller->base = '/'; + $this->Controller->data = array( + 'ScaffoldMock' => array( + 'id' => 1, + 'title' => 'New title', + 'body' => 'new body' + ) + ); + $this->Controller->constructClasses(); + unset($this->Controller->Session); + + ob_start(); + new Scaffold($this->Controller, $params); + $result = ob_get_clean(); + $this->assertPattern('/Scaffold Mock has been updated/', $result); + } +/** + * test that habtm relationship keys get added to scaffoldFields. + * + * @see http://code.cakephp.org/tickets/view/48 + * @return void + **/ + function testHabtmFieldAdditionWithScaffoldForm() { + $this->Controller->action = 'edit'; + $this->Controller->here = '/scaffold_mock'; + $this->Controller->webroot = '/'; + $params = array( + 'plugin' => null, + 'pass' => array(1), + 'form' => array(), + 'named' => array(), + 'url' => array('url' =>'scaffold_mock'), + 'controller' => 'scaffold_mock', + 'action' => 'edit', + ); + //set router. + Router::reload(); + Router::setRequestInfo(array($params, array('base' => '/', 'here' => '/scaffold_mock', 'webroot' => '/'))); + $this->Controller->params = $params; + $this->Controller->controller = 'scaffold_mock'; + $this->Controller->base = '/'; + $this->Controller->constructClasses(); + ob_start(); + $Scaffold = new Scaffold($this->Controller, $params); + $result = ob_get_clean(); + $this->assertPattern('/name="data\[ScaffoldTag\]\[ScaffoldTag\]"/', $result); + + $result = $Scaffold->controller->viewVars; + $this->assertEqual($result['scaffoldFields'], array('id', 'user_id', 'title', 'body', 'published', 'created', 'updated', 'ScaffoldTag')); + } +/** + * test that the proper names and variable values are set by Scaffold + * + * @return void + **/ + function testEditScaffoldWithScaffoldFields() { + $this->Controller = new ScaffoldMockControllerWithFields(); + $this->Controller->action = 'edit'; + $this->Controller->here = '/scaffold_mock'; + $this->Controller->webroot = '/'; + $params = array( + 'plugin' => null, + 'pass' => array(1), + 'form' => array(), + 'named' => array(), + 'url' => array('url' =>'scaffold_mock'), + 'controller' => 'scaffold_mock', + 'action' => 'edit', + ); + //set router. + Router::reload(); + Router::setRequestInfo(array($params, array('base' => '/', 'here' => '/scaffold_mock', 'webroot' => '/'))); + $this->Controller->params = $params; + $this->Controller->controller = 'scaffold_mock'; + $this->Controller->base = '/'; + $this->Controller->constructClasses(); + ob_start(); + new Scaffold($this->Controller, $params); + $result = ob_get_clean(); + + $this->assertNoPattern('/textarea name="data\[ScaffoldMock\]\[body\]" cols="30" rows="6" id="ScaffoldMockBody"/', $result); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/debugger.test.php b/cake/tests/cases/libs/debugger.test.php new file mode 100755 index 0000000..6f1f016 --- /dev/null +++ b/cake/tests/cases/libs/debugger.test.php @@ -0,0 +1,279 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * DebuggerTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs + * @since CakePHP(tm) v 1.2.0.5432 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +App::import('Core', 'Debugger'); +/** + * DebugggerTestCaseDebuggger class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class DebuggerTestCaseDebugger extends Debugger { +} +/** + * DebuggerTest class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class DebuggerTest extends CakeTestCase { +// !!! +// !!! Be careful with changing code below as it may +// !!! change line numbers which are used in the tests +// !!! +/** + * setUp method + * + * @access public + * @return void + */ + function setUp() { + Configure::write('log', false); + if (!defined('SIMPLETESTVENDORPATH')) { + if (file_exists(APP . DS . 'vendors' . DS . 'simpletest' . DS . 'reporter.php')) { + define('SIMPLETESTVENDORPATH', 'APP' . DS . 'vendors'); + } else { + define('SIMPLETESTVENDORPATH', 'CORE' . DS . 'vendors'); + } + } + } +/** + * tearDown method + * + * @access public + * @return void + */ + function tearDown() { + Configure::write('log', true); + } +/** + * testDocRef method + * + * @access public + * @return void + */ + function testDocRef() { + ini_set('docref_root', ''); + $this->assertEqual(ini_get('docref_root'), ''); + $debugger = new Debugger(); + $this->assertEqual(ini_get('docref_root'), 'http://php.net/'); + } +/** + * test Excerpt writing + * + * @access public + * @return void + */ + function testExcerpt() { + $return = Debugger::excerpt(__FILE__, 2, 2); + $this->assertTrue(is_array($return)); + $this->assertEqual(count($return), 4); + $this->assertPattern('#/*&nbsp;SVN&nbsp;FILE:&nbsp;\$Id\$#', $return[1]); + + $return = Debugger::excerpt('[internal]', 2, 2); + $this->assertTrue(empty($return)); + } +/** + * testOutput method + * + * @access public + * @return void + */ + function testOutput() { + Debugger::invoke(Debugger::getInstance()); + $result = Debugger::output(false); + $this->assertEqual($result, ''); + $out .= ''; + $result = Debugger::output(true); + + $this->assertEqual($result[0]['error'], 'Notice'); + $this->assertEqual($result[0]['description'], 'Undefined variable: out'); + $this->assertPattern('/DebuggerTest::testOutput/', $result[0]['trace']); + $this->assertPattern('/SimpleInvoker::invoke/', $result[0]['trace']); + + ob_start(); + Debugger::output('txt'); + $other .= ''; + $result = ob_get_clean(); + + $this->assertPattern('/Undefined variable: other/', $result); + $this->assertPattern('/Context:/', $result); + $this->assertPattern('/DebuggerTest::testOutput/', $result); + $this->assertPattern('/SimpleInvoker::invoke/', $result); + + ob_start(); + Debugger::output('html'); + $wrong .= ''; + $result = ob_get_clean(); + $this->assertPattern('/<pre class="cake-debug">.+<\/pre>/', $result); + $this->assertPattern('/<b>Notice<\/b>/', $result); + $this->assertPattern('/variable: wrong/', $result); + + ob_start(); + Debugger::output('js'); + $buzz .= ''; + $result = ob_get_clean(); + $this->assertPattern("/<a href\='javascript:void\(0\);' onclick\='/", $result); + $this->assertPattern('/<b>Notice<\/b>/', $result); + $this->assertPattern('/Undefined variable: buzz/', $result); + $this->assertPattern('/<a[^>]+>Code<\/a>/', $result); + $this->assertPattern('/<a[^>]+>Context<\/a>/', $result); + set_error_handler('simpleTestErrorHandler'); + } +/** + * testTrimPath method + * + * @access public + * @return void + */ + function testTrimPath() { + $this->assertEqual(Debugger::trimPath(APP), 'APP' . DS); + $this->assertEqual(Debugger::trimPath(CAKE_CORE_INCLUDE_PATH), 'CORE'); + } +/** + * testExportVar method + * + * @access public + * @return void + */ + function testExportVar() { + App::import('Controller'); + $Controller = new Controller(); + $Controller->helpers = array('Html', 'Form'); + $View = new View($Controller); + $result = Debugger::exportVar($View); + $expected = 'ViewView::$base = NULL + View::$here = NULL + View::$plugin = NULL + View::$name = "" + View::$action = NULL + View::$params = array + View::$passedArgs = array + View::$data = array + View::$helpers = array + View::$viewPath = "" + View::$viewVars = array + View::$layout = "default" + View::$layoutPath = NULL + View::$pageTitle = false + View::$autoRender = true + View::$autoLayout = true + View::$ext = ".ctp" + View::$subDir = NULL + View::$themeWeb = NULL + View::$cacheAction = false + View::$validationErrors = array + View::$hasRendered = false + View::$loaded = array + View::$modelScope = false + View::$model = NULL + View::$association = NULL + View::$field = NULL + View::$fieldSuffix = NULL + View::$modelId = NULL + View::$uuids = array + View::$output = false + View::$__passedVars = array + View::$__scripts = array + View::$__paths = array + View::$_log = NULL + View::$webroot = NULL'; + $result = str_replace(array("\t", "\r\n", "\n"), "", $result); + $expected = str_replace(array("\t", "\r\n", "\n"), "", $expected); + $this->assertEqual($result, $expected); + } +/** + * testLog method + * + * @access public + * @return void + */ + function testLog() { + if (file_exists(LOGS . 'debug.log')) { + unlink(LOGS . 'debug.log'); + } + + Debugger::log('cool'); + $result = file_get_contents(LOGS . 'debug.log'); + $this->assertPattern('/DebuggerTest\:\:testLog/', $result); + $this->assertPattern('/"cool"/', $result); + + unlink(TMP . 'logs' . DS . 'debug.log'); + + Debugger::log(array('whatever', 'here')); + $result = file_get_contents(TMP . 'logs' . DS . 'debug.log'); + $this->assertPattern('/DebuggerTest\:\:testLog/', $result); + $this->assertPattern('/array/', $result); + $this->assertPattern('/"whatever",/', $result); + $this->assertPattern('/"here"/', $result); + } +/** + * testDump method + * + * @access public + * @return void + */ + function testDump() { + $var = array('People' => array( + array( + 'name' => 'joeseph', + 'coat' => 'technicolor', + 'hair_color' => 'brown' + ), + array( + 'name' => 'Shaft', + 'coat' => 'black', + 'hair' => 'black' + ) + ) + ); + ob_start(); + Debugger::dump($var); + $result = ob_get_clean(); + $expected = "<pre>array(\n\t\"People\" => array()\n)</pre>"; + $this->assertEqual($expected, $result); + } +/** + * test getInstance. + * + * @access public + * @return void + */ + function testGetInstance() { + $result = Debugger::getInstance(); + $this->assertIsA($result, 'Debugger'); + + $result = Debugger::getInstance('DebuggerTestCaseDebugger'); + $this->assertIsA($result, 'DebuggerTestCaseDebugger'); + + $result = Debugger::getInstance(); + $this->assertIsA($result, 'DebuggerTestCaseDebugger'); + + $result = Debugger::getInstance('Debugger'); + $this->assertIsA($result, 'Debugger'); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/error.test.php b/cake/tests/cases/libs/error.test.php new file mode 100755 index 0000000..e3d233e --- /dev/null +++ b/cake/tests/cases/libs/error.test.php @@ -0,0 +1,465 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * ErrorHandlerTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs + * @since CakePHP(tm) v 1.2.0.5432 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +if (class_exists('TestErrorHandler')) { + return; +} +if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) { + define('CAKEPHP_UNIT_TEST_EXECUTION', 1); +} +/** + * BlueberryComponent class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class BlueberryComponent extends Object { +/** + * testName property + * + * @access public + * @return void + */ + var $testName = null; +/** + * initialize method + * + * @access public + * @return void + */ + function initialize(&$controller) { + $this->testName = 'BlueberryComponent'; + } +} +/** + * BlueberryDispatcher class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class BlueberryDispatcher extends Dispatcher { +/** + * cakeError method + * + * @access public + * @return void + */ + function cakeError($method, $messages = array()) { + $error = new TestErrorHandler($method, $messages); + return $error; + } +} +/** + * Short description for class. + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class AuthBlueberryUser extends CakeTestModel { +/** + * name property + * + * @var string 'AuthBlueberryUser' + * @access public + */ + var $name = 'AuthBlueberryUser'; +/** + * useTable property + * + * @var string + * @access public + */ + var $useTable = false; +} +if (!class_exists('AppController')) { + /** + * AppController class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ + class AppController extends Controller { + /** + * components property + * + * @access public + * @return void + */ + var $components = array('Blueberry'); + /** + * beforeRender method + * + * @access public + * @return void + */ + function beforeRender() { + echo $this->Blueberry->testName; + } + /** + * header method + * + * @access public + * @return void + */ + function header($header) { + echo $header; + } + /** + * _stop method + * + * @access public + * @return void + */ + function _stop($status = 0) { + echo 'Stopped with status: ' . $status; + } + } +} elseif (!defined('APP_CONTROLLER_EXISTS')){ + define('APP_CONTROLLER_EXISTS', true); +} +App::import('Core', array('Error', 'Controller')); +/** + * TestErrorController class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class TestErrorController extends AppController { +/** + * uses property + * + * @var array + * @access public + */ + var $uses = array(); +/** + * index method + * + * @access public + * @return void + */ + function index() { + $this->autoRender = false; + return 'what up'; + } +} +/** + * BlueberryController class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class BlueberryController extends AppController { +/** + * name property + * + * @access public + * @return void + */ + var $name = 'BlueberryController'; +/** + * uses property + * + * @access public + * @return void + */ + var $uses = array('AuthBlueberryUser'); +/** + * components property + * + * @access public + * @return void + */ + var $components = array('Auth'); +} +/** + * TestErrorHandler class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class TestErrorHandler extends ErrorHandler { +/** + * stop method + * + * @access public + * @return void + */ + function _stop() { + return; + } +} +/** + * ErrorHandlerTest class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class ErrorHandlerTest extends CakeTestCase { +/** + * skip method + * + * @access public + * @return void + */ + function skip() { + $this->skipIf(PHP_SAPI === 'cli', '%s Cannot be run from console'); + } +/** + * testError method + * + * @access public + * @return void + */ + function testError() { + ob_start(); + $TestErrorHandler = new TestErrorHandler('error404', array('message' => 'Page not found')); + ob_clean(); + ob_start(); + $TestErrorHandler->error(array( + 'code' => 404, + 'message' => 'Page not Found', + 'name' => "Couldn't find what you were looking for")); + $result = ob_get_clean(); + $this->assertPattern("/<h2>Couldn't find what you were looking for<\/h2>/", $result); + $this->assertPattern('/Page not Found/', $result); + } +/** + * testError404 method + * + * @access public + * @return void + */ + function testError404() { + ob_start(); + $TestErrorHandler = new TestErrorHandler('error404', array('message' => 'Page not found', 'url' => '/test_error')); + $result = ob_get_clean(); + $this->assertPattern('/<h2>Not Found<\/h2>/', $result); + $this->assertPattern("/<strong>'\/test_error'<\/strong>/", $result); + + ob_start(); + $TestErrorHandler =& new TestErrorHandler('error404', array('message' => 'Page not found')); + ob_get_clean(); + ob_start(); + $TestErrorHandler->error404(array( + 'url' => 'pages/<span id=333>pink</span></id><script>document.body.style.background = t=document.getElementById(333).innerHTML;window.alert(t);</script>', + 'message' => 'Page not found' + )); + $result = ob_get_clean(); + $this->assertNoPattern('#<script>#', $result); + $this->assertNoPattern('#</script>#', $result); + } +/** + * testMissingController method + * + * @access public + * @return void + */ + function testMissingController() { + $this->skipIf(defined('APP_CONTROLLER_EXISTS'), '%s Need a non-existent AppController'); + + ob_start(); + $TestErrorHandler = new TestErrorHandler('missingController', array('className' => 'PostsController')); + $result = ob_get_clean(); + $this->assertPattern('/<h2>Missing Controller<\/h2>/', $result); + $this->assertPattern('/<em>PostsController<\/em>/', $result); + $this->assertPattern('/BlueberryComponent/', $result); + } +/** + * testMissingAction method + * + * @access public + * @return void + */ + function testMissingAction() { + ob_start(); + $TestErrorHandler = new TestErrorHandler('missingAction', array('className' => 'PostsController', 'action' => 'index')); + $result = ob_get_clean(); + $this->assertPattern('/<h2>Missing Method in PostsController<\/h2>/', $result); + $this->assertPattern('/<em>PostsController::<\/em><em>index\(\)<\/em>/', $result); + + ob_start(); + $dispatcher = new BlueberryDispatcher('/blueberry/inexistent'); + $result = ob_get_clean(); + $this->assertPattern('/<h2>Missing Method in BlueberryController<\/h2>/', $result); + $this->assertPattern('/<em>BlueberryController::<\/em><em>inexistent\(\)<\/em>/', $result); + $this->assertNoPattern('/Location: (.*)\/users\/login/', $result); + $this->assertNoPattern('/Stopped with status: 0/', $result); + } +/** + * testPrivateAction method + * + * @access public + * @return void + */ + function testPrivateAction() { + ob_start(); + $TestErrorHandler = new TestErrorHandler('privateAction', array('className' => 'PostsController', 'action' => '_secretSauce')); + $result = ob_get_clean(); + $this->assertPattern('/<h2>Private Method in PostsController<\/h2>/', $result); + $this->assertPattern('/<em>PostsController::<\/em><em>_secretSauce\(\)<\/em>/', $result); + } +/** + * testMissingTable method + * + * @access public + * @return void + */ + function testMissingTable() { + ob_start(); + $TestErrorHandler = new TestErrorHandler('missingTable', array('className' => 'Article', 'table' => 'articles')); + $result = ob_get_clean(); + $this->assertPattern('/<h2>Missing Database Table<\/h2>/', $result); + $this->assertPattern('/table <em>articles<\/em> for model <em>Article<\/em>/', $result); + } +/** + * testMissingDatabase method + * + * @access public + * @return void + */ + function testMissingDatabase() { + ob_start(); + $TestErrorHandler = new TestErrorHandler('missingDatabase', array()); + $result = ob_get_clean(); + $this->assertPattern('/<h2>Missing Database Connection<\/h2>/', $result); + $this->assertPattern('/Confirm you have created the file/', $result); + } +/** + * testMissingView method + * + * @access public + * @return void + */ + function testMissingView() { + restore_error_handler(); + ob_start(); + $TestErrorHandler = new TestErrorHandler('missingView', array('className' => 'Pages', 'action' => 'display', 'file' => 'pages/about.ctp', 'base' => '')); + $expected = ob_get_clean(); + set_error_handler('simpleTestErrorHandler'); + $this->assertPattern("/PagesController::/", $expected); + $this->assertPattern("/pages\/about.ctp/", $expected); + } +/** + * testMissingLayout method + * + * @access public + * @return void + */ + function testMissingLayout() { + restore_error_handler(); + ob_start(); + $TestErrorHandler = new TestErrorHandler('missingLayout', array( 'layout' => 'my_layout', 'file' => 'layouts/my_layout.ctp', 'base' => '')); + $expected = ob_get_clean(); + set_error_handler('simpleTestErrorHandler'); + $this->assertPattern("/Missing Layout/", $expected); + $this->assertPattern("/layouts\/my_layout.ctp/", $expected); + } +/** + * testMissingConnection method + * + * @access public + * @return void + */ + function testMissingConnection() { + ob_start(); + $TestErrorHandler = new TestErrorHandler('missingConnection', array('className' => 'Article')); + $result = ob_get_clean(); + $this->assertPattern('/<h2>Missing Database Connection<\/h2>/', $result); + $this->assertPattern('/Article requires a database connection/', $result); + } +/** + * testMissingHelperFile method + * + * @access public + * @return void + */ + function testMissingHelperFile() { + ob_start(); + $TestErrorHandler = new TestErrorHandler('missingHelperFile', array('helper' => 'MyCustom', 'file' => 'my_custom.php')); + $result = ob_get_clean(); + $this->assertPattern('/<h2>Missing Helper File<\/h2>/', $result); + $this->assertPattern('/Create the class below in file:/', $result); + $this->assertPattern('/(\/|\\\)my_custom.php/', $result); + } +/** + * testMissingHelperClass method + * + * @access public + * @return void + */ + function testMissingHelperClass() { + ob_start(); + $TestErrorHandler = new TestErrorHandler('missingHelperClass', array('helper' => 'MyCustom', 'file' => 'my_custom.php')); + $result = ob_get_clean(); + $this->assertPattern('/<h2>Missing Helper Class<\/h2>/', $result); + $this->assertPattern('/The helper class <em>MyCustomHelper<\/em> can not be found or does not exist./', $result); + $this->assertPattern('/(\/|\\\)my_custom.php/', $result); + } +/** + * testMissingComponentFile method + * + * @access public + * @return void + */ + function testMissingComponentFile() { + ob_start(); + $TestErrorHandler = new TestErrorHandler('missingComponentFile', array('className' => 'PostsController', 'component' => 'Sidebox', 'file' => 'sidebox.php')); + $result = ob_get_clean(); + $this->assertPattern('/<h2>Missing Component File<\/h2>/', $result); + $this->assertPattern('/Create the class <em>SideboxComponent<\/em> in file:/', $result); + $this->assertPattern('/(\/|\\\)sidebox.php/', $result); + } +/** + * testMissingComponentClass method + * + * @access public + * @return void + */ + function testMissingComponentClass() { + ob_start(); + $TestErrorHandler = new TestErrorHandler('missingComponentClass', array('className' => 'PostsController', 'component' => 'Sidebox', 'file' => 'sidebox.php')); + $result = ob_get_clean(); + $this->assertPattern('/<h2>Missing Component Class<\/h2>/', $result); + $this->assertPattern('/Create the class <em>SideboxComponent<\/em> in file:/', $result); + $this->assertPattern('/(\/|\\\)sidebox.php/', $result); + } +/** + * testMissingModel method + * + * @access public + * @return void + */ + function testMissingModel() { + ob_start(); + $TestErrorHandler = new TestErrorHandler('missingModel', array('className' => 'Article', 'file' => 'article.php')); + $result = ob_get_clean(); + $this->assertPattern('/<h2>Missing Model<\/h2>/', $result); + $this->assertPattern('/<em>Article<\/em> could not be found./', $result); + $this->assertPattern('/(\/|\\\)article.php/', $result); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/file.test.php b/cake/tests/cases/libs/file.test.php new file mode 100755 index 0000000..f0d64a7 --- /dev/null +++ b/cake/tests/cases/libs/file.test.php @@ -0,0 +1,429 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * FileTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs + * @since CakePHP(tm) v 1.2.0.4206 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +App::import('Core', 'File'); +/** + * FileTest class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class FileTest extends CakeTestCase { +/** + * File property + * + * @var mixed null + * @access public + */ + var $File = null; +/** + * testBasic method + * + * @access public + * @return void + */ + function testBasic() { + $file = __FILE__; + $this->File =& new File($file); + + $result = $this->File->pwd(); + $expecting = $file; + $this->assertEqual($result, $expecting); + + $result = $this->File->name; + $expecting = basename(__FILE__); + $this->assertEqual($result, $expecting); + + $result = $this->File->info(); + $expecting = array( + 'dirname' => dirname(__FILE__), 'basename' => basename(__FILE__), + 'extension' => 'php', 'filename' =>'file.test' + ); + $this->assertEqual($result, $expecting); + + $result = $this->File->ext(); + $expecting = 'php'; + $this->assertEqual($result, $expecting); + + $result = $this->File->name(); + $expecting = 'file.test'; + $this->assertEqual($result, $expecting); + + $result = $this->File->md5(); + $expecting = md5_file($file); + $this->assertEqual($result, $expecting); + + $result = $this->File->md5(true); + $expecting = md5_file($file); + $this->assertEqual($result, $expecting); + + $result = $this->File->size(); + $expecting = filesize($file); + $this->assertEqual($result, $expecting); + + $result = $this->File->owner(); + $expecting = fileowner($file); + $this->assertEqual($result, $expecting); + + $result = $this->File->group(); + $expecting = filegroup($file); + $this->assertEqual($result, $expecting); + + $result = $this->File->Folder(); + $this->assertIsA($result, 'Folder'); + + $this->skipIf(DIRECTORY_SEPARATOR === '\\', '%s File permissions tests not supported on Windows'); + $result = $this->File->perms(); + $expecting = '0644'; + $this->assertEqual($result, $expecting); + } +/** + * testRead method + * + * @access public + * @return void + */ + function testRead() { + $result = $this->File->read(); + $expecting = file_get_contents(__FILE__); + $this->assertEqual($result, $expecting); + $this->assertTrue(!is_resource($this->File->handle)); + + $this->File->lock = true; + $result = $this->File->read(); + $expecting = file_get_contents(__FILE__); + $this->assertEqual($result, $expecting); + $this->File->lock = null; + + $data = $expecting; + $expecting = substr($data, 0, 3); + $result = $this->File->read(3); + $this->assertEqual($result, $expecting); + $this->assertTrue(is_resource($this->File->handle)); + + $expecting = substr($data, 3, 3); + $result = $this->File->read(3); + $this->assertEqual($result, $expecting); + } +/** + * testOffset method + * + * @access public + * @return void + */ + function testOffset() { + $this->File->close(); + + $result = $this->File->offset(); + $this->assertFalse($result); + + $this->assertFalse(is_resource($this->File->handle)); + $success = $this->File->offset(0); + $this->assertTrue($success); + $this->assertTrue(is_resource($this->File->handle)); + + $result = $this->File->offset(); + $expecting = 0; + $this->assertIdentical($result, $expecting); + + $data = file_get_contents(__FILE__); + $success = $this->File->offset(5); + $expecting = substr($data, 5, 3); + $result = $this->File->read(3); + $this->assertTrue($success); + $this->assertEqual($result, $expecting); + + $result = $this->File->offset(); + $expecting = 5+3; + $this->assertIdentical($result, $expecting); + } +/** + * testOpen method + * + * @access public + * @return void + */ + function testOpen() { + $this->File->handle = null; + + $r = $this->File->open(); + $this->assertTrue(is_resource($this->File->handle)); + $this->assertTrue($r); + + $handle = $this->File->handle; + $r = $this->File->open(); + $this->assertTrue($r); + $this->assertTrue($handle === $this->File->handle); + $this->assertTrue(is_resource($this->File->handle)); + + $r = $this->File->open('r', true); + $this->assertTrue($r); + $this->assertFalse($handle === $this->File->handle); + $this->assertTrue(is_resource($this->File->handle)); + } +/** + * testClose method + * + * @access public + * @return void + */ + function testClose() { + $this->File->handle = null; + $this->assertFalse(is_resource($this->File->handle)); + $this->assertTrue($this->File->close()); + $this->assertFalse(is_resource($this->File->handle)); + + $this->File->handle = fopen(__FILE__, 'r'); + $this->assertTrue(is_resource($this->File->handle)); + $this->assertTrue($this->File->close()); + $this->assertFalse(is_resource($this->File->handle)); + } +/** + * testCreate method + * + * @access public + * @return void + */ + function testCreate() { + $tmpFile = TMP.'tests'.DS.'cakephp.file.test.tmp'; + $File =& new File($tmpFile, true, 0777); + $this->assertTrue($File->exists()); + } +/** + * testOpeningNonExistantFileCreatesIt method + * + * @access public + * @return void + */ + function testOpeningNonExistantFileCreatesIt() { + $someFile =& new File(TMP . 'some_file.txt', false); + $this->assertTrue($someFile->open()); + $this->assertEqual($someFile->read(), ''); + $someFile->close(); + $someFile->delete(); + } +/** + * testPrepare method + * + * @access public + * @return void + */ + function testPrepare() { + $string = "some\nvery\ncool\r\nteststring here\n\n\nfor\r\r\n\n\r\n\nhere"; + if (DS == '\\') { + $expected = "some\r\nvery\r\ncool\r\nteststring here\r\n\r\n\r\n"; + $expected .= "for\r\n\r\n\r\n\r\n\r\nhere"; + } else { + $expected = "some\nvery\ncool\nteststring here\n\n\nfor\n\n\n\n\nhere"; + } + $this->assertIdentical(File::prepare($string), $expected); + + $expected = "some\r\nvery\r\ncool\r\nteststring here\r\n\r\n\r\n"; + $expected .= "for\r\n\r\n\r\n\r\n\r\nhere"; + $this->assertIdentical(File::prepare($string, true), $expected); + } +/** + * testReadable method + * + * @access public + * @return void + */ + function testReadable() { + $someFile =& new File(TMP . 'some_file.txt', false); + $this->assertTrue($someFile->open()); + $this->assertTrue($someFile->readable()); + $someFile->close(); + $someFile->delete(); + } +/** + * testWritable method + * + * @access public + * @return void + */ + function testWritable() { + $someFile =& new File(TMP . 'some_file.txt', false); + $this->assertTrue($someFile->open()); + $this->assertTrue($someFile->writable()); + $someFile->close(); + $someFile->delete(); + } +/** + * testExecutable method + * + * @access public + * @return void + */ + function testExecutable() { + $someFile =& new File(TMP . 'some_file.txt', false); + $this->assertTrue($someFile->open()); + $this->assertFalse($someFile->executable()); + $someFile->close(); + $someFile->delete(); + } +/** + * testLastAccess method + * + * @access public + * @return void + */ + function testLastAccess() { + $someFile =& new File(TMP . 'some_file.txt', false); + $this->assertFalse($someFile->lastAccess()); + $this->assertTrue($someFile->open()); + $this->assertEqual($someFile->lastAccess(), time()); + $someFile->close(); + $someFile->delete(); + } +/** + * testLastChange method + * + * @access public + * @return void + */ + function testLastChange() { + $someFile =& new File(TMP . 'some_file.txt', false); + $this->assertFalse($someFile->lastChange()); + $this->assertTrue($someFile->open('r+')); + $this->assertEqual($someFile->lastChange(), time()); + $someFile->write('something'); + $this->assertEqual($someFile->lastChange(), time()); + $someFile->close(); + $someFile->delete(); + } +/** + * testWrite method + * + * @access public + * @return void + */ + function testWrite() { + if (!$tmpFile = $this->_getTmpFile()) { + return false; + }; + if (file_exists($tmpFile)) { + unlink($tmpFile); + } + + $TmpFile =& new File($tmpFile); + $this->assertFalse(file_exists($tmpFile)); + $this->assertFalse(is_resource($TmpFile->handle)); + + $testData = array('CakePHP\'s', ' test suite', ' was here ...', ''); + foreach ($testData as $data) { + $r = $TmpFile->write($data); + $this->assertTrue($r); + $this->assertTrue(file_exists($tmpFile)); + $this->assertEqual($data, file_get_contents($tmpFile)); + $this->assertTrue(is_resource($TmpFile->handle)); + $TmpFile->close(); + + } + unlink($tmpFile); + } +/** + * testAppend method + * + * @access public + * @return void + */ + function testAppend() { + if (!$tmpFile = $this->_getTmpFile()) { + return false; + }; + if (file_exists($tmpFile)) { + unlink($tmpFile); + } + + $TmpFile =& new File($tmpFile); + $this->assertFalse(file_exists($tmpFile)); + + $fragments = array('CakePHP\'s', ' test suite', ' was here ...', ''); + $data = null; + foreach ($fragments as $fragment) { + $r = $TmpFile->append($fragment); + $this->assertTrue($r); + $this->assertTrue(file_exists($tmpFile)); + $data = $data.$fragment; + $this->assertEqual($data, file_get_contents($tmpFile)); + $TmpFile->close(); + } + } +/** + * testDelete method + * + * @access public + * @return void + */ + function testDelete() { + if (!$tmpFile = $this->_getTmpFile()) { + return false; + }; + + if (!file_exists($tmpFile)) { + touch($tmpFile); + } + $TmpFile =& new File($tmpFile); + $this->assertTrue(file_exists($tmpFile)); + $result = $TmpFile->delete(); + $this->assertTrue($result); + $this->assertFalse(file_exists($tmpFile)); + + $TmpFile =& new File('/this/does/not/exist'); + $result = $TmpFile->delete(); + $this->assertFalse($result); + } +/** + * getTmpFile method + * + * @param bool $paintSkip + * @access protected + * @return void + */ + function _getTmpFile($paintSkip = true) { + $tmpFile = TMP.'tests'.DS.'cakephp.file.test.tmp'; + if (is_writable(dirname($tmpFile)) && (!file_exists($tmpFile) || is_writable($tmpFile))) { + return $tmpFile; + }; + + if ($paintSkip) { + $caller = 'test'; + if (function_exists('debug_backtrace')) { + $trace = debug_backtrace(); + $caller = $trace[1]['function'].'()'; + } + $assertLine = new SimpleStackTrace(array(__FUNCTION__)); + $assertLine = $assertLine->traceMethod(); + $shortPath = substr($tmpFile, strlen(ROOT)); + + $message = '[FileTest] Skipping %s because "%s" not writeable!'; + $message = sprintf(__($message, true), $caller, $shortPath).$assertLine; + $this->_reporter->paintSkip($message); + } + return false; + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/flay.test.php b/cake/tests/cases/libs/flay.test.php new file mode 100755 index 0000000..79e55c9 --- /dev/null +++ b/cake/tests/cases/libs/flay.test.php @@ -0,0 +1,45 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * TestFlay file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs + * @since CakePHP(tm) v 1.2.0.5432 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +uses('flay'); +/** + * FlayTest class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class FlayTest extends CakeTestCase { +/** + * skip method + * + * @access public + * @return void + */ + function skip() { + $this->skipIf(true, '%s FlayTest not implemented'); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/folder.test.php b/cake/tests/cases/libs/folder.test.php new file mode 100755 index 0000000..6fad570 --- /dev/null +++ b/cake/tests/cases/libs/folder.test.php @@ -0,0 +1,706 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * FolderTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs + * @since CakePHP(tm) v 1.2.0.4206 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +App::import('Core', 'File'); +/** + * FolderTest class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class FolderTest extends CakeTestCase { +/** + * testBasic method + * + * @access public + * @return void + */ + function testBasic() { + $path = dirname(__FILE__); + $Folder =& new Folder($path); + + $result = $Folder->pwd(); + $this->assertEqual($result, $path); + + $result = Folder::addPathElement($path, 'test'); + $expected = $path . DS . 'test'; + $this->assertEqual($result, $expected); + + $result = $Folder->cd(ROOT); + $expected = ROOT; + $this->assertEqual($result, $expected); + + $result = $Folder->cd(ROOT . DS . 'non-existent'); + $this->assertFalse($result); + } +/** + * testInPath method + * + * @access public + * @return void + */ + function testInPath() { + $path = dirname(dirname(__FILE__)); + $inside = dirname($path) . DS; + + $Folder =& new Folder($path); + + $result = $Folder->pwd(); + $this->assertEqual($result, $path); + + $result = Folder::isSlashTerm($inside); + $this->assertTrue($result); + + $result = $Folder->realpath('tests/'); + $this->assertEqual($result, $path . DS .'tests' . DS); + + $result = $Folder->inPath('tests' . DS); + $this->assertTrue($result); + + $result = $Folder->inPath(DS . 'non-existing' . $inside); + $this->assertFalse($result); + } +/** + * testOperations method + * + * @access public + * @return void + */ + function testOperations() { + $path = TEST_CAKE_CORE_INCLUDE_PATH . 'console' . DS . 'libs' . DS . 'templates' . DS . 'skel'; + $Folder =& new Folder($path); + + $result = is_dir($Folder->pwd()); + $this->assertTrue($result); + + $new = TMP . 'test_folder_new'; + $result = $Folder->create($new); + $this->assertTrue($result); + + $copy = TMP . 'test_folder_copy'; + $result = $Folder->copy($copy); + $this->assertTrue($result); + + $copy = TMP . 'test_folder_copy'; + $result = $Folder->cp($copy); + $this->assertTrue($result); + + $copy = TMP . 'test_folder_copy'; + $result = $Folder->chmod($copy, 0755, false); + $this->assertTrue($result); + + $result = $Folder->cd($copy); + $this->assertTrue($result); + + $mv = TMP . 'test_folder_mv'; + $result = $Folder->move($mv); + $this->assertTrue($result); + + $mv = TMP . 'test_folder_mv_2'; + $result = $Folder->mv($mv); + $this->assertTrue($result); + + $result = $Folder->delete($new); + $this->assertTrue($result); + + $result = $Folder->delete($mv); + $this->assertTrue($result); + + $result = $Folder->rm($mv); + $this->assertTrue($result); + + $new = APP . 'index.php'; + $result = $Folder->create($new); + $this->assertFalse($result); + + $expected = $new . ' is a file'; + $result = array_pop($Folder->errors()); + $this->assertEqual($result, $expected); + + $new = TMP . 'test_folder_new'; + $result = $Folder->create($new); + $this->assertTrue($result); + + $result = $Folder->cd($new); + $this->assertTrue($result); + + $result = $Folder->delete(); + $this->assertTrue($result); + + $Folder =& new Folder('non-existent'); + $result = $Folder->pwd(); + $this->assertNull($result); + } +/** + * testChmod method + * + * @return void + * @access public + */ + function testChmod() { + $this->skipIf(DIRECTORY_SEPARATOR === '\\', '%s Folder permissions tests not supported on Windows'); + + $path = TEST_CAKE_CORE_INCLUDE_PATH . 'console' . DS . 'libs' . DS . 'templates' . DS . 'skel'; + $Folder =& new Folder($path); + + $subdir = 'test_folder_new'; + $new = TMP . $subdir; + + $this->assertTrue($Folder->create($new)); + $this->assertTrue($Folder->create($new . DS . 'test1')); + $this->assertTrue($Folder->create($new . DS . 'test2')); + + $filePath = $new . DS . 'test1.php'; + $File =& new File($filePath); + $this->assertTrue($File->create()); + $copy = TMP . 'test_folder_copy'; + + $this->assertTrue($Folder->chmod($new, 0777, true)); + $this->assertEqual($File->perms(), '0777'); + + $Folder->delete($new); + } +/** + * testRealPathForWebroot method + * + * @access public + * @return void + */ + function testRealPathForWebroot() { + $Folder = new Folder('files/'); + $this->assertEqual(realpath('files/'), $Folder->path); + } +/** + * testZeroAsDirectory method + * + * @access public + * @return void + */ + function testZeroAsDirectory() { + $Folder =& new Folder(TMP); + $new = TMP . '0'; + $this->assertTrue($Folder->create($new)); + + $result = $Folder->read(true, true); + $expected = array('0', 'cache', 'logs', 'sessions', 'tests'); + $this->assertEqual($expected, $result[0]); + + $result = $Folder->read(true, array('.', '..', 'logs', '.svn')); + $expected = array('0', 'cache', 'sessions', 'tests'); + $this->assertEqual($expected, $result[0]); + + $result = $Folder->delete($new); + $this->assertTrue($result); + } +/** + * testFolderRead method + * + * @access public + * @return void + */ + function testFolderRead() { + $Folder =& new Folder(TMP); + + $expected = array('cache', 'logs', 'sessions', 'tests'); + $result = $Folder->read(true, true); + $this->assertEqual($result[0], $expected); + + $Folder->path = TMP . DS . 'non-existent'; + $expected = array(array(), array()); + $result = $Folder->read(true, true); + $this->assertEqual($result, $expected); + } +/** + * testFolderTree method + * + * @access public + * @return void + */ + function testFolderTree() { + $Folder =& new Folder(); + $expected = array( + array( + TEST_CAKE_CORE_INCLUDE_PATH . 'config', + TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode', + TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' . DS . 'casefolding' + ), + array( + TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'config.php', + TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'paths.php', + TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' . DS . 'casefolding' . DS . '0080_00ff.php', + TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' . DS . 'casefolding' . DS . '0100_017f.php', + TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' . DS . 'casefolding' . DS . '0180_024F.php', + TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' . DS . 'casefolding' . DS . '0250_02af.php', + TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' . DS . 'casefolding' . DS . '0370_03ff.php', + TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' . DS . 'casefolding' . DS . '0400_04ff.php', + TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' . DS . 'casefolding' . DS . '0500_052f.php', + TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' . DS . 'casefolding' . DS . '0530_058f.php', + TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' . DS . 'casefolding' . DS . '1e00_1eff.php', + TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' . DS . 'casefolding' . DS . '1f00_1fff.php', + TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' . DS . 'casefolding' . DS . '2100_214f.php', + TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' . DS . 'casefolding' . DS . '2150_218f.php', + TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' . DS . 'casefolding' . DS . '2460_24ff.php', + TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' . DS . 'casefolding' . DS . '2c00_2c5f.php', + TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' . DS . 'casefolding' . DS . '2c60_2c7f.php', + TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' . DS . 'casefolding' . DS . '2c80_2cff.php', + TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' . DS . 'casefolding' . DS . 'ff00_ffef.php' + ) + ); + + $result = $Folder->tree(TEST_CAKE_CORE_INCLUDE_PATH . 'config', false); + $this->assertIdentical(array_diff($expected[0], $result[0]), array()); + $this->assertIdentical(array_diff($result[0], $expected[0]), array()); + + $result = $Folder->tree(TEST_CAKE_CORE_INCLUDE_PATH . 'config', false, 'dir'); + $this->assertIdentical(array_diff($expected[0], $result), array()); + $this->assertIdentical(array_diff($result, $expected[0]), array()); + + $result = $Folder->tree(TEST_CAKE_CORE_INCLUDE_PATH . 'config', false, 'files'); + $this->assertIdentical(array_diff($expected[1], $result), array()); + $this->assertIdentical(array_diff($result, $expected[1]), array()); + } +/** + * testWindowsPath method + * + * @access public + * @return void + */ + function testWindowsPath() { + $this->assertFalse(Folder::isWindowsPath('0:\\cake\\is\\awesome')); + $this->assertTrue(Folder::isWindowsPath('C:\\cake\\is\\awesome')); + $this->assertTrue(Folder::isWindowsPath('d:\\cake\\is\\awesome')); + } +/** + * testIsAbsolute method + * + * @access public + * @return void + */ + function testIsAbsolute() { + $this->assertFalse(Folder::isAbsolute('path/to/file')); + $this->assertFalse(Folder::isAbsolute('cake/')); + $this->assertFalse(Folder::isAbsolute('path\\to\\file')); + $this->assertFalse(Folder::isAbsolute('0:\\path\\to\\file')); + $this->assertFalse(Folder::isAbsolute('\\path/to/file')); + $this->assertFalse(Folder::isAbsolute('\\path\\to\\file')); + + $this->assertTrue(Folder::isAbsolute('/usr/local')); + $this->assertTrue(Folder::isAbsolute('//path/to/file')); + $this->assertTrue(Folder::isAbsolute('C:\\cake')); + $this->assertTrue(Folder::isAbsolute('C:\\path\\to\\file')); + $this->assertTrue(Folder::isAbsolute('d:\\path\\to\\file')); + } +/** + * testIsSlashTerm method + * + * @access public + * @return void + */ + function testIsSlashTerm() { + $this->assertFalse(Folder::isSlashTerm('cake')); + + $this->assertTrue(Folder::isSlashTerm('C:\\cake\\')); + $this->assertTrue(Folder::isSlashTerm('/usr/local/')); + } +/** + * testStatic method + * + * @access public + * @return void + */ + function testSlashTerm() { + $result = Folder::slashTerm('/path/to/file'); + $this->assertEqual($result, '/path/to/file/'); + } +/** + * testNormalizePath method + * + * @access public + * @return void + */ + function testNormalizePath() { + $path = '/path/to/file'; + $result = Folder::normalizePath($path); + $this->assertEqual($result, '/'); + + $path = '\\path\\\to\\\file'; + $result = Folder::normalizePath($path); + $this->assertEqual($result, '/'); + + $path = 'C:\\path\\to\\file'; + $result = Folder::normalizePath($path); + $this->assertEqual($result, '\\'); + } +/** + * correctSlashFor method + * + * @access public + * @return void + */ + function testCorrectSlashFor() { + $path = '/path/to/file'; + $result = Folder::correctSlashFor($path); + $this->assertEqual($result, '/'); + + $path = '\\path\\to\\file'; + $result = Folder::correctSlashFor($path); + $this->assertEqual($result, '/'); + + $path = 'C:\\path\to\\file'; + $result = Folder::correctSlashFor($path); + $this->assertEqual($result, '\\'); + } +/** + * testInCakePath method + * + * @access public + * @return void + */ + function testInCakePath() { + $Folder =& new Folder(); + $Folder->cd(ROOT); + $path = 'C:\\path\\to\\file'; + $result = $Folder->inCakePath($path); + $this->assertFalse($result); + + $path = ROOT; + $Folder->cd(ROOT); + $result = $Folder->inCakePath($path); + $this->assertFalse($result); + + // WHY DOES THIS FAIL ?? + $path = DS . 'cake' . DS . 'config'; + $Folder->cd(ROOT . DS . 'cake' . DS . 'config'); + $result = $Folder->inCakePath($path); + $this->assertTrue($result); + } +/** + * testFind method + * + * @access public + * @return void + */ + function testFind() { + $Folder =& new Folder(); + $Folder->cd(TEST_CAKE_CORE_INCLUDE_PATH . 'config'); + $result = $Folder->find(); + $expected = array('config.php', 'paths.php'); + $this->assertIdentical(array_diff($expected, $result), array()); + $this->assertIdentical(array_diff($result, $expected), array()); + + $result = $Folder->find('.*', true); + $expected = array('config.php', 'paths.php'); + $this->assertIdentical($result, $expected); + + $result = $Folder->find('.*\.php'); + $expected = array('config.php', 'paths.php'); + $this->assertIdentical(array_diff($expected, $result), array()); + $this->assertIdentical(array_diff($result, $expected), array()); + + $result = $Folder->find('.*\.php', true); + $expected = array('config.php', 'paths.php'); + $this->assertIdentical($result, $expected); + + $result = $Folder->find('.*ig\.php'); + $expected = array('config.php'); + $this->assertIdentical($result, $expected); + + $result = $Folder->find('paths\.php'); + $expected = array('paths.php'); + $this->assertIdentical($result, $expected); + + $Folder->cd(TMP); + $file = new File($Folder->pwd() . DS . 'paths.php', true); + $Folder->mkdir($Folder->pwd() . DS . 'testme'); + $Folder->cd('testme'); + $result = $Folder->find('paths\.php'); + $expected = array(); + $this->assertIdentical($result, $expected); + + $Folder->cd($Folder->pwd() . '/..'); + $result = $Folder->find('paths\.php'); + $expected = array('paths.php'); + $this->assertIdentical($result, $expected); + + $Folder->cd(TMP); + $Folder->delete($Folder->pwd() . DS . 'testme'); + $file->delete(); + } +/** + * testFindRecursive method + * + * @access public + * @return void + */ + function testFindRecursive() { + $Folder =& new Folder(); + $Folder->cd(TEST_CAKE_CORE_INCLUDE_PATH); + $result = $Folder->findRecursive('(config|paths)\.php'); + $expected = array( + TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'config.php', + TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'paths.php' + ); + $this->assertIdentical(array_diff($expected, $result), array()); + $this->assertIdentical(array_diff($result, $expected), array()); + + $result = $Folder->findRecursive('(config|paths)\.php', true); + $expected = array( + TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'config.php', + TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'paths.php' + ); + $this->assertIdentical($result, $expected); + + $Folder->cd(TMP); + $Folder->mkdir($Folder->pwd() . DS . 'testme'); + $Folder->cd('testme'); + $File =& new File($Folder->pwd() . DS . 'paths.php'); + $File->create(); + $Folder->cd(TMP . 'sessions'); + $result = $Folder->findRecursive('paths\.php'); + $expected = array(); + $this->assertIdentical($result, $expected); + + $Folder->cd(TMP . 'testme'); + $File =& new File($Folder->pwd() . DS . 'my.php'); + $File->create(); + $Folder->cd($Folder->pwd() . '/../..'); + + $result = $Folder->findRecursive('(paths|my)\.php'); + $expected = array( + TMP . 'testme' . DS . 'my.php', + TMP . 'testme' . DS . 'paths.php' + ); + $this->assertIdentical(array_diff($expected, $result), array()); + $this->assertIdentical(array_diff($result, $expected), array()); + + $result = $Folder->findRecursive('(paths|my)\.php', true); + $expected = array( + TMP . 'testme' . DS . 'my.php', + TMP . 'testme' . DS . 'paths.php' + ); + $this->assertIdentical($result, $expected); + + $Folder->cd(TEST_CAKE_CORE_INCLUDE_PATH . 'config'); + $Folder->cd(TMP); + $Folder->delete($Folder->pwd() . DS . 'testme'); + $File->delete(); + } +/** + * testConstructWithNonExistantPath method + * + * @access public + * @return void + */ + function testConstructWithNonExistantPath() { + $Folder =& new Folder(TMP . 'config_non_existant', true); + $this->assertTrue(is_dir(TMP . 'config_non_existant')); + $Folder->cd(TMP); + $Folder->delete($Folder->pwd() . 'config_non_existant'); + } +/** + * testDirSize method + * + * @access public + * @return void + */ + function testDirSize() { + $Folder =& new Folder(TMP . 'config_non_existant', true); + $this->assertEqual($Folder->dirSize(), 0); + + $File =& new File($Folder->pwd() . DS . 'my.php', true, 0777); + $File->create(); + $File->write('something here'); + $File->close(); + $this->assertEqual($Folder->dirSize(), 14); + + $Folder->cd(TMP); + $Folder->delete($Folder->pwd() . 'config_non_existant'); + } +/** + * testDelete method + * + * @access public + * @return void + */ + function testDelete() { + $path = TMP . 'folder_delete_test'; + $Folder =& new Folder($path, true); + touch(TMP . 'folder_delete_test' . DS . 'file1'); + touch(TMP . 'folder_delete_test' . DS . 'file2'); + + $return = $Folder->delete(); + $this->assertTrue($return); + + $messages = $Folder->messages(); + $errors = $Folder->errors(); + $this->assertEqual($errors, array()); + + $expected = array( + $path . ' created', + $path . DS . 'file1 removed', + $path . DS . 'file2 removed', + $path . ' removed' + ); + $this->assertEqual($expected, $messages); + } +/** + * testCopy method + * + * Verify that directories and files are copied recursively + * even if the destination directory already exists. + * Subdirectories existing in both destination and source directory + * are skipped and not merged or overwritten. + * + * @return void + * @access public + * @link https://trac.cakephp.org/ticket/6259 + */ + function testCopy() { + $path = TMP . 'folder_test'; + $folder1 = $path . DS . 'folder1'; + $folder2 = $folder1 . DS . 'folder2'; + $folder3 = $path . DS . 'folder3'; + $file1 = $folder1 . DS . 'file1.php'; + $file2 = $folder2 . DS . 'file2.php'; + + new Folder($path, true); + new Folder($folder1, true); + new Folder($folder2, true); + new Folder($folder3, true); + touch($file1); + touch($file2); + + $Folder =& new Folder($folder1); + $result = $Folder->copy($folder3); + $this->assertTrue($result); + $this->assertTrue(file_exists($folder3 . DS . 'file1.php')); + $this->assertTrue(file_exists($folder3 . DS . 'folder2' . DS . 'file2.php')); + + $Folder =& new Folder($folder3); + $Folder->delete(); + + $Folder =& new Folder($folder1); + $result = $Folder->copy($folder3); + $this->assertTrue($result); + $this->assertTrue(file_exists($folder3 . DS . 'file1.php')); + $this->assertTrue(file_exists($folder3 . DS . 'folder2' . DS . 'file2.php')); + + $Folder =& new Folder($folder3); + $Folder->delete(); + + new Folder($folder3, true); + new Folder($folder3 . DS . 'folder2', true); + file_put_contents($folder3 . DS . 'folder2' . DS . 'file2.php', 'untouched'); + + $Folder =& new Folder($folder1); + $result = $Folder->copy($folder3); + $this->assertTrue($result); + $this->assertTrue(file_exists($folder3 . DS . 'file1.php')); + $this->assertEqual(file_get_contents($folder3 . DS . 'folder2' . DS . 'file2.php'), 'untouched'); + + $Folder =& new Folder($path); + $Folder->delete(); + } +/** + * testMove method + * + * Verify that directories and files are moved recursively + * even if the destination directory already exists. + * Subdirectories existing in both destination and source directory + * are skipped and not merged or overwritten. + * + * @return void + * @access public + * @link https://trac.cakephp.org/ticket/6259 + */ + function testMove() { + $path = TMP . 'folder_test'; + $folder1 = $path . DS . 'folder1'; + $folder2 = $folder1 . DS . 'folder2'; + $folder3 = $path . DS . 'folder3'; + $file1 = $folder1 . DS . 'file1.php'; + $file2 = $folder2 . DS . 'file2.php'; + + new Folder($path, true); + new Folder($folder1, true); + new Folder($folder2, true); + new Folder($folder3, true); + touch($file1); + touch($file2); + + $Folder =& new Folder($folder1); + $result = $Folder->move($folder3); + $this->assertTrue($result); + $this->assertTrue(file_exists($folder3 . DS . 'file1.php')); + $this->assertTrue(is_dir($folder3 . DS . 'folder2')); + $this->assertTrue(file_exists($folder3 . DS . 'folder2' . DS . 'file2.php')); + $this->assertFalse(file_exists($file1)); + $this->assertFalse(file_exists($folder2)); + $this->assertFalse(file_exists($file2)); + + $Folder =& new Folder($folder3); + $Folder->delete(); + + new Folder($folder1, true); + new Folder($folder2, true); + touch($file1); + touch($file2); + + $Folder =& new Folder($folder1); + $result = $Folder->move($folder3); + $this->assertTrue($result); + $this->assertTrue(file_exists($folder3 . DS . 'file1.php')); + $this->assertTrue(is_dir($folder3 . DS . 'folder2')); + $this->assertTrue(file_exists($folder3 . DS . 'folder2' . DS . 'file2.php')); + $this->assertFalse(file_exists($file1)); + $this->assertFalse(file_exists($folder2)); + $this->assertFalse(file_exists($file2)); + + $Folder =& new Folder($folder3); + $Folder->delete(); + + new Folder($folder1, true); + new Folder($folder2, true); + new Folder($folder3, true); + new Folder($folder3 . DS . 'folder2', true); + touch($file1); + touch($file2); + file_put_contents($folder3 . DS . 'folder2' . DS . 'file2.php', 'untouched'); + + $Folder =& new Folder($folder1); + $result = $Folder->move($folder3); + $this->assertTrue($result); + $this->assertTrue(file_exists($folder3 . DS . 'file1.php')); + $this->assertEqual(file_get_contents($folder3 . DS . 'folder2' . DS . 'file2.php'), 'untouched'); + $this->assertFalse(file_exists($file1)); + $this->assertFalse(file_exists($folder2)); + $this->assertFalse(file_exists($file2)); + + $Folder =& new Folder($path); + $Folder->delete(); + } +} +?> diff --git a/cake/tests/cases/libs/http_socket.test.php b/cake/tests/cases/libs/http_socket.test.php new file mode 100755 index 0000000..b3fa948 --- /dev/null +++ b/cake/tests/cases/libs/http_socket.test.php @@ -0,0 +1,1338 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * HttpSocketTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs + * @since CakePHP(tm) v 1.2.0.4206 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +App::import('Core', 'HttpSocket'); +/** + * HttpSocketTest class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class HttpSocketTest extends CakeTestCase { +/** + * Socket property + * + * @var mixed null + * @access public + */ + var $Socket = null; +/** + * RequestSocket property + * + * @var mixed null + * @access public + */ + var $RequestSocket = null; +/** + * This function sets up a TestHttpSocket instance we are going to use for testing + * + * @access public + * @return void + */ + function setUp() { + if (!class_exists('TestHttpSocket')) { + Mock::generatePartial('HttpSocket', 'TestHttpSocket', array('read', 'write', 'connect')); + Mock::generatePartial('HttpSocket', 'TestHttpSocketRequests', array('read', 'write', 'connect', 'request')); + } + + $this->Socket =& new TestHttpSocket(); + $this->RequestSocket =& new TestHttpSocketRequests(); + } +/** + * We use this function to clean up after the test case was executed + * + * @access public + * @return void + */ + function tearDown() { + unset($this->Socket, $this->RequestSocket); + } +/** + * Test that HttpSocket::__construct does what one would expect it to do + * + * @access public + * @return void + */ + function testConstruct() { + $this->Socket->reset(); + $baseConfig = $this->Socket->config; + $this->Socket->expectNever('connect'); + $this->Socket->__construct(array('host' => 'foo-bar')); + $baseConfig['host'] = 'foo-bar'; + $baseConfig['protocol'] = getprotobyname($baseConfig['protocol']); + $this->assertIdentical($this->Socket->config, $baseConfig); + $this->Socket->reset(); + $baseConfig = $this->Socket->config; + $this->Socket->__construct('http://www.cakephp.org:23/'); + $baseConfig['host'] = 'www.cakephp.org'; + $baseConfig['request']['uri']['host'] = 'www.cakephp.org'; + $baseConfig['port'] = 23; + $baseConfig['request']['uri']['port'] = 23; + $baseConfig['protocol'] = getprotobyname($baseConfig['protocol']); + $this->assertIdentical($this->Socket->config, $baseConfig); + + $this->Socket->reset(); + $this->Socket->__construct(array('request' => array('uri' => 'http://www.cakephp.org:23/'))); + $this->assertIdentical($this->Socket->config, $baseConfig); + } +/** + * Test that HttpSocket::configUri works properly with different types of arguments + * + * @access public + * @return void + */ + function testConfigUri() { + $this->Socket->reset(); + $r = $this->Socket->configUri('https://bob:secret@www.cakephp.org:23/?query=foo'); + $expected = array( + 'persistent' => false, + 'host' => 'www.cakephp.org', + 'protocol' => 'tcp', + 'port' => 23, + 'timeout' => 30, + 'request' => array( + 'uri' => array( + 'scheme' => 'https' + , 'host' => 'www.cakephp.org' + , 'port' => 23 + ), + 'auth' => array( + 'method' => 'Basic' + , 'user' => 'bob' + , 'pass' => 'secret' + ), + 'cookies' => array(), + ) + ); + $this->assertIdentical($this->Socket->config, $expected); + $this->assertIdentical($r, $expected); + $r = $this->Socket->configUri(array('host' => 'www.foo-bar.org')); + $expected['host'] = 'www.foo-bar.org'; + $expected['request']['uri']['host'] = 'www.foo-bar.org'; + $this->assertIdentical($this->Socket->config, $expected); + $this->assertIdentical($r, $expected); + + $r = $this->Socket->configUri('http://www.foo.com'); + $expected = array( + 'persistent' => false, + 'host' => 'www.foo.com', + 'protocol' => 'tcp', + 'port' => 80, + 'timeout' => 30, + 'request' => array( + 'uri' => array( + 'scheme' => 'http' + , 'host' => 'www.foo.com' + , 'port' => 80 + ), + 'auth' => array( + 'method' => 'Basic' + , 'user' => null + , 'pass' => null + ), + 'cookies' => array() + ) + ); + $this->assertIdentical($this->Socket->config, $expected); + $this->assertIdentical($r, $expected); + $r = $this->Socket->configUri('/this-is-broken'); + $this->assertIdentical($this->Socket->config, $expected); + $this->assertIdentical($r, false); + $r = $this->Socket->configUri(false); + $this->assertIdentical($this->Socket->config, $expected); + $this->assertIdentical($r, false); + } +/** + * Tests that HttpSocket::request (the heart of the HttpSocket) is working properly. + * + * @access public + * @return void + */ + function testRequest() { + $this->Socket->reset(); + + $this->Socket->reset(); + $response = $this->Socket->request(true); + $this->assertFalse($response); + + $tests = array( + 0 => array( + 'request' => 'http://www.cakephp.org/?foo=bar' + , 'expectation' => array( + 'config' => array( + 'persistent' => false + , 'host' => 'www.cakephp.org' + , 'protocol' => 'tcp' + , 'port' => 80 + , 'timeout' => 30 + , 'request' => array( + 'uri' => array ( + 'scheme' => 'http' + , 'host' => 'www.cakephp.org' + , 'port' => 80, + ) + , 'auth' => array( + 'method' => 'Basic' + ,'user' => null + ,'pass' => null + ), + 'cookies' => array(), + ), + ) + , 'request' => array( + 'method' => 'GET' + , 'uri' => array( + 'scheme' => 'http' + , 'host' => 'www.cakephp.org' + , 'port' => 80 + , 'user' => null + , 'pass' => null + , 'path' => '/' + , 'query' => array('foo' => 'bar') + , 'fragment' => null + ) + , 'auth' => array( + 'method' => 'Basic' + , 'user' => null + , 'pass' => null + ) + , 'version' => '1.1' + , 'body' => '' + , 'line' => "GET /?foo=bar HTTP/1.1\r\n" + , 'header' => "Host: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\n" + , 'raw' => "" + , 'cookies' => array(), + ) + ) + ) + , 1 => array( + 'request' => array( + 'uri' => array( + 'host' => 'www.cakephp.org' + , 'query' => '?foo=bar' + ) + ) + ) + , 2 => array( + 'request' => 'www.cakephp.org/?foo=bar' + ) + , 3 => array( + 'request' => array('host' => '192.168.0.1', 'uri' => 'http://www.cakephp.org/?foo=bar') + , 'expectation' => array( + 'request' => array( + 'uri' => array('host' => 'www.cakephp.org') + ) + , 'config' => array( + 'request' => array( + 'uri' => array('host' => 'www.cakephp.org') + ) + , 'host' => '192.168.0.1' + ) + ) + ) + , 'reset4' => array( + 'request.uri.query' => array() + ) + , 4 => array( + 'request' => array('header' => array('Foo@woo' => 'bar-value')) + , 'expectation' => array( + 'request' => array( + 'header' => "Host: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\nFoo\"@\"woo: bar-value\r\n" + , 'line' => "GET / HTTP/1.1\r\n" + ) + ) + ) + , 5 => array( + 'request' => array('header' => array('Foo@woo' => 'bar-value', 'host' => 'foo.com'), 'uri' => 'http://www.cakephp.org/') + , 'expectation' => array( + 'request' => array( + 'header' => "Host: foo.com\r\nConnection: close\r\nUser-Agent: CakePHP\r\nFoo\"@\"woo: bar-value\r\n" + ) + , 'config' => array( + 'host' => 'www.cakephp.org' + ) + ) + ) + , 6 => array( + 'request' => array('header' => "Foo: bar\r\n") + , 'expectation' => array( + 'request' => array( + 'header' => "Foo: bar\r\n" + ) + ) + ) + , 7 => array( + 'request' => array('header' => "Foo: bar\r\n", 'uri' => 'http://www.cakephp.org/search?q=http_socket#ignore-me') + , 'expectation' => array( + 'request' => array( + 'uri' => array( + 'path' => '/search' + , 'query' => array('q' => 'http_socket') + , 'fragment' => 'ignore-me' + ) + , 'line' => "GET /search?q=http_socket HTTP/1.1\r\n" + ) + ) + ) + , 'reset8' => array( + 'request.uri.query' => array() + ) + , 8 => array( + 'request' => array('method' => 'POST', 'uri' => 'http://www.cakephp.org/posts/add', 'body' => array('name' => 'HttpSocket-is-released', 'date' => 'today')) + , 'expectation' => array( + 'request' => array( + 'method' => 'POST' + , 'uri' => array( + 'path' => '/posts/add' + , 'fragment' => null + ) + , 'body' => "name=HttpSocket-is-released&date=today" + , 'line' => "POST /posts/add HTTP/1.1\r\n" + , 'header' => "Host: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 38\r\n" + , 'raw' => "name=HttpSocket-is-released&date=today" + ) + ) + ) + , 9 => array( + 'request' => array('method' => 'POST', 'uri' => 'https://www.cakephp.org/posts/add', 'body' => array('name' => 'HttpSocket-is-released', 'date' => 'today')) + , 'expectation' => array( + 'config' => array( + 'port' => 443 + , 'request' => array( + 'uri' => array( + 'scheme' => 'https' + , 'port' => 443 + ) + ) + ) + , 'request' => array( + 'uri' => array( + 'scheme' => 'https' + , 'port' => 443 + ) + ) + ) + ) + , 10 => array( + 'request' => array( + 'method' => 'POST', + 'uri' => 'https://www.cakephp.org/posts/add', + 'body' => array('name' => 'HttpSocket-is-released', 'date' => 'today'), + 'cookies' => array('foo' => array('value' => 'bar')) + ) + , 'expectation' => array( + 'request' => array( + 'header' => "Host: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 38\r\nCookie: foo=bar\r\n", + 'cookies' => array( + 'foo' => array('value' => 'bar'), + ) + ) + ) + ) + ); + + $expectation = array(); + foreach ($tests as $i => $test) { + if (strpos($i, 'reset') === 0) { + foreach ($test as $path => $val) { + $expectation = Set::insert($expectation, $path, $val); + } + continue; + } + + if (isset($test['expectation'])) { + $expectation = Set::merge($expectation, $test['expectation']); + } + $this->Socket->request($test['request']); + + $raw = $expectation['request']['raw']; + $expectation['request']['raw'] = $expectation['request']['line'].$expectation['request']['header']."\r\n".$raw; + + $r = array('config' => $this->Socket->config, 'request' => $this->Socket->request); + $v = $this->assertIdentical($r, $expectation, '%s in test #'.$i.' '); + if (!$v) { + debug('Result:'); + debug($r); + debug('Expected:'); + debug($expectation); + } + $expectation['request']['raw'] = $raw; + } + + $this->Socket->reset(); + $request = array('method' => 'POST', 'uri' => 'http://www.cakephp.org/posts/add', 'body' => array('name' => 'HttpSocket-is-released', 'date' => 'today')); + $response = $this->Socket->request($request); + $this->assertIdentical($this->Socket->request['body'], "name=HttpSocket-is-released&date=today"); + + $request = array('uri' => '*', 'method' => 'GET'); + $this->expectError(new PatternExpectation('/activate quirks mode/i')); + $response = $this->Socket->request($request); + $this->assertFalse($response); + $this->assertFalse($this->Socket->response); + + $this->Socket->reset(); + $request = array('uri' => 'htpp://www.cakephp.org/'); + $this->Socket->setReturnValue('connect', true); + $this->Socket->setReturnValue('read', false); + $this->Socket->_mock->_call_counts['read'] = 0; + $number = mt_rand(0, 9999999); + $serverResponse = "HTTP/1.x 200 OK\r\nDate: Mon, 16 Apr 2007 04:14:16 GMT\r\nServer: CakeHttp Server\r\nContent-Type: text/html\r\n\r\n<h1>Hello, your lucky number is " . $number . "</h1>"; + $this->Socket->setReturnValueAt(0, 'read', $serverResponse); + $this->Socket->expect('write', array("GET / HTTP/1.1\r\nHost: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\n\r\n")); + $this->Socket->expectCallCount('read', 2); + $response = $this->Socket->request($request); + $this->assertIdentical($response, "<h1>Hello, your lucky number is " . $number . "</h1>"); + + $this->Socket->reset(); + $serverResponse = "HTTP/1.x 200 OK\r\nSet-Cookie: foo=bar\r\nDate: Mon, 16 Apr 2007 04:14:16 GMT\r\nServer: CakeHttp Server\r\nContent-Type: text/html\r\n\r\n<h1>This is a cookie test!</h1>"; + unset($this->Socket->_mock->_actions->_at['read']); + unset($this->Socket->_mock->_return_sequence['read']); + $this->Socket->_mock->_call_counts['read'] = 0; + $this->Socket->setReturnValueAt(0, 'read', $serverResponse); + + $this->Socket->connected = true; + $this->Socket->request($request); + $result = $this->Socket->response['cookies']; + $expect = array( + 'foo' => array( + 'value' => 'bar' + ) + ); + $this->assertEqual($result, $expect); + $this->assertEqual($this->Socket->config['request']['cookies'], $expect); + $this->assertFalse($this->Socket->connected); + } +/** + * testUrl method + * + * @access public + * @return void + */ + function testUrl() { + $this->Socket->reset(true); + + $this->assertIdentical($this->Socket->url(true), false); + + $url = $this->Socket->url('www.cakephp.org'); + $this->assertIdentical($url, 'http://www.cakephp.org/'); + + $url = $this->Socket->url('https://www.cakephp.org/posts/add'); + $this->assertIdentical($url, 'https://www.cakephp.org/posts/add'); + $url = $this->Socket->url('http://www.cakephp/search?q=socket', '/%path?%query'); + $this->assertIdentical($url, '/search?q=socket'); + + $this->Socket->config['request']['uri']['host'] = 'bakery.cakephp.org'; + $url = $this->Socket->url(); + $this->assertIdentical($url, 'http://bakery.cakephp.org/'); + + $this->Socket->configUri('http://www.cakephp.org'); + $url = $this->Socket->url('/search?q=bar'); + $this->assertIdentical($url, 'http://www.cakephp.org/search?q=bar'); + + $url = $this->Socket->url(array('host' => 'www.foobar.org', 'query' => array('q' => 'bar'))); + $this->assertIdentical($url, 'http://www.foobar.org/?q=bar'); + + $url = $this->Socket->url(array('path' => '/supersearch', 'query' => array('q' => 'bar'))); + $this->assertIdentical($url, 'http://www.cakephp.org/supersearch?q=bar'); + + $this->Socket->configUri('http://www.google.com'); + $url = $this->Socket->url('/search?q=socket'); + $this->assertIdentical($url, 'http://www.google.com/search?q=socket'); + + $url = $this->Socket->url(); + $this->assertIdentical($url, 'http://www.google.com/'); + + $this->Socket->configUri('https://www.google.com'); + $url = $this->Socket->url('/search?q=socket'); + $this->assertIdentical($url, 'https://www.google.com/search?q=socket'); + + $this->Socket->reset(); + $this->Socket->configUri('www.google.com:443'); + $url = $this->Socket->url('/search?q=socket'); + $this->assertIdentical($url, 'https://www.google.com/search?q=socket'); + + $this->Socket->reset(); + $this->Socket->configUri('www.google.com:8080'); + $url = $this->Socket->url('/search?q=socket'); + $this->assertIdentical($url, 'http://www.google.com:8080/search?q=socket'); + } +/** + * testGet method + * + * @access public + * @return void + */ + function testGet() { + $this->RequestSocket->reset(); + + $this->RequestSocket->expect('request', a(array('method' => 'GET', 'uri' => 'http://www.google.com/'))); + $this->RequestSocket->get('http://www.google.com/'); + + $this->RequestSocket->expect('request', a(array('method' => 'GET', 'uri' => 'http://www.google.com/?foo=bar'))); + $this->RequestSocket->get('http://www.google.com/', array('foo' => 'bar')); + + $this->RequestSocket->expect('request', a(array('method' => 'GET', 'uri' => 'http://www.google.com/?foo=bar'))); + $this->RequestSocket->get('http://www.google.com/', 'foo=bar'); + + $this->RequestSocket->expect('request', a(array('method' => 'GET', 'uri' => 'http://www.google.com/?foo=23&foobar=42'))); + $this->RequestSocket->get('http://www.google.com/?foo=bar', array('foobar' => '42', 'foo' => '23')); + + $this->RequestSocket->expect('request', a(array('method' => 'GET', 'uri' => 'http://www.google.com/', 'auth' => array('user' => 'foo', 'pass' => 'bar')))); + $this->RequestSocket->get('http://www.google.com/', null, array('auth' => array('user' => 'foo', 'pass' => 'bar'))); + } +/** + * testPostPutDelete method + * + * @access public + * @return void + */ + function testPostPutDelete() { + $this->RequestSocket->reset(); + + foreach (array('POST', 'PUT', 'DELETE') as $method) { + $this->RequestSocket->expect('request', a(array('method' => $method, 'uri' => 'http://www.google.com/', 'body' => array()))); + $this->RequestSocket->{low($method)}('http://www.google.com/'); + + $this->RequestSocket->expect('request', a(array('method' => $method, 'uri' => 'http://www.google.com/', 'body' => array('Foo' => 'bar')))); + $this->RequestSocket->{low($method)}('http://www.google.com/', array('Foo' => 'bar')); + + $this->RequestSocket->expect('request', a(array('method' => $method, 'uri' => 'http://www.google.com/', 'body' => null, 'line' => 'Hey Server'))); + $this->RequestSocket->{low($method)}('http://www.google.com/', null, array('line' => 'Hey Server')); + } + } +/** + * testParseResponse method + * + * @access public + * @return void + */ + function testParseResponse() { + $this->Socket->reset(); + + $r = $this->Socket->parseResponse(array('foo' => 'bar')); + $this->assertIdentical($r, array('foo' => 'bar')); + + $r = $this->Socket->parseResponse(true); + $this->assertIdentical($r, false); + + $r = $this->Socket->parseResponse("HTTP Foo\r\nBar: La"); + $this->assertIdentical($r, false); + + $tests = array( + 'simple-request' => array( + 'response' => array( + 'status-line' => "HTTP/1.x 200 OK\r\n", + 'header' => "Date: Mon, 16 Apr 2007 04:14:16 GMT\r\nServer: CakeHttp Server\r\n", + 'body' => "<h1>Hello World</h1>\r\n<p>It's good to be html</p>" + ) + , 'expectations' => array( + 'status.http-version' => 'HTTP/1.x', + 'status.code' => 200, + 'status.reason-phrase' => 'OK', + 'header' => $this->Socket->parseHeader("Date: Mon, 16 Apr 2007 04:14:16 GMT\r\nServer: CakeHttp Server\r\n"), + 'body' => "<h1>Hello World</h1>\r\n<p>It's good to be html</p>" + ) + ), + 'no-header' => array( + 'response' => array( + 'status-line' => "HTTP/1.x 404 OK\r\n", + 'header' => null, + ) + , 'expectations' => array( + 'status.code' => 404, + 'header' => array() + ) + ), + 'chunked' => array( + 'response' => array( + 'header' => "Transfer-Encoding: chunked\r\n", + 'body' => "19\r\nThis is a chunked message\r\n0\r\n" + ), + 'expectations' => array( + 'body' => "This is a chunked message", + 'header' => $this->Socket->parseHeader("Transfer-Encoding: chunked\r\n") + ) + ), + 'enitity-header' => array( + 'response' => array( + 'body' => "19\r\nThis is a chunked message\r\n0\r\nFoo: Bar\r\n" + ), + 'expectations' => array( + 'header' => $this->Socket->parseHeader("Transfer-Encoding: chunked\r\nFoo: Bar\r\n") + ) + ), + 'enitity-header-combine' => array( + 'response' => array( + 'header' => "Transfer-Encoding: chunked\r\nFoo: Foobar\r\n" + ), + 'expectations' => array( + 'header' => $this->Socket->parseHeader("Transfer-Encoding: chunked\r\nFoo: Foobar\r\nFoo: Bar\r\n") + ) + ) + ); + + $testResponse = array(); + $expectations = array(); + + foreach ($tests as $name => $test) { + + $testResponse = array_merge($testResponse, $test['response']); + $testResponse['response'] = $testResponse['status-line'].$testResponse['header']."\r\n".$testResponse['body']; + $r = $this->Socket->parseResponse($testResponse['response']); + $expectations = array_merge($expectations, $test['expectations']); + + foreach ($expectations as $property => $expectedVal) { + $val = Set::extract($r, $property); + $this->assertIdentical($val, $expectedVal, 'Test "'.$name.'": response.'.$property.' - %s'); + } + + foreach (array('status-line', 'header', 'body', 'response') as $field) { + $this->assertIdentical($r['raw'][$field], $testResponse[$field], 'Test response.raw.'.$field.': %s'); + } + } + } +/** + * testDecodeBody method + * + * @access public + * @return void + */ + function testDecodeBody() { + $this->Socket->reset(); + + $r = $this->Socket->decodeBody(true); + $this->assertIdentical($r, false); + + $r = $this->Socket->decodeBody('Foobar', false); + $this->assertIdentical($r, array('body' => 'Foobar', 'header' => false)); + + $encodings = array( + 'chunked' => array( + 'encoded' => "19\r\nThis is a chunked message\r\n0\r\n", + 'decoded' => array('body' => "This is a chunked message", 'header' => false) + ), + 'foo-coded' => array( + 'encoded' => '!Foobar!', + 'decoded' => array('body' => '!Foobar!', 'header' => false), + 'error' => new PatternExpectation('/unknown encoding: foo-coded/i') + ) + ); + + foreach ($encodings as $encoding => $sample) { + if (isset($sample['error'])) { + $this->expectError($sample['error']); + } + + $r = $this->Socket->decodeBody($sample['encoded'], $encoding); + $this->assertIdentical($r, $sample['decoded']); + + if (isset($sample['error'])) { + $this->Socket->quirksMode = true; + $r = $this->Socket->decodeBody($sample['encoded'], $encoding); + $this->assertIdentical($r, $sample['decoded']); + $this->Socket->quirksMode = false; + } + } + } +/** + * testDecodeChunkedBody method + * + * @access public + * @return void + */ + function testDecodeChunkedBody() { + $this->Socket->reset(); + + $r = $this->Socket->decodeChunkedBody(true); + $this->assertIdentical($r, false); + + $encoded = "19\r\nThis is a chunked message\r\n0\r\n"; + $decoded = "This is a chunked message"; + $r = $this->Socket->decodeChunkedBody($encoded); + $this->assertIdentical($r['body'], $decoded); + $this->assertIdentical($r['header'], false); + + $encoded = "19 \r\nThis is a chunked message\r\n0\r\n"; + $r = $this->Socket->decodeChunkedBody($encoded); + $this->assertIdentical($r['body'], $decoded); + + $encoded = "19\r\nThis is a chunked message\r\nE\r\n\nThat is cool\n\r\n0\r\n"; + $decoded = "This is a chunked message\nThat is cool\n"; + $r = $this->Socket->decodeChunkedBody($encoded); + $this->assertIdentical($r['body'], $decoded); + $this->assertIdentical($r['header'], false); + + $encoded = "19\r\nThis is a chunked message\r\nE;foo-chunk=5\r\n\nThat is cool\n\r\n0\r\n"; + $r = $this->Socket->decodeChunkedBody($encoded); + $this->assertIdentical($r['body'], $decoded); + $this->assertIdentical($r['header'], false); + + $encoded = "19\r\nThis is a chunked message\r\nE\r\n\nThat is cool\n\r\n0\r\nfoo-header: bar\r\ncake: PHP\r\n\r\n"; + $r = $this->Socket->decodeChunkedBody($encoded); + $this->assertIdentical($r['body'], $decoded); + $this->assertIdentical($r['header'], array('Foo-Header' => 'bar', 'Cake' => 'PHP')); + + $encoded = "19\r\nThis is a chunked message\r\nE\r\n\nThat is cool\n\r\n"; + $this->expectError(new PatternExpectation('/activate quirks mode/i')); + $r = $this->Socket->decodeChunkedBody($encoded); + $this->assertIdentical($r, false); + + $this->Socket->quirksMode = true; + $r = $this->Socket->decodeChunkedBody($encoded); + $this->assertIdentical($r['body'], $decoded); + $this->assertIdentical($r['header'], false); + + $encoded = "19\r\nThis is a chunked message\r\nE\r\n\nThat is cool\n\r\nfoo-header: bar\r\ncake: PHP\r\n\r\n"; + $r = $this->Socket->decodeChunkedBody($encoded); + $this->assertIdentical($r['body'], $decoded); + $this->assertIdentical($r['header'], array('Foo-Header' => 'bar', 'Cake' => 'PHP')); + } +/** + * testBuildRequestLine method + * + * @access public + * @return void + */ + function testBuildRequestLine() { + $this->Socket->reset(); + + $this->expectError(new PatternExpectation('/activate quirks mode/i')); + $r = $this->Socket->buildRequestLine('Foo'); + $this->assertIdentical($r, false); + + $this->Socket->quirksMode = true; + $r = $this->Socket->buildRequestLine('Foo'); + $this->assertIdentical($r, 'Foo'); + $this->Socket->quirksMode = false; + + $r = $this->Socket->buildRequestLine(true); + $this->assertIdentical($r, false); + + $r = $this->Socket->buildRequestLine(array('foo' => 'bar', 'method' => 'foo')); + $this->assertIdentical($r, false); + + $r = $this->Socket->buildRequestLine(array('method' => 'GET', 'uri' => 'http://www.cakephp.org/search?q=socket')); + $this->assertIdentical($r, "GET /search?q=socket HTTP/1.1\r\n"); + + $request = array( + 'method' => 'GET', + 'uri' => array( + 'path' => '/search', + 'query' => array('q' => 'socket') + ) + ); + $r = $this->Socket->buildRequestLine($request); + $this->assertIdentical($r, "GET /search?q=socket HTTP/1.1\r\n"); + + unset($request['method']); + $r = $this->Socket->buildRequestLine($request); + $this->assertIdentical($r, "GET /search?q=socket HTTP/1.1\r\n"); + + $r = $this->Socket->buildRequestLine($request, 'CAKE-HTTP/0.1'); + $this->assertIdentical($r, "GET /search?q=socket CAKE-HTTP/0.1\r\n"); + + $request = array('method' => 'OPTIONS', 'uri' => '*'); + $r = $this->Socket->buildRequestLine($request); + $this->assertIdentical($r, "OPTIONS * HTTP/1.1\r\n"); + + $request['method'] = 'GET'; + $this->expectError(new PatternExpectation('/activate quirks mode/i')); + $r = $this->Socket->buildRequestLine($request); + $this->assertIdentical($r, false); + + $this->expectError(new PatternExpectation('/activate quirks mode/i')); + $r = $this->Socket->buildRequestLine("GET * HTTP/1.1\r\n"); + $this->assertIdentical($r, false); + + $this->Socket->quirksMode = true; + $r = $this->Socket->buildRequestLine($request); + $this->assertIdentical($r, "GET * HTTP/1.1\r\n"); + + $r = $this->Socket->buildRequestLine("GET * HTTP/1.1\r\n"); + $this->assertIdentical($r, "GET * HTTP/1.1\r\n"); + } +/** + * Asserts that HttpSocket::parseUri is working properly + * + * @access public + * @return void + */ + function testParseUri() { + $this->Socket->reset(); + + $uri = $this->Socket->parseUri(array('invalid' => 'uri-string')); + $this->assertIdentical($uri, false); + + $uri = $this->Socket->parseUri(array('invalid' => 'uri-string'), array('host' => 'somehost')); + $this->assertIdentical($uri, array('host' => 'somehost', 'invalid' => 'uri-string')); + + $uri = $this->Socket->parseUri(false); + $this->assertIdentical($uri, false); + + $uri = $this->Socket->parseUri('/my-cool-path'); + $this->assertIdentical($uri, array('path' => '/my-cool-path')); + + $uri = $this->Socket->parseUri('http://bob:foo123@www.cakephp.org:40/search?q=dessert#results'); + $this->assertIdentical($uri, array( + 'scheme' => 'http', + 'host' => 'www.cakephp.org', + 'port' => 40, + 'user' => 'bob', + 'pass' => 'foo123', + 'path' => '/search', + 'query' => array('q' => 'dessert'), + 'fragment' => 'results' + )); + + $uri = $this->Socket->parseUri('http://www.cakephp.org/'); + $this->assertIdentical($uri, array( + 'scheme' => 'http', + 'host' => 'www.cakephp.org', + 'path' => '/', + )); + + $uri = $this->Socket->parseUri('http://www.cakephp.org', true); + $this->assertIdentical($uri, array( + 'scheme' => 'http', + 'host' => 'www.cakephp.org', + 'port' => 80, + 'user' => null, + 'pass' => null, + 'path' => '/', + 'query' => array(), + 'fragment' => null + )); + + $uri = $this->Socket->parseUri('https://www.cakephp.org', true); + $this->assertIdentical($uri, array( + 'scheme' => 'https', + 'host' => 'www.cakephp.org', + 'port' => 443, + 'user' => null, + 'pass' => null, + 'path' => '/', + 'query' => array(), + 'fragment' => null + )); + + $uri = $this->Socket->parseUri('www.cakephp.org:443/query?foo', true); + $this->assertIdentical($uri, array( + 'scheme' => 'https', + 'host' => 'www.cakephp.org', + 'port' => 443, + 'user' => null, + 'pass' => null, + 'path' => '/query', + 'query' => array('foo' => ""), + 'fragment' => null + )); + + $uri = $this->Socket->parseUri('http://www.cakephp.org', array('host' => 'piephp.org', 'user' => 'bob', 'fragment' => 'results')); + $this->assertIdentical($uri, array( + 'host' => 'www.cakephp.org', + 'user' => 'bob', + 'fragment' => 'results', + 'scheme' => 'http' + )); + + $uri = $this->Socket->parseUri('https://www.cakephp.org', array('scheme' => 'http', 'port' => 23)); + $this->assertIdentical($uri, array( + 'scheme' => 'https', + 'port' => 23, + 'host' => 'www.cakephp.org' + )); + + $uri = $this->Socket->parseUri('www.cakephp.org:59', array('scheme' => array('http', 'https'), 'port' => 80)); + $this->assertIdentical($uri, array( + 'scheme' => 'http', + 'port' => 59, + 'host' => 'www.cakephp.org' + )); + + $uri = $this->Socket->parseUri(array('scheme' => 'http', 'host' => 'www.google.com', 'port' => 8080), array('scheme' => array('http', 'https'), 'host' => 'www.google.com', 'port' => array(80, 443))); + $this->assertIdentical($uri, array( + 'scheme' => 'http', + 'host' => 'www.google.com', + 'port' => 8080, + )); + } +/** + * Tests that HttpSocket::buildUri can turn all kinds of uri arrays (and strings) into fully or partially qualified URI's + * + * @access public + * @return void + */ + function testBuildUri() { + $this->Socket->reset(); + + $r = $this->Socket->buildUri(true); + $this->assertIdentical($r, false); + + $r = $this->Socket->buildUri('foo.com'); + $this->assertIdentical($r, 'http://foo.com/'); + + $r = $this->Socket->buildUri(array('host' => 'www.cakephp.org')); + $this->assertIdentical($r, 'http://www.cakephp.org/'); + + $r = $this->Socket->buildUri(array('host' => 'www.cakephp.org', 'scheme' => 'https')); + $this->assertIdentical($r, 'https://www.cakephp.org/'); + + $r = $this->Socket->buildUri(array('host' => 'www.cakephp.org', 'port' => 23)); + $this->assertIdentical($r, 'http://www.cakephp.org:23/'); + + $r = $this->Socket->buildUri(array('host' => 'www.cakephp.org', 'scheme' => 'https', 'port' => 79)); + $this->assertIdentical($r, 'https://www.cakephp.org:79/'); + + $r = $this->Socket->buildUri(array('host' => 'www.cakephp.org', 'path' => 'foo')); + $this->assertIdentical($r, 'http://www.cakephp.org/foo'); + + $r = $this->Socket->buildUri(array('host' => 'www.cakephp.org', 'path' => '/foo')); + $this->assertIdentical($r, 'http://www.cakephp.org/foo'); + + $r = $this->Socket->buildUri(array('host' => 'www.cakephp.org', 'path' => '/search', 'query' => array('q' => 'HttpSocket'))); + $this->assertIdentical($r, 'http://www.cakephp.org/search?q=HttpSocket'); + + $r = $this->Socket->buildUri(array('host' => 'www.cakephp.org', 'fragment' => 'bar')); + $this->assertIdentical($r, 'http://www.cakephp.org/#bar'); + + $r = $this->Socket->buildUri(array( + 'scheme' => 'https', + 'host' => 'www.cakephp.org', + 'port' => 25, + 'user' => 'bob', + 'pass' => 'secret', + 'path' => '/cool', + 'query' => array('foo' => 'bar'), + 'fragment' => 'comment' + )); + $this->assertIdentical($r, 'https://bob:secret@www.cakephp.org:25/cool?foo=bar#comment'); + + $r = $this->Socket->buildUri(array('host' => 'www.cakephp.org', 'fragment' => 'bar'), '%fragment?%host'); + $this->assertIdentical($r, 'bar?www.cakephp.org'); + + $r = $this->Socket->buildUri(array('host' => 'www.cakephp.org'), '%fragment???%host'); + $this->assertIdentical($r, '???www.cakephp.org'); + + $r = $this->Socket->buildUri(array('path' => '*'), '/%path?%query'); + $this->assertIdentical($r, '*'); + + $r = $this->Socket->buildUri(array('scheme' => 'foo', 'host' => 'www.cakephp.org')); + $this->assertIdentical($r, 'foo://www.cakephp.org:80/'); + } +/** + * Asserts that HttpSocket::parseQuery is working properly + * + * @access public + * @return void + */ + function testParseQuery() { + $this->Socket->reset(); + + $query = $this->Socket->parseQuery(array('framework' => 'cakephp')); + $this->assertIdentical($query, array('framework' => 'cakephp')); + + $query = $this->Socket->parseQuery(''); + $this->assertIdentical($query, array()); + + $query = $this->Socket->parseQuery('framework=cakephp'); + $this->assertIdentical($query, array('framework' => 'cakephp')); + + $query = $this->Socket->parseQuery('?framework=cakephp'); + $this->assertIdentical($query, array('framework' => 'cakephp')); + + $query = $this->Socket->parseQuery('a&b&c'); + $this->assertIdentical($query, array('a' => '', 'b' => '', 'c' => '')); + + $query = $this->Socket->parseQuery('value=12345'); + $this->assertIdentical($query, array('value' => '12345')); + + $query = $this->Socket->parseQuery('a[0]=foo&a[1]=bar&a[2]=cake'); + $this->assertIdentical($query, array('a' => array(0 => 'foo', 1 => 'bar', 2 => 'cake'))); + + $query = $this->Socket->parseQuery('a[]=foo&a[]=bar&a[]=cake'); + $this->assertIdentical($query, array('a' => array(0 => 'foo', 1 => 'bar', 2 => 'cake'))); + + $query = $this->Socket->parseQuery('a]][[=foo&[]=bar&]]][]=cake'); + $this->assertIdentical($query, array('a]][[' => 'foo', 0 => 'bar', ']]]' => array('cake'))); + + $query = $this->Socket->parseQuery('a[][]=foo&a[][]=bar&a[][]=cake'); + $expectedQuery = array( + 'a' => array( + 0 => array( + 0 => 'foo' + ), + 1 => array( + 0 => 'bar' + ), + array( + 0 => 'cake' + ) + ) + ); + $this->assertIdentical($query, $expectedQuery); + + $query = $this->Socket->parseQuery('a[][]=foo&a[bar]=php&a[][]=bar&a[][]=cake'); + $expectedQuery = array( + 'a' => array( + 0 => array( + 0 => 'foo' + ), + 'bar' => 'php', + 1 => array( + 0 => 'bar' + ), + array( + 0 => 'cake' + ) + ) + ); + $this->assertIdentical($query, $expectedQuery); + + $query = $this->Socket->parseQuery('user[]=jim&user[3]=tom&user[]=bob'); + $expectedQuery = array( + 'user' => array( + 0 => 'jim', + 3 => 'tom', + 4 => 'bob' + ) + ); + $this->assertIdentical($query, $expectedQuery); + + $queryStr = 'user[0]=foo&user[0][items][]=foo&user[0][items][]=bar&user[][name]=jim&user[1][items][personal][]=book&user[1][items][personal][]=pen&user[1][items][]=ball&user[count]=2&empty'; + $query = $this->Socket->parseQuery($queryStr); + $expectedQuery = array( + 'user' => array( + 0 => array( + 'items' => array( + 'foo', + 'bar' + ) + ), + 1 => array( + 'name' => 'jim', + 'items' => array( + 'personal' => array( + 'book' + , 'pen' + ), + 'ball' + ) + ), + 'count' => '2' + ), + 'empty' => '' + ); + $this->assertIdentical($query, $expectedQuery); + } +/** + * Tests that HttpSocket::buildHeader can turn a given $header array into a proper header string according to + * HTTP 1.1 specs. + * + * @access public + * @return void + */ + function testBuildHeader() { + $this->Socket->reset(); + + $r = $this->Socket->buildHeader(true); + $this->assertIdentical($r, false); + + $r = $this->Socket->buildHeader('My raw header'); + $this->assertIdentical($r, 'My raw header'); + + $r = $this->Socket->buildHeader(array('Host' => 'www.cakephp.org')); + $this->assertIdentical($r, "Host: www.cakephp.org\r\n"); + + $r = $this->Socket->buildHeader(array('Host' => 'www.cakephp.org', 'Connection' => 'Close')); + $this->assertIdentical($r, "Host: www.cakephp.org\r\nConnection: Close\r\n"); + + $r = $this->Socket->buildHeader(array('People' => array('Bob', 'Jim', 'John'))); + $this->assertIdentical($r, "People: Bob,Jim,John\r\n"); + + $r = $this->Socket->buildHeader(array('Multi-Line-Field' => "This is my\r\nMulti Line field")); + $this->assertIdentical($r, "Multi-Line-Field: This is my\r\n Multi Line field\r\n"); + + $r = $this->Socket->buildHeader(array('Multi-Line-Field' => "This is my\r\n Multi Line field")); + $this->assertIdentical($r, "Multi-Line-Field: This is my\r\n Multi Line field\r\n"); + + $r = $this->Socket->buildHeader(array('Multi-Line-Field' => "This is my\r\n\tMulti Line field")); + $this->assertIdentical($r, "Multi-Line-Field: This is my\r\n\tMulti Line field\r\n"); + + $r = $this->Socket->buildHeader(array('Test@Field' => "My value")); + $this->assertIdentical($r, "Test\"@\"Field: My value\r\n"); + + } +/** + * Test that HttpSocket::parseHeader can take apart a given (and valid) $header string and turn it into an array. + * + * @access public + * @return void + */ + function testParseHeader() { + $this->Socket->reset(); + + $r = $this->Socket->parseHeader(array('foo' => 'Bar', 'fOO-bAr' => 'quux')); + $this->assertIdentical($r, array('Foo' => 'Bar', 'Foo-Bar' => 'quux')); + + $r = $this->Socket->parseHeader(true); + $this->assertIdentical($r, false); + + $header = "Host: cakephp.org\t\r\n"; + $r = $this->Socket->parseHeader($header); + $expected = array( + 'Host' => 'cakephp.org' + ); + $this->assertIdentical($r, $expected); + + $header = "Date:Sat, 07 Apr 2007 10:10:25 GMT\r\nX-Powered-By: PHP/5.1.2\r\n"; + $r = $this->Socket->parseHeader($header); + $expected = array( + 'Date' => 'Sat, 07 Apr 2007 10:10:25 GMT' + , 'X-Powered-By' => 'PHP/5.1.2' + ); + $this->assertIdentical($r, $expected); + + $header = "people: Jim,John\r\nfoo-LAND: Bar\r\ncAKe-PHP: rocks\r\n"; + $r = $this->Socket->parseHeader($header); + $expected = array( + 'People' => 'Jim,John' + , 'Foo-Land' => 'Bar' + , 'Cake-Php' => 'rocks' + ); + $this->assertIdentical($r, $expected); + + $header = "People: Jim,John,Tim\r\nPeople: Lisa,Tina,Chelsea\r\n"; + $r = $this->Socket->parseHeader($header); + $expected = array( + 'People' => array('Jim,John,Tim', 'Lisa,Tina,Chelsea') + ); + $this->assertIdentical($r, $expected); + + $header = "Multi-Line: I am a \r\nmulti line\t\r\nfield value.\r\nSingle-Line: I am not\r\n"; + $r = $this->Socket->parseHeader($header); + $expected = array( + 'Multi-Line' => "I am a\r\nmulti line\r\nfield value." + , 'Single-Line' => 'I am not' + ); + $this->assertIdentical($r, $expected); + + $header = "Esc\"@\"ped: value\r\n"; + $r = $this->Socket->parseHeader($header); + $expected = array( + 'Esc@ped' => 'value' + ); + $this->assertIdentical($r, $expected); + } +/** + * testParseCookies method + * + * @access public + * @return void + */ + function testParseCookies() { + $header = array( + 'Set-Cookie' => array( + 'foo=bar', + 'people=jim,jack,johnny";";Path=/accounts', + 'google=not=nice' + ), + 'Transfer-Encoding' => 'chunked', + 'Date' => 'Sun, 18 Nov 2007 18:57:42 GMT', + ); + $cookies = $this->Socket->parseCookies($header); + $expected = array( + 'foo' => array( + 'value' => 'bar' + ), + 'people' => array( + 'value' => 'jim,jack,johnny";"', + 'path' => '/accounts', + ), + 'google' => array( + 'value' => 'not=nice', + ) + ); + $this->assertEqual($cookies, $expected); + + $header['Set-Cookie'][] = 'cakephp=great; Secure'; + $expected['cakephp'] = array('value' => 'great', 'secure' => true); + $cookies = $this->Socket->parseCookies($header); + $this->assertEqual($cookies, $expected); + + $header['Set-Cookie'] = 'foo=bar'; + unset($expected['people'], $expected['cakephp'], $expected['google']); + $cookies = $this->Socket->parseCookies($header); + $this->assertEqual($cookies, $expected); + } +/** + * testBuildCookies method + * + * @return void + * @access public + * @todo Test more scenarios + */ + function testBuildCookies() { + $cookies = array( + 'foo' => array( + 'value' => 'bar' + ), + 'people' => array( + 'value' => 'jim,jack,johnny;', + 'path' => '/accounts' + ) + ); + $expect = "Cookie: foo=bar\r\nCookie: people=jim,jack,johnny\";\"\r\n"; + $result = $this->Socket->buildCookies($cookies); + $this->assertEqual($result, $expect); + } +/** + * Tests that HttpSocket::__tokenEscapeChars() returns the right characters. + * + * @access public + * @return void + */ + function testTokenEscapeChars() { + $this->Socket->reset(); + + $expected = array( + '\x22','\x28','\x29','\x3c','\x3e','\x40','\x2c','\x3b','\x3a','\x5c','\x2f','\x5b','\x5d','\x3f','\x3d','\x7b', + '\x7d','\x20','\x00','\x01','\x02','\x03','\x04','\x05','\x06','\x07','\x08','\x09','\x0a','\x0b','\x0c','\x0d', + '\x0e','\x0f','\x10','\x11','\x12','\x13','\x14','\x15','\x16','\x17','\x18','\x19','\x1a','\x1b','\x1c','\x1d', + '\x1e','\x1f','\x7f' + ); + $r = $this->Socket->__tokenEscapeChars(); + $this->assertEqual($r, $expected); + + foreach ($expected as $key => $char) { + $expected[$key] = chr(hexdec(substr($char, 2))); + } + + $r = $this->Socket->__tokenEscapeChars(false); + $this->assertEqual($r, $expected); + } +/** + * Test that HttpSocket::escapeToken is escaping all characters as descriped in RFC 2616 (HTTP 1.1 specs) + * + * @access public + * @return void + */ + function testEscapeToken() { + $this->Socket->reset(); + + $this->assertIdentical($this->Socket->escapeToken('Foo'), 'Foo'); + + $escape = $this->Socket->__tokenEscapeChars(false); + foreach ($escape as $char) { + $token = 'My-special-'.$char.'-Token'; + $escapedToken = $this->Socket->escapeToken($token); + $expectedToken = 'My-special-"'.$char.'"-Token'; + + $this->assertIdentical($escapedToken, $expectedToken, 'Test token escaping for ASCII '.ord($char)); + } + + $token = 'Extreme-:Token- -"@-test'; + $escapedToken = $this->Socket->escapeToken($token); + $expectedToken = 'Extreme-":"Token-" "-""""@"-test'; + $this->assertIdentical($expectedToken, $escapedToken); + } +/** + * Test that escaped token strings are properly unescaped by HttpSocket::unescapeToken + * + * @access public + * @return void + */ + function testUnescapeToken() { + $this->Socket->reset(); + + $this->assertIdentical($this->Socket->unescapeToken('Foo'), 'Foo'); + + $escape = $this->Socket->__tokenEscapeChars(false); + foreach ($escape as $char) { + $token = 'My-special-"'.$char.'"-Token'; + $unescapedToken = $this->Socket->unescapeToken($token); + $expectedToken = 'My-special-'.$char.'-Token'; + + $this->assertIdentical($unescapedToken, $expectedToken, 'Test token unescaping for ASCII '.ord($char)); + } + + $token = 'Extreme-":"Token-" "-""""@"-test'; + $escapedToken = $this->Socket->unescapeToken($token); + $expectedToken = 'Extreme-:Token- -"@-test'; + $this->assertIdentical($expectedToken, $escapedToken); + } +/** + * This tests asserts HttpSocket::reset() resets a HttpSocket instance to it's initial state (before Object::__construct + * got executed) + * + * @access public + * @return void + */ + function testReset() { + $this->Socket->reset(); + + $initialState = get_class_vars('HttpSocket'); + foreach ($initialState as $property => $value) { + $this->Socket->{$property} = 'Overwritten'; + } + + $return = $this->Socket->reset(); + + foreach ($initialState as $property => $value) { + $this->assertIdentical($this->Socket->{$property}, $value); + } + + $this->assertIdentical($return, true); + } +/** + * This tests asserts HttpSocket::reset(false) resets certain HttpSocket properties to their initial state (before + * Object::__construct got executed). + * + * @access public + * @return void + */ + function testPartialReset() { + $this->Socket->reset(); + + $partialResetProperties = array('request', 'response'); + $initialState = get_class_vars('HttpSocket'); + + foreach ($initialState as $property => $value) { + $this->Socket->{$property} = 'Overwritten'; + } + + $return = $this->Socket->reset(false); + + foreach ($initialState as $property => $originalValue) { + if (in_array($property, $partialResetProperties)) { + $this->assertIdentical($this->Socket->{$property}, $originalValue); + } else { + $this->assertIdentical($this->Socket->{$property}, 'Overwritten'); + } + } + $this->assertIdentical($return, true); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/i18n.test.php b/cake/tests/cases/libs/i18n.test.php new file mode 100755 index 0000000..e570bc6 --- /dev/null +++ b/cake/tests/cases/libs/i18n.test.php @@ -0,0 +1,2657 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * I18nTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs + * @since CakePHP(tm) v 1.2.0.5432 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +App::import('Core', 'i18n'); +/** + * I18nTest class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class I18nTest extends CakeTestCase { +/** + * setUp method + * + * @access public + * @return void + */ + function setUp() { + $this->_objects = Configure::read('__objects'); + Configure::write('__objects', array()); + + $this->_localePaths = Configure::read('localePaths'); + Configure::write('localePaths', array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'locale')); + + $this->_pluginPaths = Configure::read('pluginPaths'); + Configure::write('pluginPaths', array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins')); + + } +/** + * tearDown method + * + * @access public + * @return void + */ + function tearDown() { + Configure::write('localePaths', $this->_localePaths); + Configure::write('pluginPaths', $this->_pluginPaths); + Configure::write('__objects', $this->_objects); + + } +/** + * testDefaultStrings method + * + * @access public + * @return void + */ + function testDefaultStrings() { + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 1', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 = 0 or > 1', $plurals)); + $this->assertTrue(in_array('1 = 1', $plurals)); + $this->assertTrue(in_array('2 = 0 or > 1', $plurals)); + $this->assertTrue(in_array('3 = 0 or > 1', $plurals)); + $this->assertTrue(in_array('4 = 0 or > 1', $plurals)); + $this->assertTrue(in_array('5 = 0 or > 1', $plurals)); + $this->assertTrue(in_array('6 = 0 or > 1', $plurals)); + $this->assertTrue(in_array('7 = 0 or > 1', $plurals)); + $this->assertTrue(in_array('8 = 0 or > 1', $plurals)); + $this->assertTrue(in_array('9 = 0 or > 1', $plurals)); + $this->assertTrue(in_array('10 = 0 or > 1', $plurals)); + $this->assertTrue(in_array('11 = 0 or > 1', $plurals)); + $this->assertTrue(in_array('12 = 0 or > 1', $plurals)); + $this->assertTrue(in_array('13 = 0 or > 1', $plurals)); + $this->assertTrue(in_array('14 = 0 or > 1', $plurals)); + $this->assertTrue(in_array('15 = 0 or > 1', $plurals)); + $this->assertTrue(in_array('16 = 0 or > 1', $plurals)); + $this->assertTrue(in_array('17 = 0 or > 1', $plurals)); + $this->assertTrue(in_array('18 = 0 or > 1', $plurals)); + $this->assertTrue(in_array('19 = 0 or > 1', $plurals)); + $this->assertTrue(in_array('20 = 0 or > 1', $plurals)); + $this->assertTrue(in_array('21 = 0 or > 1', $plurals)); + $this->assertTrue(in_array('22 = 0 or > 1', $plurals)); + $this->assertTrue(in_array('23 = 0 or > 1', $plurals)); + $this->assertTrue(in_array('24 = 0 or > 1', $plurals)); + $this->assertTrue(in_array('25 = 0 or > 1', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 1 (from core)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 = 0 or > 1 (from core)', $corePlurals)); + $this->assertTrue(in_array('1 = 1 (from core)', $corePlurals)); + $this->assertTrue(in_array('2 = 0 or > 1 (from core)', $corePlurals)); + $this->assertTrue(in_array('3 = 0 or > 1 (from core)', $corePlurals)); + $this->assertTrue(in_array('4 = 0 or > 1 (from core)', $corePlurals)); + $this->assertTrue(in_array('5 = 0 or > 1 (from core)', $corePlurals)); + $this->assertTrue(in_array('6 = 0 or > 1 (from core)', $corePlurals)); + $this->assertTrue(in_array('7 = 0 or > 1 (from core)', $corePlurals)); + $this->assertTrue(in_array('8 = 0 or > 1 (from core)', $corePlurals)); + $this->assertTrue(in_array('9 = 0 or > 1 (from core)', $corePlurals)); + $this->assertTrue(in_array('10 = 0 or > 1 (from core)', $corePlurals)); + $this->assertTrue(in_array('11 = 0 or > 1 (from core)', $corePlurals)); + $this->assertTrue(in_array('12 = 0 or > 1 (from core)', $corePlurals)); + $this->assertTrue(in_array('13 = 0 or > 1 (from core)', $corePlurals)); + $this->assertTrue(in_array('14 = 0 or > 1 (from core)', $corePlurals)); + $this->assertTrue(in_array('15 = 0 or > 1 (from core)', $corePlurals)); + $this->assertTrue(in_array('16 = 0 or > 1 (from core)', $corePlurals)); + $this->assertTrue(in_array('17 = 0 or > 1 (from core)', $corePlurals)); + $this->assertTrue(in_array('18 = 0 or > 1 (from core)', $corePlurals)); + $this->assertTrue(in_array('19 = 0 or > 1 (from core)', $corePlurals)); + $this->assertTrue(in_array('20 = 0 or > 1 (from core)', $corePlurals)); + $this->assertTrue(in_array('21 = 0 or > 1 (from core)', $corePlurals)); + $this->assertTrue(in_array('22 = 0 or > 1 (from core)', $corePlurals)); + $this->assertTrue(in_array('23 = 0 or > 1 (from core)', $corePlurals)); + $this->assertTrue(in_array('24 = 0 or > 1 (from core)', $corePlurals)); + $this->assertTrue(in_array('25 = 0 or > 1 (from core)', $corePlurals)); + } +/** + * testPoRulesZero method + * + * @access public + * @return void + */ + function testPoRulesZero() { + Configure::write('Config.language', 'rule_0_po'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 0 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('1 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('2 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('3 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('4 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('5 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('6 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('7 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('8 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('9 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('10 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('11 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('12 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('13 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('14 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('15 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('16 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('17 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('18 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('19 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('20 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('21 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('22 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('23 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('24 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('25 ends with any # (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 0 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 ends with any # (from core translated)', $corePlurals)); + } +/** + * testMoRulesZero method + * + * @access public + * @return void + */ + function testMoRulesZero() { + Configure::write('Config.language', 'rule_0_mo'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 0 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('1 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('2 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('3 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('4 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('5 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('6 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('7 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('8 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('9 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('10 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('11 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('12 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('13 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('14 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('15 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('16 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('17 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('18 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('19 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('20 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('21 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('22 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('23 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('24 ends with any # (translated)', $plurals)); + $this->assertTrue(in_array('25 ends with any # (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 0 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 ends with any # (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 ends with any # (from core translated)', $corePlurals)); + } +/** + * testPoRulesOne method + * + * @access public + * @return void + */ + function testPoRulesOne() { + Configure::write('Config.language', 'rule_1_po'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 1 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('1 = 1 (translated)', $plurals)); + $this->assertTrue(in_array('2 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('3 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('4 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('5 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('6 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('7 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('8 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('9 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('10 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('11 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('12 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('13 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('14 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('15 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('16 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('17 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('18 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('19 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('20 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('21 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('22 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('23 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('24 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('25 = 0 or > 1 (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 1 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 = 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 = 0 or > 1 (from core translated)', $corePlurals)); + } +/** + * testMoRulesOne method + * + * @access public + * @return void + */ + function testMoRulesOne() { + Configure::write('Config.language', 'rule_1_mo'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 1 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('1 = 1 (translated)', $plurals)); + $this->assertTrue(in_array('2 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('3 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('4 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('5 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('6 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('7 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('8 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('9 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('10 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('11 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('12 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('13 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('14 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('15 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('16 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('17 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('18 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('19 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('20 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('21 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('22 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('23 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('24 = 0 or > 1 (translated)', $plurals)); + $this->assertTrue(in_array('25 = 0 or > 1 (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 1 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 = 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 = 0 or > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 = 0 or > 1 (from core translated)', $corePlurals)); + } +/** + * testPoRulesTwo method + * + * @access public + * @return void + */ + function testPoRulesTwo() { + Configure::write('Config.language', 'rule_2_po'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 2 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 = 0 or 1 (translated)', $plurals)); + $this->assertTrue(in_array('1 = 0 or 1 (translated)', $plurals)); + $this->assertTrue(in_array('2 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('3 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('4 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('5 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('6 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('7 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('8 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('9 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('10 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('11 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('12 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('13 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('14 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('15 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('16 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('17 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('18 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('19 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('20 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('21 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('22 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('23 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('24 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('25 > 1 (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 2 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 = 0 or 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 = 0 or 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 > 1 (from core translated)', $corePlurals)); + } +/** + * testMoRulesTwo method + * + * @access public + * @return void + */ + function testMoRulesTwo() { + Configure::write('Config.language', 'rule_2_mo'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 2 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 = 0 or 1 (translated)', $plurals)); + $this->assertTrue(in_array('1 = 0 or 1 (translated)', $plurals)); + $this->assertTrue(in_array('2 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('3 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('4 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('5 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('6 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('7 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('8 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('9 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('10 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('11 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('12 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('13 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('14 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('15 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('16 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('17 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('18 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('19 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('20 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('21 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('22 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('23 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('24 > 1 (translated)', $plurals)); + $this->assertTrue(in_array('25 > 1 (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 2 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 = 0 or 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 = 0 or 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 > 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 > 1 (from core translated)', $corePlurals)); + } +/** + * testPoRulesThree method + * + * @access public + * @return void + */ + function testPoRulesThree() { + Configure::write('Config.language', 'rule_3_po'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 3 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 = 0 (translated)', $plurals)); + $this->assertTrue(in_array('1 ends 1 but not 11 (translated)', $plurals)); + $this->assertTrue(in_array('2 everything else (translated)', $plurals)); + $this->assertTrue(in_array('3 everything else (translated)', $plurals)); + $this->assertTrue(in_array('4 everything else (translated)', $plurals)); + $this->assertTrue(in_array('5 everything else (translated)', $plurals)); + $this->assertTrue(in_array('6 everything else (translated)', $plurals)); + $this->assertTrue(in_array('7 everything else (translated)', $plurals)); + $this->assertTrue(in_array('8 everything else (translated)', $plurals)); + $this->assertTrue(in_array('9 everything else (translated)', $plurals)); + $this->assertTrue(in_array('10 everything else (translated)', $plurals)); + $this->assertTrue(in_array('11 everything else (translated)', $plurals)); + $this->assertTrue(in_array('12 everything else (translated)', $plurals)); + $this->assertTrue(in_array('13 everything else (translated)', $plurals)); + $this->assertTrue(in_array('14 everything else (translated)', $plurals)); + $this->assertTrue(in_array('15 everything else (translated)', $plurals)); + $this->assertTrue(in_array('16 everything else (translated)', $plurals)); + $this->assertTrue(in_array('17 everything else (translated)', $plurals)); + $this->assertTrue(in_array('18 everything else (translated)', $plurals)); + $this->assertTrue(in_array('19 everything else (translated)', $plurals)); + $this->assertTrue(in_array('20 everything else (translated)', $plurals)); + $this->assertTrue(in_array('21 ends 1 but not 11 (translated)', $plurals)); + $this->assertTrue(in_array('22 everything else (translated)', $plurals)); + $this->assertTrue(in_array('23 everything else (translated)', $plurals)); + $this->assertTrue(in_array('24 everything else (translated)', $plurals)); + $this->assertTrue(in_array('25 everything else (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 3 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 = 0 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 ends 1 but not 11 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 ends 1 but not 11 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals)); + } +/** + * testMoRulesThree method + * + * @access public + * @return void + */ + function testMoRulesThree() { + Configure::write('Config.language', 'rule_3_mo'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 3 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 = 0 (translated)', $plurals)); + $this->assertTrue(in_array('1 ends 1 but not 11 (translated)', $plurals)); + $this->assertTrue(in_array('2 everything else (translated)', $plurals)); + $this->assertTrue(in_array('3 everything else (translated)', $plurals)); + $this->assertTrue(in_array('4 everything else (translated)', $plurals)); + $this->assertTrue(in_array('5 everything else (translated)', $plurals)); + $this->assertTrue(in_array('6 everything else (translated)', $plurals)); + $this->assertTrue(in_array('7 everything else (translated)', $plurals)); + $this->assertTrue(in_array('8 everything else (translated)', $plurals)); + $this->assertTrue(in_array('9 everything else (translated)', $plurals)); + $this->assertTrue(in_array('10 everything else (translated)', $plurals)); + $this->assertTrue(in_array('11 everything else (translated)', $plurals)); + $this->assertTrue(in_array('12 everything else (translated)', $plurals)); + $this->assertTrue(in_array('13 everything else (translated)', $plurals)); + $this->assertTrue(in_array('14 everything else (translated)', $plurals)); + $this->assertTrue(in_array('15 everything else (translated)', $plurals)); + $this->assertTrue(in_array('16 everything else (translated)', $plurals)); + $this->assertTrue(in_array('17 everything else (translated)', $plurals)); + $this->assertTrue(in_array('18 everything else (translated)', $plurals)); + $this->assertTrue(in_array('19 everything else (translated)', $plurals)); + $this->assertTrue(in_array('20 everything else (translated)', $plurals)); + $this->assertTrue(in_array('21 ends 1 but not 11 (translated)', $plurals)); + $this->assertTrue(in_array('22 everything else (translated)', $plurals)); + $this->assertTrue(in_array('23 everything else (translated)', $plurals)); + $this->assertTrue(in_array('24 everything else (translated)', $plurals)); + $this->assertTrue(in_array('25 everything else (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 3 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 = 0 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 ends 1 but not 11 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 ends 1 but not 11 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals)); + } +/** + * testPoRulesFour method + * + * @access public + * @return void + */ + function testPoRulesFour() { + Configure::write('Config.language', 'rule_4_po'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 4 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 everything else (translated)', $plurals)); + $this->assertTrue(in_array('1 = 1 (translated)', $plurals)); + $this->assertTrue(in_array('2 = 2 (translated)', $plurals)); + $this->assertTrue(in_array('3 everything else (translated)', $plurals)); + $this->assertTrue(in_array('4 everything else (translated)', $plurals)); + $this->assertTrue(in_array('5 everything else (translated)', $plurals)); + $this->assertTrue(in_array('6 everything else (translated)', $plurals)); + $this->assertTrue(in_array('7 everything else (translated)', $plurals)); + $this->assertTrue(in_array('8 everything else (translated)', $plurals)); + $this->assertTrue(in_array('9 everything else (translated)', $plurals)); + $this->assertTrue(in_array('10 everything else (translated)', $plurals)); + $this->assertTrue(in_array('11 everything else (translated)', $plurals)); + $this->assertTrue(in_array('12 everything else (translated)', $plurals)); + $this->assertTrue(in_array('13 everything else (translated)', $plurals)); + $this->assertTrue(in_array('14 everything else (translated)', $plurals)); + $this->assertTrue(in_array('15 everything else (translated)', $plurals)); + $this->assertTrue(in_array('16 everything else (translated)', $plurals)); + $this->assertTrue(in_array('17 everything else (translated)', $plurals)); + $this->assertTrue(in_array('18 everything else (translated)', $plurals)); + $this->assertTrue(in_array('19 everything else (translated)', $plurals)); + $this->assertTrue(in_array('20 everything else (translated)', $plurals)); + $this->assertTrue(in_array('21 everything else (translated)', $plurals)); + $this->assertTrue(in_array('22 everything else (translated)', $plurals)); + $this->assertTrue(in_array('23 everything else (translated)', $plurals)); + $this->assertTrue(in_array('24 everything else (translated)', $plurals)); + $this->assertTrue(in_array('25 everything else (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 4 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 = 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 = 2 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals)); + } +/** + * testMoRulesFour method + * + * @access public + * @return void + */ + function testMoRulesFour() { + Configure::write('Config.language', 'rule_4_mo'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 4 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 everything else (translated)', $plurals)); + $this->assertTrue(in_array('1 = 1 (translated)', $plurals)); + $this->assertTrue(in_array('2 = 2 (translated)', $plurals)); + $this->assertTrue(in_array('3 everything else (translated)', $plurals)); + $this->assertTrue(in_array('4 everything else (translated)', $plurals)); + $this->assertTrue(in_array('5 everything else (translated)', $plurals)); + $this->assertTrue(in_array('6 everything else (translated)', $plurals)); + $this->assertTrue(in_array('7 everything else (translated)', $plurals)); + $this->assertTrue(in_array('8 everything else (translated)', $plurals)); + $this->assertTrue(in_array('9 everything else (translated)', $plurals)); + $this->assertTrue(in_array('10 everything else (translated)', $plurals)); + $this->assertTrue(in_array('11 everything else (translated)', $plurals)); + $this->assertTrue(in_array('12 everything else (translated)', $plurals)); + $this->assertTrue(in_array('13 everything else (translated)', $plurals)); + $this->assertTrue(in_array('14 everything else (translated)', $plurals)); + $this->assertTrue(in_array('15 everything else (translated)', $plurals)); + $this->assertTrue(in_array('16 everything else (translated)', $plurals)); + $this->assertTrue(in_array('17 everything else (translated)', $plurals)); + $this->assertTrue(in_array('18 everything else (translated)', $plurals)); + $this->assertTrue(in_array('19 everything else (translated)', $plurals)); + $this->assertTrue(in_array('20 everything else (translated)', $plurals)); + $this->assertTrue(in_array('21 everything else (translated)', $plurals)); + $this->assertTrue(in_array('22 everything else (translated)', $plurals)); + $this->assertTrue(in_array('23 everything else (translated)', $plurals)); + $this->assertTrue(in_array('24 everything else (translated)', $plurals)); + $this->assertTrue(in_array('25 everything else (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 4 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 = 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 = 2 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals)); + } +/** + * testPoRulesFive method + * + * @access public + * @return void + */ + function testPoRulesFive() { + Configure::write('Config.language', 'rule_5_po'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 5 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('0 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('1 = 1 (translated)', $plurals)); + $this->assertTrue(in_array('2 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('3 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('4 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('5 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('6 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('7 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('8 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('9 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('10 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('11 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('12 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('13 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('14 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('15 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('16 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('17 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('18 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('19 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('20 everything else (translated)', $plurals)); + $this->assertTrue(in_array('21 everything else (translated)', $plurals)); + $this->assertTrue(in_array('22 everything else (translated)', $plurals)); + $this->assertTrue(in_array('23 everything else (translated)', $plurals)); + $this->assertTrue(in_array('24 everything else (translated)', $plurals)); + $this->assertTrue(in_array('25 everything else (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 5 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('0 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 = 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals)); + } +/** + * testMoRulesFive method + * + * @access public + * @return void + */ + function testMoRulesFive() { + Configure::write('Config.language', 'rule_5_mo'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 5 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('0 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('1 = 1 (translated)', $plurals)); + $this->assertTrue(in_array('2 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('3 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('4 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('5 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('6 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('7 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('8 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('9 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('10 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('11 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('12 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('13 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('14 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('15 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('16 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('17 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('18 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('19 = 0 or ends in 01-19 (translated)', $plurals)); + $this->assertTrue(in_array('20 everything else (translated)', $plurals)); + $this->assertTrue(in_array('21 everything else (translated)', $plurals)); + $this->assertTrue(in_array('22 everything else (translated)', $plurals)); + $this->assertTrue(in_array('23 everything else (translated)', $plurals)); + $this->assertTrue(in_array('24 everything else (translated)', $plurals)); + $this->assertTrue(in_array('25 everything else (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 5 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('0 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 = 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 = 0 or ends in 01-19 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals)); + } +/** + * testPoRulesSix method + * + * @access public + * @return void + */ + function testPoRulesSix() { + Configure::write('Config.language', 'rule_6_po'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 6 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 ends in 0 or ends in 10-20 (translated)', $plurals)); + $this->assertTrue(in_array('1 ends in 1, not 11 (translated)', $plurals)); + $this->assertTrue(in_array('2 everything else (translated)', $plurals)); + $this->assertTrue(in_array('3 everything else (translated)', $plurals)); + $this->assertTrue(in_array('4 everything else (translated)', $plurals)); + $this->assertTrue(in_array('5 everything else (translated)', $plurals)); + $this->assertTrue(in_array('6 everything else (translated)', $plurals)); + $this->assertTrue(in_array('7 everything else (translated)', $plurals)); + $this->assertTrue(in_array('8 everything else (translated)', $plurals)); + $this->assertTrue(in_array('9 everything else (translated)', $plurals)); + $this->assertTrue(in_array('10 ends in 0 or ends in 10-20 (translated)', $plurals)); + $this->assertTrue(in_array('11 ends in 0 or ends in 10-20 (translated)', $plurals)); + $this->assertTrue(in_array('12 ends in 0 or ends in 10-20 (translated)', $plurals)); + $this->assertTrue(in_array('13 ends in 0 or ends in 10-20 (translated)', $plurals)); + $this->assertTrue(in_array('14 ends in 0 or ends in 10-20 (translated)', $plurals)); + $this->assertTrue(in_array('15 ends in 0 or ends in 10-20 (translated)', $plurals)); + $this->assertTrue(in_array('16 ends in 0 or ends in 10-20 (translated)', $plurals)); + $this->assertTrue(in_array('17 ends in 0 or ends in 10-20 (translated)', $plurals)); + $this->assertTrue(in_array('18 ends in 0 or ends in 10-20 (translated)', $plurals)); + $this->assertTrue(in_array('19 ends in 0 or ends in 10-20 (translated)', $plurals)); + $this->assertTrue(in_array('20 ends in 0 or ends in 10-20 (translated)', $plurals)); + $this->assertTrue(in_array('21 ends in 1, not 11 (translated)', $plurals)); + $this->assertTrue(in_array('22 everything else (translated)', $plurals)); + $this->assertTrue(in_array('23 everything else (translated)', $plurals)); + $this->assertTrue(in_array('24 everything else (translated)', $plurals)); + $this->assertTrue(in_array('25 everything else (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 6 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 ends in 0 or ends in 10-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 ends in 1, not 11 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 ends in 0 or ends in 10-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 ends in 0 or ends in 10-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 ends in 0 or ends in 10-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 ends in 0 or ends in 10-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 ends in 0 or ends in 10-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 ends in 0 or ends in 10-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 ends in 0 or ends in 10-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 ends in 0 or ends in 10-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 ends in 0 or ends in 10-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 ends in 0 or ends in 10-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 ends in 0 or ends in 10-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 ends in 1, not 11 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals)); + } +/** + * testMoRulesSix method + * + * @access public + * @return void + */ + function testMoRulesSix() { + Configure::write('Config.language', 'rule_6_mo'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 6 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 ends in 0 or ends in 10-20 (translated)', $plurals)); + $this->assertTrue(in_array('1 ends in 1, not 11 (translated)', $plurals)); + $this->assertTrue(in_array('2 everything else (translated)', $plurals)); + $this->assertTrue(in_array('3 everything else (translated)', $plurals)); + $this->assertTrue(in_array('4 everything else (translated)', $plurals)); + $this->assertTrue(in_array('5 everything else (translated)', $plurals)); + $this->assertTrue(in_array('6 everything else (translated)', $plurals)); + $this->assertTrue(in_array('7 everything else (translated)', $plurals)); + $this->assertTrue(in_array('8 everything else (translated)', $plurals)); + $this->assertTrue(in_array('9 everything else (translated)', $plurals)); + $this->assertTrue(in_array('10 ends in 0 or ends in 10-20 (translated)', $plurals)); + $this->assertTrue(in_array('11 ends in 0 or ends in 10-20 (translated)', $plurals)); + $this->assertTrue(in_array('12 ends in 0 or ends in 10-20 (translated)', $plurals)); + $this->assertTrue(in_array('13 ends in 0 or ends in 10-20 (translated)', $plurals)); + $this->assertTrue(in_array('14 ends in 0 or ends in 10-20 (translated)', $plurals)); + $this->assertTrue(in_array('15 ends in 0 or ends in 10-20 (translated)', $plurals)); + $this->assertTrue(in_array('16 ends in 0 or ends in 10-20 (translated)', $plurals)); + $this->assertTrue(in_array('17 ends in 0 or ends in 10-20 (translated)', $plurals)); + $this->assertTrue(in_array('18 ends in 0 or ends in 10-20 (translated)', $plurals)); + $this->assertTrue(in_array('19 ends in 0 or ends in 10-20 (translated)', $plurals)); + $this->assertTrue(in_array('20 ends in 0 or ends in 10-20 (translated)', $plurals)); + $this->assertTrue(in_array('21 ends in 1, not 11 (translated)', $plurals)); + $this->assertTrue(in_array('22 everything else (translated)', $plurals)); + $this->assertTrue(in_array('23 everything else (translated)', $plurals)); + $this->assertTrue(in_array('24 everything else (translated)', $plurals)); + $this->assertTrue(in_array('25 everything else (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 6 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 ends in 0 or ends in 10-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 ends in 1, not 11 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 ends in 0 or ends in 10-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 ends in 0 or ends in 10-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 ends in 0 or ends in 10-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 ends in 0 or ends in 10-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 ends in 0 or ends in 10-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 ends in 0 or ends in 10-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 ends in 0 or ends in 10-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 ends in 0 or ends in 10-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 ends in 0 or ends in 10-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 ends in 0 or ends in 10-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 ends in 0 or ends in 10-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 ends in 1, not 11 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals)); + } +/** + * testPoRulesSeven method + * + * @access public + * @return void + */ + function testPoRulesSeven() { + Configure::write('Config.language', 'rule_7_po'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 7 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 everything else (translated)', $plurals)); + $this->assertTrue(in_array('1 ends in 1, not 11 (translated)', $plurals)); + $this->assertTrue(in_array('2 ends in 2-4, not 12-14 (translated)', $plurals)); + $this->assertTrue(in_array('3 ends in 2-4, not 12-14 (translated)', $plurals)); + $this->assertTrue(in_array('4 ends in 2-4, not 12-14 (translated)', $plurals)); + $this->assertTrue(in_array('5 everything else (translated)', $plurals)); + $this->assertTrue(in_array('6 everything else (translated)', $plurals)); + $this->assertTrue(in_array('7 everything else (translated)', $plurals)); + $this->assertTrue(in_array('8 everything else (translated)', $plurals)); + $this->assertTrue(in_array('9 everything else (translated)', $plurals)); + $this->assertTrue(in_array('10 everything else (translated)', $plurals)); + $this->assertTrue(in_array('11 everything else (translated)', $plurals)); + $this->assertTrue(in_array('12 everything else (translated)', $plurals)); + $this->assertTrue(in_array('13 everything else (translated)', $plurals)); + $this->assertTrue(in_array('14 everything else (translated)', $plurals)); + $this->assertTrue(in_array('15 everything else (translated)', $plurals)); + $this->assertTrue(in_array('16 everything else (translated)', $plurals)); + $this->assertTrue(in_array('17 everything else (translated)', $plurals)); + $this->assertTrue(in_array('18 everything else (translated)', $plurals)); + $this->assertTrue(in_array('19 everything else (translated)', $plurals)); + $this->assertTrue(in_array('20 everything else (translated)', $plurals)); + $this->assertTrue(in_array('21 ends in 1, not 11 (translated)', $plurals)); + $this->assertTrue(in_array('22 ends in 2-4, not 12-14 (translated)', $plurals)); + $this->assertTrue(in_array('23 ends in 2-4, not 12-14 (translated)', $plurals)); + $this->assertTrue(in_array('24 ends in 2-4, not 12-14 (translated)', $plurals)); + $this->assertTrue(in_array('25 everything else (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 7 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 ends in 1, not 11 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 ends in 2-4, not 12-14 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 ends in 2-4, not 12-14 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 ends in 2-4, not 12-14 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 ends in 1, not 11 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 ends in 2-4, not 12-14 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 ends in 2-4, not 12-14 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 ends in 2-4, not 12-14 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals)); + } +/** + * testMoRulesSeven method + * + * @access public + * @return void + */ + function testMoRulesSeven() { + Configure::write('Config.language', 'rule_7_mo'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 7 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 everything else (translated)', $plurals)); + $this->assertTrue(in_array('1 ends in 1, not 11 (translated)', $plurals)); + $this->assertTrue(in_array('2 ends in 2-4, not 12-14 (translated)', $plurals)); + $this->assertTrue(in_array('3 ends in 2-4, not 12-14 (translated)', $plurals)); + $this->assertTrue(in_array('4 ends in 2-4, not 12-14 (translated)', $plurals)); + $this->assertTrue(in_array('5 everything else (translated)', $plurals)); + $this->assertTrue(in_array('6 everything else (translated)', $plurals)); + $this->assertTrue(in_array('7 everything else (translated)', $plurals)); + $this->assertTrue(in_array('8 everything else (translated)', $plurals)); + $this->assertTrue(in_array('9 everything else (translated)', $plurals)); + $this->assertTrue(in_array('10 everything else (translated)', $plurals)); + $this->assertTrue(in_array('11 everything else (translated)', $plurals)); + $this->assertTrue(in_array('12 everything else (translated)', $plurals)); + $this->assertTrue(in_array('13 everything else (translated)', $plurals)); + $this->assertTrue(in_array('14 everything else (translated)', $plurals)); + $this->assertTrue(in_array('15 everything else (translated)', $plurals)); + $this->assertTrue(in_array('16 everything else (translated)', $plurals)); + $this->assertTrue(in_array('17 everything else (translated)', $plurals)); + $this->assertTrue(in_array('18 everything else (translated)', $plurals)); + $this->assertTrue(in_array('19 everything else (translated)', $plurals)); + $this->assertTrue(in_array('20 everything else (translated)', $plurals)); + $this->assertTrue(in_array('21 ends in 1, not 11 (translated)', $plurals)); + $this->assertTrue(in_array('22 ends in 2-4, not 12-14 (translated)', $plurals)); + $this->assertTrue(in_array('23 ends in 2-4, not 12-14 (translated)', $plurals)); + $this->assertTrue(in_array('24 ends in 2-4, not 12-14 (translated)', $plurals)); + $this->assertTrue(in_array('25 everything else (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 7 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 ends in 1, not 11 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 ends in 2-4, not 12-14 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 ends in 2-4, not 12-14 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 ends in 2-4, not 12-14 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 ends in 1, not 11 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 ends in 2-4, not 12-14 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 ends in 2-4, not 12-14 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 ends in 2-4, not 12-14 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals)); + } +/** + * testPoRulesEight method + * + * @access public + * @return void + */ + function testPoRulesEight() { + Configure::write('Config.language', 'rule_8_po'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 8 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 everything else (translated)', $plurals)); + $this->assertTrue(in_array('1 is 1 (translated)', $plurals)); + $this->assertTrue(in_array('2 is 2-4 (translated)', $plurals)); + $this->assertTrue(in_array('3 is 2-4 (translated)', $plurals)); + $this->assertTrue(in_array('4 is 2-4 (translated)', $plurals)); + $this->assertTrue(in_array('5 everything else (translated)', $plurals)); + $this->assertTrue(in_array('6 everything else (translated)', $plurals)); + $this->assertTrue(in_array('7 everything else (translated)', $plurals)); + $this->assertTrue(in_array('8 everything else (translated)', $plurals)); + $this->assertTrue(in_array('9 everything else (translated)', $plurals)); + $this->assertTrue(in_array('10 everything else (translated)', $plurals)); + $this->assertTrue(in_array('11 everything else (translated)', $plurals)); + $this->assertTrue(in_array('12 everything else (translated)', $plurals)); + $this->assertTrue(in_array('13 everything else (translated)', $plurals)); + $this->assertTrue(in_array('14 everything else (translated)', $plurals)); + $this->assertTrue(in_array('15 everything else (translated)', $plurals)); + $this->assertTrue(in_array('16 everything else (translated)', $plurals)); + $this->assertTrue(in_array('17 everything else (translated)', $plurals)); + $this->assertTrue(in_array('18 everything else (translated)', $plurals)); + $this->assertTrue(in_array('19 everything else (translated)', $plurals)); + $this->assertTrue(in_array('20 everything else (translated)', $plurals)); + $this->assertTrue(in_array('21 everything else (translated)', $plurals)); + $this->assertTrue(in_array('22 everything else (translated)', $plurals)); + $this->assertTrue(in_array('23 everything else (translated)', $plurals)); + $this->assertTrue(in_array('24 everything else (translated)', $plurals)); + $this->assertTrue(in_array('25 everything else (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 8 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 is 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 is 2-4 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 is 2-4 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 is 2-4 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals)); + } +/** + * testMoRulesEight method + * + * @access public + * @return void + */ + function testMoRulesEight() { + Configure::write('Config.language', 'rule_8_mo'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 8 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 everything else (translated)', $plurals)); + $this->assertTrue(in_array('1 is 1 (translated)', $plurals)); + $this->assertTrue(in_array('2 is 2-4 (translated)', $plurals)); + $this->assertTrue(in_array('3 is 2-4 (translated)', $plurals)); + $this->assertTrue(in_array('4 is 2-4 (translated)', $plurals)); + $this->assertTrue(in_array('5 everything else (translated)', $plurals)); + $this->assertTrue(in_array('6 everything else (translated)', $plurals)); + $this->assertTrue(in_array('7 everything else (translated)', $plurals)); + $this->assertTrue(in_array('8 everything else (translated)', $plurals)); + $this->assertTrue(in_array('9 everything else (translated)', $plurals)); + $this->assertTrue(in_array('10 everything else (translated)', $plurals)); + $this->assertTrue(in_array('11 everything else (translated)', $plurals)); + $this->assertTrue(in_array('12 everything else (translated)', $plurals)); + $this->assertTrue(in_array('13 everything else (translated)', $plurals)); + $this->assertTrue(in_array('14 everything else (translated)', $plurals)); + $this->assertTrue(in_array('15 everything else (translated)', $plurals)); + $this->assertTrue(in_array('16 everything else (translated)', $plurals)); + $this->assertTrue(in_array('17 everything else (translated)', $plurals)); + $this->assertTrue(in_array('18 everything else (translated)', $plurals)); + $this->assertTrue(in_array('19 everything else (translated)', $plurals)); + $this->assertTrue(in_array('20 everything else (translated)', $plurals)); + $this->assertTrue(in_array('21 everything else (translated)', $plurals)); + $this->assertTrue(in_array('22 everything else (translated)', $plurals)); + $this->assertTrue(in_array('23 everything else (translated)', $plurals)); + $this->assertTrue(in_array('24 everything else (translated)', $plurals)); + $this->assertTrue(in_array('25 everything else (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 8 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 is 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 is 2-4 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 is 2-4 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 is 2-4 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals)); + } +/** + * testPoRulesNine method + * + * @access public + * @return void + */ + function testPoRulesNine() { + Configure::write('Config.language', 'rule_9_po'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 9 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 everything else (translated)', $plurals)); + $this->assertTrue(in_array('0 everything else (translated)', $plurals)); + $this->assertTrue(in_array('1 is 1 (translated)', $plurals)); + $this->assertTrue(in_array('2 ends in 2-4, not 12-14 (translated)', $plurals)); + $this->assertTrue(in_array('3 ends in 2-4, not 12-14 (translated)', $plurals)); + $this->assertTrue(in_array('4 ends in 2-4, not 12-14 (translated)', $plurals)); + $this->assertTrue(in_array('5 everything else (translated)', $plurals)); + $this->assertTrue(in_array('6 everything else (translated)', $plurals)); + $this->assertTrue(in_array('7 everything else (translated)', $plurals)); + $this->assertTrue(in_array('8 everything else (translated)', $plurals)); + $this->assertTrue(in_array('9 everything else (translated)', $plurals)); + $this->assertTrue(in_array('10 everything else (translated)', $plurals)); + $this->assertTrue(in_array('11 everything else (translated)', $plurals)); + $this->assertTrue(in_array('12 everything else (translated)', $plurals)); + $this->assertTrue(in_array('13 everything else (translated)', $plurals)); + $this->assertTrue(in_array('14 everything else (translated)', $plurals)); + $this->assertTrue(in_array('15 everything else (translated)', $plurals)); + $this->assertTrue(in_array('16 everything else (translated)', $plurals)); + $this->assertTrue(in_array('17 everything else (translated)', $plurals)); + $this->assertTrue(in_array('18 everything else (translated)', $plurals)); + $this->assertTrue(in_array('19 everything else (translated)', $plurals)); + $this->assertTrue(in_array('20 everything else (translated)', $plurals)); + $this->assertTrue(in_array('21 everything else (translated)', $plurals)); + $this->assertTrue(in_array('22 ends in 2-4, not 12-14 (translated)', $plurals)); + $this->assertTrue(in_array('23 ends in 2-4, not 12-14 (translated)', $plurals)); + $this->assertTrue(in_array('24 ends in 2-4, not 12-14 (translated)', $plurals)); + $this->assertTrue(in_array('25 everything else (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 9 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 is 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 ends in 2-4, not 12-14 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 ends in 2-4, not 12-14 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 ends in 2-4, not 12-14 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 ends in 2-4, not 12-14 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 ends in 2-4, not 12-14 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 ends in 2-4, not 12-14 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals)); + } +/** + * testMoRulesNine method + * + * @access public + * @return void + */ + function testMoRulesNine() { + Configure::write('Config.language', 'rule_9_po'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 9 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 everything else (translated)', $plurals)); + $this->assertTrue(in_array('0 everything else (translated)', $plurals)); + $this->assertTrue(in_array('1 is 1 (translated)', $plurals)); + $this->assertTrue(in_array('2 ends in 2-4, not 12-14 (translated)', $plurals)); + $this->assertTrue(in_array('3 ends in 2-4, not 12-14 (translated)', $plurals)); + $this->assertTrue(in_array('4 ends in 2-4, not 12-14 (translated)', $plurals)); + $this->assertTrue(in_array('5 everything else (translated)', $plurals)); + $this->assertTrue(in_array('6 everything else (translated)', $plurals)); + $this->assertTrue(in_array('7 everything else (translated)', $plurals)); + $this->assertTrue(in_array('8 everything else (translated)', $plurals)); + $this->assertTrue(in_array('9 everything else (translated)', $plurals)); + $this->assertTrue(in_array('10 everything else (translated)', $plurals)); + $this->assertTrue(in_array('11 everything else (translated)', $plurals)); + $this->assertTrue(in_array('12 everything else (translated)', $plurals)); + $this->assertTrue(in_array('13 everything else (translated)', $plurals)); + $this->assertTrue(in_array('14 everything else (translated)', $plurals)); + $this->assertTrue(in_array('15 everything else (translated)', $plurals)); + $this->assertTrue(in_array('16 everything else (translated)', $plurals)); + $this->assertTrue(in_array('17 everything else (translated)', $plurals)); + $this->assertTrue(in_array('18 everything else (translated)', $plurals)); + $this->assertTrue(in_array('19 everything else (translated)', $plurals)); + $this->assertTrue(in_array('20 everything else (translated)', $plurals)); + $this->assertTrue(in_array('21 everything else (translated)', $plurals)); + $this->assertTrue(in_array('22 ends in 2-4, not 12-14 (translated)', $plurals)); + $this->assertTrue(in_array('23 ends in 2-4, not 12-14 (translated)', $plurals)); + $this->assertTrue(in_array('24 ends in 2-4, not 12-14 (translated)', $plurals)); + $this->assertTrue(in_array('25 everything else (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 9 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 is 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 ends in 2-4, not 12-14 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 ends in 2-4, not 12-14 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 ends in 2-4, not 12-14 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 ends in 2-4, not 12-14 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 ends in 2-4, not 12-14 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 ends in 2-4, not 12-14 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals)); + } +/** + * testPoRulesTen method + * + * @access public + * @return void + */ + function testPoRulesTen() { + Configure::write('Config.language', 'rule_10_po'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 10 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 everything else (translated)', $plurals)); + $this->assertTrue(in_array('0 everything else (translated)', $plurals)); + $this->assertTrue(in_array('1 ends in 1 (translated)', $plurals)); + $this->assertTrue(in_array('2 ends in 2 (translated)', $plurals)); + $this->assertTrue(in_array('3 ends in 03-04 (translated)', $plurals)); + $this->assertTrue(in_array('4 ends in 03-04 (translated)', $plurals)); + $this->assertTrue(in_array('5 everything else (translated)', $plurals)); + $this->assertTrue(in_array('6 everything else (translated)', $plurals)); + $this->assertTrue(in_array('7 everything else (translated)', $plurals)); + $this->assertTrue(in_array('8 everything else (translated)', $plurals)); + $this->assertTrue(in_array('9 everything else (translated)', $plurals)); + $this->assertTrue(in_array('10 everything else (translated)', $plurals)); + $this->assertTrue(in_array('11 everything else (translated)', $plurals)); + $this->assertTrue(in_array('12 everything else (translated)', $plurals)); + $this->assertTrue(in_array('13 everything else (translated)', $plurals)); + $this->assertTrue(in_array('14 everything else (translated)', $plurals)); + $this->assertTrue(in_array('15 everything else (translated)', $plurals)); + $this->assertTrue(in_array('16 everything else (translated)', $plurals)); + $this->assertTrue(in_array('17 everything else (translated)', $plurals)); + $this->assertTrue(in_array('18 everything else (translated)', $plurals)); + $this->assertTrue(in_array('19 everything else (translated)', $plurals)); + $this->assertTrue(in_array('20 everything else (translated)', $plurals)); + $this->assertTrue(in_array('21 everything else (translated)', $plurals)); + $this->assertTrue(in_array('22 everything else (translated)', $plurals)); + $this->assertTrue(in_array('23 everything else (translated)', $plurals)); + $this->assertTrue(in_array('24 everything else (translated)', $plurals)); + $this->assertTrue(in_array('25 everything else (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 10 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 ends in 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 ends in 2 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 ends in 03-04 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 ends in 03-04 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals)); + } +/** + * testMoRulesTen method + * + * @access public + * @return void + */ + function testMoRulesTen() { + Configure::write('Config.language', 'rule_10_mo'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 10 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 everything else (translated)', $plurals)); + $this->assertTrue(in_array('0 everything else (translated)', $plurals)); + $this->assertTrue(in_array('1 ends in 1 (translated)', $plurals)); + $this->assertTrue(in_array('2 ends in 2 (translated)', $plurals)); + $this->assertTrue(in_array('3 ends in 03-04 (translated)', $plurals)); + $this->assertTrue(in_array('4 ends in 03-04 (translated)', $plurals)); + $this->assertTrue(in_array('5 everything else (translated)', $plurals)); + $this->assertTrue(in_array('6 everything else (translated)', $plurals)); + $this->assertTrue(in_array('7 everything else (translated)', $plurals)); + $this->assertTrue(in_array('8 everything else (translated)', $plurals)); + $this->assertTrue(in_array('9 everything else (translated)', $plurals)); + $this->assertTrue(in_array('10 everything else (translated)', $plurals)); + $this->assertTrue(in_array('11 everything else (translated)', $plurals)); + $this->assertTrue(in_array('12 everything else (translated)', $plurals)); + $this->assertTrue(in_array('13 everything else (translated)', $plurals)); + $this->assertTrue(in_array('14 everything else (translated)', $plurals)); + $this->assertTrue(in_array('15 everything else (translated)', $plurals)); + $this->assertTrue(in_array('16 everything else (translated)', $plurals)); + $this->assertTrue(in_array('17 everything else (translated)', $plurals)); + $this->assertTrue(in_array('18 everything else (translated)', $plurals)); + $this->assertTrue(in_array('19 everything else (translated)', $plurals)); + $this->assertTrue(in_array('20 everything else (translated)', $plurals)); + $this->assertTrue(in_array('21 everything else (translated)', $plurals)); + $this->assertTrue(in_array('22 everything else (translated)', $plurals)); + $this->assertTrue(in_array('23 everything else (translated)', $plurals)); + $this->assertTrue(in_array('24 everything else (translated)', $plurals)); + $this->assertTrue(in_array('25 everything else (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 10 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 ends in 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 ends in 2 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 ends in 03-04 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 ends in 03-04 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals)); + } +/** + * testPoRulesEleven method + * + * @access public + * @return void + */ + function testPoRulesEleven() { + Configure::write('Config.language', 'rule_11_po'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 11 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 everything else (translated)', $plurals)); + $this->assertTrue(in_array('1 is 1 (translated)', $plurals)); + $this->assertTrue(in_array('2 is 2 (translated)', $plurals)); + $this->assertTrue(in_array('3 is 3-6 (translated)', $plurals)); + $this->assertTrue(in_array('4 is 3-6 (translated)', $plurals)); + $this->assertTrue(in_array('5 is 3-6 (translated)', $plurals)); + $this->assertTrue(in_array('6 is 3-6 (translated)', $plurals)); + $this->assertTrue(in_array('7 is 7-10 (translated)', $plurals)); + $this->assertTrue(in_array('8 is 7-10 (translated)', $plurals)); + $this->assertTrue(in_array('9 is 7-10 (translated)', $plurals)); + $this->assertTrue(in_array('10 is 7-10 (translated)', $plurals)); + $this->assertTrue(in_array('11 everything else (translated)', $plurals)); + $this->assertTrue(in_array('12 everything else (translated)', $plurals)); + $this->assertTrue(in_array('13 everything else (translated)', $plurals)); + $this->assertTrue(in_array('14 everything else (translated)', $plurals)); + $this->assertTrue(in_array('15 everything else (translated)', $plurals)); + $this->assertTrue(in_array('16 everything else (translated)', $plurals)); + $this->assertTrue(in_array('17 everything else (translated)', $plurals)); + $this->assertTrue(in_array('18 everything else (translated)', $plurals)); + $this->assertTrue(in_array('19 everything else (translated)', $plurals)); + $this->assertTrue(in_array('20 everything else (translated)', $plurals)); + $this->assertTrue(in_array('21 everything else (translated)', $plurals)); + $this->assertTrue(in_array('22 everything else (translated)', $plurals)); + $this->assertTrue(in_array('23 everything else (translated)', $plurals)); + $this->assertTrue(in_array('24 everything else (translated)', $plurals)); + $this->assertTrue(in_array('25 everything else (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 11 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 is 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 is 2 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 is 3-6 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 is 3-6 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 is 3-6 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 is 3-6 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 is 7-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 is 7-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 is 7-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 is 7-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals)); + } +/** + * testMoRulesEleven method + * + * @access public + * @return void + */ + function testMoRulesEleven() { + Configure::write('Config.language', 'rule_11_mo'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 11 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 everything else (translated)', $plurals)); + $this->assertTrue(in_array('1 is 1 (translated)', $plurals)); + $this->assertTrue(in_array('2 is 2 (translated)', $plurals)); + $this->assertTrue(in_array('3 is 3-6 (translated)', $plurals)); + $this->assertTrue(in_array('4 is 3-6 (translated)', $plurals)); + $this->assertTrue(in_array('5 is 3-6 (translated)', $plurals)); + $this->assertTrue(in_array('6 is 3-6 (translated)', $plurals)); + $this->assertTrue(in_array('7 is 7-10 (translated)', $plurals)); + $this->assertTrue(in_array('8 is 7-10 (translated)', $plurals)); + $this->assertTrue(in_array('9 is 7-10 (translated)', $plurals)); + $this->assertTrue(in_array('10 is 7-10 (translated)', $plurals)); + $this->assertTrue(in_array('11 everything else (translated)', $plurals)); + $this->assertTrue(in_array('12 everything else (translated)', $plurals)); + $this->assertTrue(in_array('13 everything else (translated)', $plurals)); + $this->assertTrue(in_array('14 everything else (translated)', $plurals)); + $this->assertTrue(in_array('15 everything else (translated)', $plurals)); + $this->assertTrue(in_array('16 everything else (translated)', $plurals)); + $this->assertTrue(in_array('17 everything else (translated)', $plurals)); + $this->assertTrue(in_array('18 everything else (translated)', $plurals)); + $this->assertTrue(in_array('19 everything else (translated)', $plurals)); + $this->assertTrue(in_array('20 everything else (translated)', $plurals)); + $this->assertTrue(in_array('21 everything else (translated)', $plurals)); + $this->assertTrue(in_array('22 everything else (translated)', $plurals)); + $this->assertTrue(in_array('23 everything else (translated)', $plurals)); + $this->assertTrue(in_array('24 everything else (translated)', $plurals)); + $this->assertTrue(in_array('25 everything else (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 11 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 is 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 is 2 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 is 3-6 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 is 3-6 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 is 3-6 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 is 3-6 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 is 7-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 is 7-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 is 7-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 is 7-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals)); + } +/** + * testPoRulesTwelve method + * + * @access public + * @return void + */ + function testPoRulesTwelve() { + Configure::write('Config.language', 'rule_12_po'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 12 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 is 0 or 3-10 (translated)', $plurals)); + $this->assertTrue(in_array('1 is 1 (translated)', $plurals)); + $this->assertTrue(in_array('2 is 2 (translated)', $plurals)); + $this->assertTrue(in_array('3 is 0 or 3-10 (translated)', $plurals)); + $this->assertTrue(in_array('4 is 0 or 3-10 (translated)', $plurals)); + $this->assertTrue(in_array('5 is 0 or 3-10 (translated)', $plurals)); + $this->assertTrue(in_array('6 is 0 or 3-10 (translated)', $plurals)); + $this->assertTrue(in_array('7 is 0 or 3-10 (translated)', $plurals)); + $this->assertTrue(in_array('8 is 0 or 3-10 (translated)', $plurals)); + $this->assertTrue(in_array('9 is 0 or 3-10 (translated)', $plurals)); + $this->assertTrue(in_array('10 is 0 or 3-10 (translated)', $plurals)); + $this->assertTrue(in_array('11 everything else (translated)', $plurals)); + $this->assertTrue(in_array('12 everything else (translated)', $plurals)); + $this->assertTrue(in_array('13 everything else (translated)', $plurals)); + $this->assertTrue(in_array('14 everything else (translated)', $plurals)); + $this->assertTrue(in_array('15 everything else (translated)', $plurals)); + $this->assertTrue(in_array('16 everything else (translated)', $plurals)); + $this->assertTrue(in_array('17 everything else (translated)', $plurals)); + $this->assertTrue(in_array('18 everything else (translated)', $plurals)); + $this->assertTrue(in_array('19 everything else (translated)', $plurals)); + $this->assertTrue(in_array('20 everything else (translated)', $plurals)); + $this->assertTrue(in_array('21 everything else (translated)', $plurals)); + $this->assertTrue(in_array('22 everything else (translated)', $plurals)); + $this->assertTrue(in_array('23 everything else (translated)', $plurals)); + $this->assertTrue(in_array('24 everything else (translated)', $plurals)); + $this->assertTrue(in_array('25 everything else (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 12 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 is 0 or 3-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 is 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 is 2 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 is 0 or 3-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 is 0 or 3-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 is 0 or 3-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 is 0 or 3-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 is 0 or 3-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 is 0 or 3-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 is 0 or 3-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 is 0 or 3-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals)); + } +/** + * testMoRulesTwelve method + * + * @access public + * @return void + */ + function testMoRulesTwelve() { + Configure::write('Config.language', 'rule_12_mo'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 12 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 is 0 or 3-10 (translated)', $plurals)); + $this->assertTrue(in_array('1 is 1 (translated)', $plurals)); + $this->assertTrue(in_array('2 is 2 (translated)', $plurals)); + $this->assertTrue(in_array('3 is 0 or 3-10 (translated)', $plurals)); + $this->assertTrue(in_array('4 is 0 or 3-10 (translated)', $plurals)); + $this->assertTrue(in_array('5 is 0 or 3-10 (translated)', $plurals)); + $this->assertTrue(in_array('6 is 0 or 3-10 (translated)', $plurals)); + $this->assertTrue(in_array('7 is 0 or 3-10 (translated)', $plurals)); + $this->assertTrue(in_array('8 is 0 or 3-10 (translated)', $plurals)); + $this->assertTrue(in_array('9 is 0 or 3-10 (translated)', $plurals)); + $this->assertTrue(in_array('10 is 0 or 3-10 (translated)', $plurals)); + $this->assertTrue(in_array('11 everything else (translated)', $plurals)); + $this->assertTrue(in_array('12 everything else (translated)', $plurals)); + $this->assertTrue(in_array('13 everything else (translated)', $plurals)); + $this->assertTrue(in_array('14 everything else (translated)', $plurals)); + $this->assertTrue(in_array('15 everything else (translated)', $plurals)); + $this->assertTrue(in_array('16 everything else (translated)', $plurals)); + $this->assertTrue(in_array('17 everything else (translated)', $plurals)); + $this->assertTrue(in_array('18 everything else (translated)', $plurals)); + $this->assertTrue(in_array('19 everything else (translated)', $plurals)); + $this->assertTrue(in_array('20 everything else (translated)', $plurals)); + $this->assertTrue(in_array('21 everything else (translated)', $plurals)); + $this->assertTrue(in_array('22 everything else (translated)', $plurals)); + $this->assertTrue(in_array('23 everything else (translated)', $plurals)); + $this->assertTrue(in_array('24 everything else (translated)', $plurals)); + $this->assertTrue(in_array('25 everything else (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 12 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 is 0 or 3-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 is 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 is 2 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 is 0 or 3-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 is 0 or 3-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 is 0 or 3-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 is 0 or 3-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 is 0 or 3-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 is 0 or 3-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 is 0 or 3-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 is 0 or 3-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals)); + } +/** + * testPoRulesThirteen method + * + * @access public + * @return void + */ + function testPoRulesThirteen() { + Configure::write('Config.language', 'rule_13_po'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 13 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 is 0 or ends in 01-10 (translated)', $plurals)); + $this->assertTrue(in_array('1 is 1 (translated)', $plurals)); + $this->assertTrue(in_array('2 is 0 or ends in 01-10 (translated)', $plurals)); + $this->assertTrue(in_array('3 is 0 or ends in 01-10 (translated)', $plurals)); + $this->assertTrue(in_array('4 is 0 or ends in 01-10 (translated)', $plurals)); + $this->assertTrue(in_array('5 is 0 or ends in 01-10 (translated)', $plurals)); + $this->assertTrue(in_array('6 is 0 or ends in 01-10 (translated)', $plurals)); + $this->assertTrue(in_array('7 is 0 or ends in 01-10 (translated)', $plurals)); + $this->assertTrue(in_array('8 is 0 or ends in 01-10 (translated)', $plurals)); + $this->assertTrue(in_array('9 is 0 or ends in 01-10 (translated)', $plurals)); + $this->assertTrue(in_array('10 is 0 or ends in 01-10 (translated)', $plurals)); + $this->assertTrue(in_array('11 ends in 11-20 (translated)', $plurals)); + $this->assertTrue(in_array('12 ends in 11-20 (translated)', $plurals)); + $this->assertTrue(in_array('13 ends in 11-20 (translated)', $plurals)); + $this->assertTrue(in_array('14 ends in 11-20 (translated)', $plurals)); + $this->assertTrue(in_array('15 ends in 11-20 (translated)', $plurals)); + $this->assertTrue(in_array('16 ends in 11-20 (translated)', $plurals)); + $this->assertTrue(in_array('17 ends in 11-20 (translated)', $plurals)); + $this->assertTrue(in_array('18 ends in 11-20 (translated)', $plurals)); + $this->assertTrue(in_array('19 ends in 11-20 (translated)', $plurals)); + $this->assertTrue(in_array('20 ends in 11-20 (translated)', $plurals)); + $this->assertTrue(in_array('21 everything else (translated)', $plurals)); + $this->assertTrue(in_array('22 everything else (translated)', $plurals)); + $this->assertTrue(in_array('23 everything else (translated)', $plurals)); + $this->assertTrue(in_array('24 everything else (translated)', $plurals)); + $this->assertTrue(in_array('25 everything else (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 13 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 is 0 or ends in 01-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 is 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 is 0 or ends in 01-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 is 0 or ends in 01-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 is 0 or ends in 01-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 is 0 or ends in 01-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 is 0 or ends in 01-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 is 0 or ends in 01-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 is 0 or ends in 01-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 is 0 or ends in 01-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 is 0 or ends in 01-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 ends in 11-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 ends in 11-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 ends in 11-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 ends in 11-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 ends in 11-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 ends in 11-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 ends in 11-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 ends in 11-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 ends in 11-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 ends in 11-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals)); + } +/** + * testMoRulesThirteen method + * + * @access public + * @return void + */ + function testMoRulesThirteen() { + Configure::write('Config.language', 'rule_13_mo'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 13 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 is 0 or ends in 01-10 (translated)', $plurals)); + $this->assertTrue(in_array('1 is 1 (translated)', $plurals)); + $this->assertTrue(in_array('2 is 0 or ends in 01-10 (translated)', $plurals)); + $this->assertTrue(in_array('3 is 0 or ends in 01-10 (translated)', $plurals)); + $this->assertTrue(in_array('4 is 0 or ends in 01-10 (translated)', $plurals)); + $this->assertTrue(in_array('5 is 0 or ends in 01-10 (translated)', $plurals)); + $this->assertTrue(in_array('6 is 0 or ends in 01-10 (translated)', $plurals)); + $this->assertTrue(in_array('7 is 0 or ends in 01-10 (translated)', $plurals)); + $this->assertTrue(in_array('8 is 0 or ends in 01-10 (translated)', $plurals)); + $this->assertTrue(in_array('9 is 0 or ends in 01-10 (translated)', $plurals)); + $this->assertTrue(in_array('10 is 0 or ends in 01-10 (translated)', $plurals)); + $this->assertTrue(in_array('11 ends in 11-20 (translated)', $plurals)); + $this->assertTrue(in_array('12 ends in 11-20 (translated)', $plurals)); + $this->assertTrue(in_array('13 ends in 11-20 (translated)', $plurals)); + $this->assertTrue(in_array('14 ends in 11-20 (translated)', $plurals)); + $this->assertTrue(in_array('15 ends in 11-20 (translated)', $plurals)); + $this->assertTrue(in_array('16 ends in 11-20 (translated)', $plurals)); + $this->assertTrue(in_array('17 ends in 11-20 (translated)', $plurals)); + $this->assertTrue(in_array('18 ends in 11-20 (translated)', $plurals)); + $this->assertTrue(in_array('19 ends in 11-20 (translated)', $plurals)); + $this->assertTrue(in_array('20 ends in 11-20 (translated)', $plurals)); + $this->assertTrue(in_array('21 everything else (translated)', $plurals)); + $this->assertTrue(in_array('22 everything else (translated)', $plurals)); + $this->assertTrue(in_array('23 everything else (translated)', $plurals)); + $this->assertTrue(in_array('24 everything else (translated)', $plurals)); + $this->assertTrue(in_array('25 everything else (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 13 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 is 0 or ends in 01-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 is 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 is 0 or ends in 01-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 is 0 or ends in 01-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 is 0 or ends in 01-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 is 0 or ends in 01-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 is 0 or ends in 01-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 is 0 or ends in 01-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 is 0 or ends in 01-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 is 0 or ends in 01-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 is 0 or ends in 01-10 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 ends in 11-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 ends in 11-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 ends in 11-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 ends in 11-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 ends in 11-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 ends in 11-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 ends in 11-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 ends in 11-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 ends in 11-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 ends in 11-20 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals)); + } +/** + * testPoRulesFourteen method + * + * @access public + * @return void + */ + function testPoRulesFourteen() { + Configure::write('Config.language', 'rule_14_po'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 14 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 everything else (translated)', $plurals)); + $this->assertTrue(in_array('1 ends in 1 (translated)', $plurals)); + $this->assertTrue(in_array('2 ends in 2 (translated)', $plurals)); + $this->assertTrue(in_array('3 everything else (translated)', $plurals)); + $this->assertTrue(in_array('4 everything else (translated)', $plurals)); + $this->assertTrue(in_array('5 everything else (translated)', $plurals)); + $this->assertTrue(in_array('6 everything else (translated)', $plurals)); + $this->assertTrue(in_array('7 everything else (translated)', $plurals)); + $this->assertTrue(in_array('8 everything else (translated)', $plurals)); + $this->assertTrue(in_array('9 everything else (translated)', $plurals)); + $this->assertTrue(in_array('10 everything else (translated)', $plurals)); + $this->assertTrue(in_array('11 ends in 1 (translated)', $plurals)); + $this->assertTrue(in_array('12 ends in 2 (translated)', $plurals)); + $this->assertTrue(in_array('13 everything else (translated)', $plurals)); + $this->assertTrue(in_array('14 everything else (translated)', $plurals)); + $this->assertTrue(in_array('15 everything else (translated)', $plurals)); + $this->assertTrue(in_array('16 everything else (translated)', $plurals)); + $this->assertTrue(in_array('17 everything else (translated)', $plurals)); + $this->assertTrue(in_array('18 everything else (translated)', $plurals)); + $this->assertTrue(in_array('19 everything else (translated)', $plurals)); + $this->assertTrue(in_array('20 everything else (translated)', $plurals)); + $this->assertTrue(in_array('21 ends in 1 (translated)', $plurals)); + $this->assertTrue(in_array('22 ends in 2 (translated)', $plurals)); + $this->assertTrue(in_array('23 everything else (translated)', $plurals)); + $this->assertTrue(in_array('24 everything else (translated)', $plurals)); + $this->assertTrue(in_array('25 everything else (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 14 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 ends in 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 ends in 2 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 ends in 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 ends in 2 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 ends in 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 ends in 2 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals)); + } +/** + * testMoRulesFourteen method + * + * @access public + * @return void + */ + function testMoRulesFourteen() { + Configure::write('Config.language', 'rule_14_mo'); + + $singular = $this->__singular(); + $this->assertEqual('Plural Rule 14 (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 everything else (translated)', $plurals)); + $this->assertTrue(in_array('1 ends in 1 (translated)', $plurals)); + $this->assertTrue(in_array('2 ends in 2 (translated)', $plurals)); + $this->assertTrue(in_array('3 everything else (translated)', $plurals)); + $this->assertTrue(in_array('4 everything else (translated)', $plurals)); + $this->assertTrue(in_array('5 everything else (translated)', $plurals)); + $this->assertTrue(in_array('6 everything else (translated)', $plurals)); + $this->assertTrue(in_array('7 everything else (translated)', $plurals)); + $this->assertTrue(in_array('8 everything else (translated)', $plurals)); + $this->assertTrue(in_array('9 everything else (translated)', $plurals)); + $this->assertTrue(in_array('10 everything else (translated)', $plurals)); + $this->assertTrue(in_array('11 ends in 1 (translated)', $plurals)); + $this->assertTrue(in_array('12 ends in 2 (translated)', $plurals)); + $this->assertTrue(in_array('13 everything else (translated)', $plurals)); + $this->assertTrue(in_array('14 everything else (translated)', $plurals)); + $this->assertTrue(in_array('15 everything else (translated)', $plurals)); + $this->assertTrue(in_array('16 everything else (translated)', $plurals)); + $this->assertTrue(in_array('17 everything else (translated)', $plurals)); + $this->assertTrue(in_array('18 everything else (translated)', $plurals)); + $this->assertTrue(in_array('19 everything else (translated)', $plurals)); + $this->assertTrue(in_array('20 everything else (translated)', $plurals)); + $this->assertTrue(in_array('21 ends in 1 (translated)', $plurals)); + $this->assertTrue(in_array('22 ends in 2 (translated)', $plurals)); + $this->assertTrue(in_array('23 everything else (translated)', $plurals)); + $this->assertTrue(in_array('24 everything else (translated)', $plurals)); + $this->assertTrue(in_array('25 everything else (translated)', $plurals)); + + $coreSingular = $this->__singularFromCore(); + $this->assertEqual('Plural Rule 14 (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('1 ends in 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('2 ends in 2 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('3 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('4 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('11 ends in 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('12 ends in 2 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('21 ends in 1 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('22 ends in 2 (from core translated)', $corePlurals)); + $this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals)); + $this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals)); + } +/** + * testSetLanguageWithSession method + * + * @access public + * @return void + */ + function testSetLanguageWithSession () { + $_SESSION['Config']['language'] = 'po'; + $singular = $this->__singular(); + $this->assertEqual('Po (translated)', $singular); + + $plurals = $this->__plural(); + $this->assertTrue(in_array('0 everything else (po translated)', $plurals)); + $this->assertTrue(in_array('1 is 1 (po translated)', $plurals)); + $this->assertTrue(in_array('2 is 2-4 (po translated)', $plurals)); + $this->assertTrue(in_array('3 is 2-4 (po translated)', $plurals)); + $this->assertTrue(in_array('4 is 2-4 (po translated)', $plurals)); + $this->assertTrue(in_array('5 everything else (po translated)', $plurals)); + $this->assertTrue(in_array('6 everything else (po translated)', $plurals)); + $this->assertTrue(in_array('7 everything else (po translated)', $plurals)); + $this->assertTrue(in_array('8 everything else (po translated)', $plurals)); + $this->assertTrue(in_array('9 everything else (po translated)', $plurals)); + $this->assertTrue(in_array('10 everything else (po translated)', $plurals)); + $this->assertTrue(in_array('11 everything else (po translated)', $plurals)); + $this->assertTrue(in_array('12 everything else (po translated)', $plurals)); + $this->assertTrue(in_array('13 everything else (po translated)', $plurals)); + $this->assertTrue(in_array('14 everything else (po translated)', $plurals)); + $this->assertTrue(in_array('15 everything else (po translated)', $plurals)); + $this->assertTrue(in_array('16 everything else (po translated)', $plurals)); + $this->assertTrue(in_array('17 everything else (po translated)', $plurals)); + $this->assertTrue(in_array('18 everything else (po translated)', $plurals)); + $this->assertTrue(in_array('19 everything else (po translated)', $plurals)); + $this->assertTrue(in_array('20 everything else (po translated)', $plurals)); + $this->assertTrue(in_array('21 everything else (po translated)', $plurals)); + $this->assertTrue(in_array('22 everything else (po translated)', $plurals)); + $this->assertTrue(in_array('23 everything else (po translated)', $plurals)); + $this->assertTrue(in_array('24 everything else (po translated)', $plurals)); + $this->assertTrue(in_array('25 everything else (po translated)', $plurals)); + unset($_SESSION['Config']['language']); + } +/** + * testNoCoreTranslation method + * + * @access public + * @return void + */ + function testNoCoreTranslation () { + Configure::write('Config.language', 'po'); + $singular = $this->__singular(); + $this->assertEqual('Po (translated)', $singular); + + $coreSingular = $this->__singularFromCore(); + $this->assertNotEqual('Po (from core translated)', $coreSingular); + + $corePlurals = $this->__pluralFromCore(); + $this->assertFalse(in_array('0 everything else (from core translated)', $corePlurals)); + $this->assertFalse(in_array('1 is 1 (from core translated)', $corePlurals)); + $this->assertFalse(in_array('2 is 2-4 (from core translated)', $corePlurals)); + $this->assertFalse(in_array('3 is 2-4 (from core translated)', $corePlurals)); + $this->assertFalse(in_array('4 is 2-4 (from core translated)', $corePlurals)); + $this->assertFalse(in_array('5 everything else (from core translated)', $corePlurals)); + $this->assertFalse(in_array('6 everything else (from core translated)', $corePlurals)); + $this->assertFalse(in_array('7 everything else (from core translated)', $corePlurals)); + $this->assertFalse(in_array('8 everything else (from core translated)', $corePlurals)); + $this->assertFalse(in_array('9 everything else (from core translated)', $corePlurals)); + $this->assertFalse(in_array('10 everything else (from core translated)', $corePlurals)); + $this->assertFalse(in_array('11 everything else (from core translated)', $corePlurals)); + $this->assertFalse(in_array('12 everything else (from core translated)', $corePlurals)); + $this->assertFalse(in_array('13 everything else (from core translated)', $corePlurals)); + $this->assertFalse(in_array('14 everything else (from core translated)', $corePlurals)); + $this->assertFalse(in_array('15 everything else (from core translated)', $corePlurals)); + $this->assertFalse(in_array('16 everything else (from core translated)', $corePlurals)); + $this->assertFalse(in_array('17 everything else (from core translated)', $corePlurals)); + $this->assertFalse(in_array('18 everything else (from core translated)', $corePlurals)); + $this->assertFalse(in_array('19 everything else (from core translated)', $corePlurals)); + $this->assertFalse(in_array('20 everything else (from core translated)', $corePlurals)); + $this->assertFalse(in_array('21 everything else (from core translated)', $corePlurals)); + $this->assertFalse(in_array('22 everything else (from core translated)', $corePlurals)); + $this->assertFalse(in_array('23 everything else (from core translated)', $corePlurals)); + $this->assertFalse(in_array('24 everything else (from core translated)', $corePlurals)); + $this->assertFalse(in_array('25 everything else (from core translated)', $corePlurals)); + } +/** + * testPluginTranslation method + * + * @access public + * @return void + */ + function testPluginTranslation() { + Configure::write('Config.language', 'po'); + $singular = $this->__domainSingular(); + $this->assertEqual('Plural Rule 1 (from plugin)', $singular); + + $plurals = $this->__domainPlural(); + $this->assertTrue(in_array('0 = 0 or > 1 (from plugin)', $plurals)); + $this->assertTrue(in_array('1 = 1 (from plugin)', $plurals)); + $this->assertTrue(in_array('2 = 0 or > 1 (from plugin)', $plurals)); + $this->assertTrue(in_array('3 = 0 or > 1 (from plugin)', $plurals)); + $this->assertTrue(in_array('4 = 0 or > 1 (from plugin)', $plurals)); + $this->assertTrue(in_array('5 = 0 or > 1 (from plugin)', $plurals)); + $this->assertTrue(in_array('6 = 0 or > 1 (from plugin)', $plurals)); + $this->assertTrue(in_array('7 = 0 or > 1 (from plugin)', $plurals)); + $this->assertTrue(in_array('8 = 0 or > 1 (from plugin)', $plurals)); + $this->assertTrue(in_array('9 = 0 or > 1 (from plugin)', $plurals)); + $this->assertTrue(in_array('10 = 0 or > 1 (from plugin)', $plurals)); + $this->assertTrue(in_array('11 = 0 or > 1 (from plugin)', $plurals)); + $this->assertTrue(in_array('12 = 0 or > 1 (from plugin)', $plurals)); + $this->assertTrue(in_array('13 = 0 or > 1 (from plugin)', $plurals)); + $this->assertTrue(in_array('14 = 0 or > 1 (from plugin)', $plurals)); + $this->assertTrue(in_array('15 = 0 or > 1 (from plugin)', $plurals)); + $this->assertTrue(in_array('16 = 0 or > 1 (from plugin)', $plurals)); + $this->assertTrue(in_array('17 = 0 or > 1 (from plugin)', $plurals)); + $this->assertTrue(in_array('18 = 0 or > 1 (from plugin)', $plurals)); + $this->assertTrue(in_array('19 = 0 or > 1 (from plugin)', $plurals)); + $this->assertTrue(in_array('20 = 0 or > 1 (from plugin)', $plurals)); + $this->assertTrue(in_array('21 = 0 or > 1 (from plugin)', $plurals)); + $this->assertTrue(in_array('22 = 0 or > 1 (from plugin)', $plurals)); + $this->assertTrue(in_array('23 = 0 or > 1 (from plugin)', $plurals)); + $this->assertTrue(in_array('24 = 0 or > 1 (from plugin)', $plurals)); + $this->assertTrue(in_array('25 = 0 or > 1 (from plugin)', $plurals)); + } +/** + * testPoMultipleLineTranslation method + * + * @access public + * @return void + */ + function testPoMultipleLineTranslation () { + Configure::write('Config.language', 'po'); + + $string = "This is a multiline translation\n"; + $string .= "broken up over multiple lines.\n"; + $string .= "This is the third line.\n"; + $string .= "This is the forth line."; + $result = __($string, true); + + $expected = "This is a multiline translation\n"; + $expected .= "broken up over multiple lines.\n"; + $expected .= "This is the third line.\n"; + $expected .= "This is the forth line. (translated)"; + $this->assertEqual($result, $expected); + + // Windows Newline is \r\n + $string = "This is a multiline translation\r\n"; + $string .= "broken up over multiple lines.\r\n"; + $string .= "This is the third line.\r\n"; + $string .= "This is the forth line."; + $result = __($string, true); + $this->assertEqual($result, $expected); + + $singular = "valid\nsecond line"; + $plural = "valids\nsecond line"; + + $result = __n($singular, $plural, 1, true); + $expected = "v\nsecond line"; + $this->assertEqual($result, $expected); + + $result = __n($singular, $plural, 2, true); + $expected = "vs\nsecond line"; + $this->assertEqual($result, $expected); + + $string = "This is a multiline translation\n"; + $string .= "broken up over multiple lines.\n"; + $string .= "This is the third line.\n"; + $string .= "This is the forth line."; + + $singular = "%d = 1\n" . $string; + $plural = "%d = 0 or > 1\n" . $string; + + $result = __n($singular, $plural, 1, true); + $expected = "%d is 1\n" . $string; + $this->assertEqual($result, $expected); + + $result = __n($singular, $plural, 2, true); + $expected = "%d is 2-4\n" . $string; + $this->assertEqual($result, $expected); + + // Windows Newline is \r\n + $string = "This is a multiline translation\r\n"; + $string .= "broken up over multiple lines.\r\n"; + $string .= "This is the third line.\r\n"; + $string .= "This is the forth line."; + + $singular = "%d = 1\r\n" . $string; + $plural = "%d = 0 or > 1\r\n" . $string; + + $result = __n($singular, $plural, 1, true); + $expected = "%d is 1\n" . str_replace("\r\n", "\n", $string); + $this->assertEqual($result, $expected); + + $result = __n($singular, $plural, 2, true); + $expected = "%d is 2-4\n" . str_replace("\r\n", "\n", $string); + $this->assertEqual($result, $expected); + } +/** + * testPoNoTranslationNeeded method + * + * @access public + * @return void + */ + function testPoNoTranslationNeeded () { + Configure::write('Config.language', 'po'); + $result = __('No Translation needed', true); + $this->assertEqual($result, 'No Translation needed'); + } +/** + * testPoQuotedString method + * + * @access public + * @return void + */ + function testPoQuotedString () { + $expected = 'this is a "quoted string" (translated)'; + $this->assertEqual(__('this is a "quoted string"', true), $expected); + } +/** + * testFloatValue method + * + * @access public + * @return void + */ + function testFloatValue() { + Configure::write('Config.language', 'rule_9_po'); + + $result = __n('%d = 1', '%d = 0 or > 1', (float)1, true); + $expected = '%d is 1 (translated)'; + $this->assertEqual($result, $expected); + + $result = __n('%d = 1', '%d = 0 or > 1', (float)2, true); + $expected = "%d ends in 2-4, not 12-14 (translated)"; + $this->assertEqual($result, $expected); + + $result = __n('%d = 1', '%d = 0 or > 1', (float)5, true); + $expected = "%d everything else (translated)"; + $this->assertEqual($result, $expected); + } +/** + * testCategory method + * + * @access public + * @return void + */ + function testCategory() { + Configure::write('Config.language', 'po'); + $category = $this->__category(); + $this->assertEqual('Monetary Po (translated)', $category); + } +/** + * testPluginCategory method + * + * @access public + * @return void + */ + function testPluginCategory() { + Configure::write('Config.language', 'po'); + + $singular = $this->__domainCategorySingular(); + $this->assertEqual('Monetary Plural Rule 1 (from plugin)', $singular); + + $plurals = $this->__domainCategoryPlural(); + $this->assertTrue(in_array('Monetary 0 = 0 or > 1 (from plugin)', $plurals)); + $this->assertTrue(in_array('Monetary 1 = 1 (from plugin)', $plurals)); + } +/** + * testCategoryThenSingular method + * + * @access public + * @return void + */ + function testCategoryThenSingular() { + Configure::write('Config.language', 'po'); + $category = $this->__category(); + $this->assertEqual('Monetary Po (translated)', $category); + + $singular = $this->__singular(); + $this->assertEqual('Po (translated)', $singular); + } +/** + * Singular method + * + * @access private + * @return void + */ + function __domainCategorySingular($domain = 'test_plugin', $category = LC_MONETARY) { + $singular = __dc($domain, 'Plural Rule 1', $category, true); + return $singular; + } +/** + * Plural method + * + * @access private + * @return void + */ + function __domainCategoryPlural($domain = 'test_plugin', $category = LC_MONETARY) { + $plurals = array(); + for ($number = 0; $number <= 25; $number++) { + $plurals[] = sprintf(__dcn($domain, '%d = 1', '%d = 0 or > 1', (float)$number, $category, true), (float)$number); + } + return $plurals; + } +/** + * Singular method + * + * @access private + * @return void + */ + function __domainSingular($domain = 'test_plugin') { + $singular = __d($domain, 'Plural Rule 1', true); + return $singular; + } +/** + * Plural method + * + * @access private + * @return void + */ + function __domainPlural($domain = 'test_plugin') { + $plurals = array(); + for ($number = 0; $number <= 25; $number++) { + $plurals[] = sprintf(__dn($domain, '%d = 1', '%d = 0 or > 1', (float)$number, true), (float)$number ); + } + return $plurals; + } +/** + * category method + * + * @access private + * @return void + */ + function __category($category = LC_MONETARY) { + $singular = __c('Plural Rule 1', $category, true); + return $singular; + } +/** + * Singular method + * + * @access private + * @return void + */ + function __singular() { + $singular = __('Plural Rule 1', true); + return $singular; + } +/** + * Plural method + * + * @access private + * @return void + */ + function __plural() { + $plurals = array(); + for ($number = 0; $number <= 25; $number++) { + $plurals[] = sprintf(__n('%d = 1', '%d = 0 or > 1', (float)$number, true), (float)$number ); + } + return $plurals; + } +/** + * singularFromCore method + * + * @access private + * @return void + */ + function __singularFromCore() { + $singular = __('Plural Rule 1 (from core)', true); + return $singular; + } +/** + * pluralFromCore method + * + * @access private + * @return void + */ + function __pluralFromCore() { + $plurals = array(); + for ($number = 0; $number <= 25; $number++) { + $plurals[] = sprintf(__n('%d = 1 (from core)', '%d = 0 or > 1 (from core)', (float)$number, true), (float)$number ); + } + return $plurals; + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/inflector.test.php b/cake/tests/cases/libs/inflector.test.php new file mode 100755 index 0000000..2c293bb --- /dev/null +++ b/cake/tests/cases/libs/inflector.test.php @@ -0,0 +1,255 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * InflectorTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs + * @since CakePHP(tm) v 1.2.0.4206 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +App::import('Core', 'Inflector'); +/** + * InflectorTest class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class InflectorTest extends CakeTestCase { +/** + * setUp method + * + * @access public + * @return void + */ + function setUp() { + } +/** + * tearDown method + * + * @access public + * @return void + */ + function tearDown() { + } +/** + * testInstantiation method + * + * @access public + * @return void + */ + function testInstantiation() { + $this->skipUnless(strpos(Debugger::trace(), 'GroupTest') === false, '%s Cannot be run from within a group test'); + + $Instance = Inflector::getInstance(); + $this->assertEqual(new Inflector(), $Instance); + } +/** + * testInflectingSingulars method + * + * @access public + * @return void + */ + function testInflectingSingulars() { + $this->assertEqual(Inflector::singularize('categorias'), 'categoria'); + $this->assertEqual(Inflector::singularize('menus'), 'menu'); + $this->assertEqual(Inflector::singularize('news'), 'news'); + $this->assertEqual(Inflector::singularize('food_menus'), 'food_menu'); + $this->assertEqual(Inflector::singularize('Menus'), 'Menu'); + $this->assertEqual(Inflector::singularize('FoodMenus'), 'FoodMenu'); + $this->assertEqual(Inflector::singularize('houses'), 'house'); + $this->assertEqual(Inflector::singularize('powerhouses'), 'powerhouse'); + $this->assertEqual(Inflector::singularize('quizzes'), 'quiz'); + $this->assertEqual(Inflector::singularize('Buses'), 'Bus'); + $this->assertEqual(Inflector::singularize('buses'), 'bus'); + $this->assertEqual(Inflector::singularize('matrix_rows'), 'matrix_row'); + $this->assertEqual(Inflector::singularize('matrices'), 'matrix'); + $this->assertEqual(Inflector::singularize('vertices'), 'vertex'); + $this->assertEqual(Inflector::singularize('indices'), 'index'); + $this->assertEqual(Inflector::singularize('Aliases'), 'Alias'); + $this->assertEqual(Inflector::singularize('Alias'), 'Alias'); + $this->assertEqual(Inflector::singularize('Media'), 'Media'); + $this->assertEqual(Inflector::singularize('alumni'), 'alumnus'); + $this->assertEqual(Inflector::singularize('bacilli'), 'bacillus'); + $this->assertEqual(Inflector::singularize('cacti'), 'cactus'); + $this->assertEqual(Inflector::singularize('foci'), 'focus'); + $this->assertEqual(Inflector::singularize('fungi'), 'fungus'); + $this->assertEqual(Inflector::singularize('nuclei'), 'nucleus'); + $this->assertEqual(Inflector::singularize('octopuses'), 'octopus'); + $this->assertEqual(Inflector::singularize('radii'), 'radius'); + $this->assertEqual(Inflector::singularize('stimuli'), 'stimulus'); + $this->assertEqual(Inflector::singularize('syllabi'), 'syllabus'); + $this->assertEqual(Inflector::singularize('termini'), 'terminus'); + $this->assertEqual(Inflector::singularize('viri'), 'virus'); + $this->assertEqual(Inflector::singularize('people'), 'person'); + $this->assertEqual(Inflector::singularize('gloves'), 'glove'); + $this->assertEqual(Inflector::singularize('doves'), 'dove'); + $this->assertEqual(Inflector::singularize('lives'), 'life'); + $this->assertEqual(Inflector::singularize('knives'), 'knife'); + $this->assertEqual(Inflector::singularize('wolves'), 'wolf'); + $this->assertEqual(Inflector::singularize('shelves'), 'shelf'); + $this->assertEqual(Inflector::singularize('taxis'), 'taxi'); + $this->assertEqual(Inflector::singularize('taxes'), 'tax'); + $this->assertEqual(Inflector::singularize('faxes'), 'fax'); + $this->assertEqual(Inflector::singularize('waxes'), 'wax'); + $this->assertEqual(Inflector::singularize('waves'), 'wave'); + $this->assertEqual(Inflector::singularize(''), ''); + } +/** + * testInflectingPlurals method + * + * @access public + * @return void + */ + function testInflectingPlurals() { + $this->assertEqual(Inflector::pluralize('categoria'), 'categorias'); + $this->assertEqual(Inflector::pluralize('house'), 'houses'); + $this->assertEqual(Inflector::pluralize('powerhouse'), 'powerhouses'); + $this->assertEqual(Inflector::pluralize('Bus'), 'Buses'); + $this->assertEqual(Inflector::pluralize('bus'), 'buses'); + $this->assertEqual(Inflector::pluralize('menu'), 'menus'); + $this->assertEqual(Inflector::pluralize('news'), 'news'); + $this->assertEqual(Inflector::pluralize('food_menu'), 'food_menus'); + $this->assertEqual(Inflector::pluralize('Menu'), 'Menus'); + $this->assertEqual(Inflector::pluralize('FoodMenu'), 'FoodMenus'); + $this->assertEqual(Inflector::pluralize('quiz'), 'quizzes'); + $this->assertEqual(Inflector::pluralize('matrix_row'), 'matrix_rows'); + $this->assertEqual(Inflector::pluralize('matrix'), 'matrices'); + $this->assertEqual(Inflector::pluralize('vertex'), 'vertices'); + $this->assertEqual(Inflector::pluralize('index'), 'indices'); + $this->assertEqual(Inflector::pluralize('Alias'), 'Aliases'); + $this->assertEqual(Inflector::pluralize('Aliases'), 'Aliases'); + $this->assertEqual(Inflector::pluralize('Media'), 'Media'); + $this->assertEqual(Inflector::pluralize('alumnus'), 'alumni'); + $this->assertEqual(Inflector::pluralize('bacillus'), 'bacilli'); + $this->assertEqual(Inflector::pluralize('cactus'), 'cacti'); + $this->assertEqual(Inflector::pluralize('focus'), 'foci'); + $this->assertEqual(Inflector::pluralize('fungus'), 'fungi'); + $this->assertEqual(Inflector::pluralize('nucleus'), 'nuclei'); + $this->assertEqual(Inflector::pluralize('octopus'), 'octopuses'); + $this->assertEqual(Inflector::pluralize('radius'), 'radii'); + $this->assertEqual(Inflector::pluralize('stimulus'), 'stimuli'); + $this->assertEqual(Inflector::pluralize('syllabus'), 'syllabi'); + $this->assertEqual(Inflector::pluralize('terminus'), 'termini'); + $this->assertEqual(Inflector::pluralize('virus'), 'viri'); + $this->assertEqual(Inflector::pluralize('person'), 'people'); + $this->assertEqual(Inflector::pluralize('people'), 'people'); + $this->assertEqual(Inflector::pluralize('glove'), 'gloves'); + $this->assertEqual(Inflector::pluralize('crisis'), 'crises'); + $this->assertEqual(Inflector::pluralize('wave'), 'waves'); + $this->assertEqual(Inflector::pluralize(''), ''); + } +/** + * testInflectorSlug method + * + * @access public + * @return void + */ + function testInflectorSlug() { + $result = Inflector::slug('Foo Bar: Not just for breakfast any-more'); + $expected = 'Foo_Bar_Not_just_for_breakfast_any_more'; + $this->assertEqual($result, $expected); + + $result = Inflector::slug('this/is/a/path'); + $expected = 'this_is_a_path'; + $this->assertEqual($result, $expected); + + $result = Inflector::slug('Foo Bar: Not just for breakfast any-more', "-"); + $expected = 'Foo-Bar-Not-just-for-breakfast-any-more'; + $this->assertEqual($result, $expected); + + $result = Inflector::slug('Foo Bar: Not just for breakfast any-more', "+"); + $expected = 'Foo+Bar+Not+just+for+breakfast+any+more'; + $this->assertEqual($result, $expected); + + $result = Inflector::slug('Äpfel Über Öl grün ärgert groß öko', '-'); + $expected = 'Aepfel-Ueber-Oel-gruen-aergert-gross-oeko'; + $this->assertEqual($result, $expected); + + $result = Inflector::slug('The truth - and- more- news', '-'); + $expected = 'The-truth-and-more-news'; + $this->assertEqual($result, $expected); + + $result = Inflector::slug('The truth: and more news', '-'); + $expected = 'The-truth-and-more-news'; + $this->assertEqual($result, $expected); + + $result = Inflector::slug('La langue française est un attribut de souveraineté en France', '-'); + $expected = 'La-langue-francaise-est-un-attribut-de-souverainete-en-France'; + $this->assertEqual($result, $expected); + + $result = Inflector::slug('!@$#exciting stuff! - what !@-# was that?', '-'); + $expected = 'exciting-stuff-what-was-that'; + $this->assertEqual($result, $expected); + + $result = Inflector::slug('20% of profits went to me!', '-'); + $expected = '20-of-profits-went-to-me'; + $this->assertEqual($result, $expected); + + $result = Inflector::slug('#this melts your face1#2#3', '-'); + $expected = 'this-melts-your-face1-2-3'; + $this->assertEqual($result, $expected); + } +/** + * testVariableNaming method + * + * @access public + * @return void + */ + function testVariableNaming() { + $this->assertEqual(Inflector::variable('test_field'), 'testField'); + $this->assertEqual(Inflector::variable('test_fieLd'), 'testFieLd'); + $this->assertEqual(Inflector::variable('test field'), 'testField'); + $this->assertEqual(Inflector::variable('Test_field'), 'testField'); + } +/** + * testClassNaming method + * + * @access public + * @return void + */ + function testClassNaming() { + $this->assertEqual(Inflector::classify('artists_genres'), 'ArtistsGenre'); + $this->assertEqual(Inflector::classify('file_systems'), 'FileSystem'); + $this->assertEqual(Inflector::classify('news'), 'News'); + } +/** + * testTableNaming method + * + * @access public + * @return void + */ + function testTableNaming() { + $this->assertEqual(Inflector::tableize('ArtistsGenre'), 'artists_genres'); + $this->assertEqual(Inflector::tableize('FileSystem'), 'file_systems'); + $this->assertEqual(Inflector::tableize('News'), 'news'); + } +/** + * testHumanization method + * + * @access public + * @return void + */ + function testHumanization() { + $this->assertEqual(Inflector::humanize('posts'), 'Posts'); + $this->assertEqual(Inflector::humanize('posts_tags'), 'Posts Tags'); + $this->assertEqual(Inflector::humanize('file_systems'), 'File Systems'); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/l10n.test.php b/cake/tests/cases/libs/l10n.test.php new file mode 100755 index 0000000..48df8fa --- /dev/null +++ b/cake/tests/cases/libs/l10n.test.php @@ -0,0 +1,980 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * L10nTest file + * + * Long description for file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) Tests <https://trac.cakephp.org/wiki/Developement/TestSuite> + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The Open Group Test Suite License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.libs + * @since CakePHP(tm) v 1.2.0.5432 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + */ +App::import('Core', 'l10n'); +/** + * L10nTest class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class L10nTest extends CakeTestCase { +/** + * testGet method + * + * @access public + * @return void + */ + function testGet() { + $l10n =& new L10n(); + + // Catalog Entry + $l10n->get('en'); + $result = $l10n->language; + $expected = 'English'; + $this->assertEqual($result, $expected); + + $result = $l10n->languagePath; + $expected = array('eng', 'eng'); + $this->assertEqual($result, $expected); + + $result = $l10n->locale; + $expected = 'eng'; + $this->assertEqual($result, $expected); + + // Map Entry + $l10n->get('eng'); + $result = $l10n->language; + $expected = 'English'; + $this->assertEqual($result, $expected); + + $result = $l10n->languagePath; + $expected = array('eng', 'eng'); + $this->assertEqual($result, $expected); + + $result = $l10n->locale; + $expected = 'eng'; + $this->assertEqual($result, $expected); + + // Catalog Entry + $l10n->get('en-ca'); + $result = $l10n->language; + $expected = 'English (Canadian)'; + $this->assertEqual($result, $expected); + + $result = $l10n->languagePath; + $expected = array('en_ca', 'eng'); + $this->assertEqual($result, $expected); + + $result = $l10n->locale; + $expected = 'en_ca'; + $this->assertEqual($result, $expected); + + // Default Entry + define('DEFAULT_LANGUAGE', 'en-us'); + + $l10n->get('use_default'); + $result = $l10n->language; + $expected = 'English (United States)'; + $this->assertEqual($result, $expected); + + $result = $l10n->languagePath; + $expected = array('en_us', 'eng'); + $this->assertEqual($result, $expected); + + $result = $l10n->locale; + $expected = 'en_us'; + $this->assertEqual($result, $expected); + + // Using $this->default + $l10n = new L10n(); + $l10n->get('use_default'); + $result = $l10n->language; + $expected = 'English (United States)'; + $this->assertEqual($result, $expected); + + $result = $l10n->languagePath; + $expected = array('en_us', 'eng', 'eng'); + $this->assertEqual($result, $expected); + + $result = $l10n->locale; + $expected = 'en_us'; + $this->assertEqual($result, $expected); + } +/** + * testGetAutoLanguage method + * + * @access public + * @return void + */ + function testGetAutoLanguage() { + $__SERVER = $_SERVER; + $_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'inexistent,en-ca'; + + $l10n =& new L10n(); + $l10n->get(); + $result = $l10n->language; + $expected = 'English (Canadian)'; + $this->assertEqual($result, $expected); + + $result = $l10n->languagePath; + $expected = array('en_ca', 'eng', 'eng'); + $this->assertEqual($result, $expected); + + $result = $l10n->locale; + $expected = 'en_ca'; + $this->assertEqual($result, $expected); + + $_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'es_mx'; + $l10n->get(); + $result = $l10n->language; + $expected = 'Spanish (Mexican)'; + $this->assertEqual($result, $expected); + + $result = $l10n->languagePath; + $expected = array('es_mx', 'spa', 'eng'); + $this->assertEqual($result, $expected); + + $result = $l10n->locale; + $expected = 'es_mx'; + $this->assertEqual($result, $expected); + + $_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'en_xy,en_ca'; + $l10n->get(); + $result = $l10n->language; + $expected = 'English'; + $this->assertEqual($result, $expected); + + $result = $l10n->languagePath; + $expected = array('eng', 'eng', 'eng'); + $this->assertEqual($result, $expected); + + $result = $l10n->locale; + $expected = 'eng'; + $this->assertEqual($result, $expected); + + $_SERVER = $__SERVER; + } +/** + * testMap method + * + * @access public + * @return void + */ + function testMap() { + $l10n =& new L10n(); + + $result = $l10n->map(array('afr', 'af')); + $expected = array('afr' => 'af', 'af' => 'afr'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('alb', 'sq')); + $expected = array('alb' => 'sq', 'sq' => 'alb'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('ara', 'ar')); + $expected = array('ara' => 'ar', 'ar' => 'ara'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('hye', 'hy')); + $expected = array('hye' => 'hy', 'hy' => 'hye'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('baq', 'eu')); + $expected = array('baq' => 'eu', 'eu' => 'baq'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('baq', 'eu')); + $expected = array('baq' => 'eu', 'eu' => 'baq'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('bos', 'bs')); + $expected = array('bos' => 'bs', 'bs' => 'bos'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('bul', 'bg')); + $expected = array('bul' => 'bg', 'bg' => 'bul'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('bel', 'be')); + $expected = array('bel' => 'be', 'be' => 'bel'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('cat', 'ca')); + $expected = array('cat' => 'ca', 'ca' => 'cat'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('chi', 'zh')); + $expected = array('chi' => 'zh', 'zh' => 'chi'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('zho', 'zh')); + $expected = array('zho' => 'zh', 'zh' => 'chi'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('hrv', 'hr')); + $expected = array('hrv' => 'hr', 'hr' => 'hrv'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('ces', 'cs')); + $expected = array('ces' => 'cs', 'cs' => 'cze'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('cze', 'cs')); + $expected = array('cze' => 'cs', 'cs' => 'cze'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('dan', 'da')); + $expected = array('dan' => 'da', 'da' => 'dan'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('dut', 'nl')); + $expected = array('dut' => 'nl', 'nl' => 'dut'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('nld', 'nl')); + $expected = array('nld' => 'nl', 'nl' => 'dut'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('eng', 'en')); + $expected = array('eng' => 'en', 'en' => 'eng'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('est', 'et')); + $expected = array('est' => 'et', 'et' => 'est'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('fao', 'fo')); + $expected = array('fao' => 'fo', 'fo' => 'fao'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('fas', 'fa')); + $expected = array('fas' => 'fa', 'fa' => 'fas'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('per', 'fa')); + $expected = array('per' => 'fa', 'fa' => 'fas'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('fin', 'fi')); + $expected = array('fin' => 'fi', 'fi' => 'fin'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('fra', 'fr')); + $expected = array('fra' => 'fr', 'fr' => 'fre'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('fre', 'fr')); + $expected = array('fre' => 'fr', 'fr' => 'fre'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('gla', 'gd')); + $expected = array('gla' => 'gd', 'gd' => 'gla'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('glg', 'gl')); + $expected = array('glg' => 'gl', 'gl' => 'glg'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('deu', 'de')); + $expected = array('deu' => 'de', 'de' => 'deu'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('ger', 'de')); + $expected = array('ger' => 'de', 'de' => 'deu'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('ell', 'el')); + $expected = array('ell' => 'el', 'el' => 'gre'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('gre', 'el')); + $expected = array('gre' => 'el', 'el' => 'gre'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('heb', 'he')); + $expected = array('heb' => 'he', 'he' => 'heb'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('hin', 'hi')); + $expected = array('hin' => 'hi', 'hi' => 'hin'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('hun', 'hu')); + $expected = array('hun' => 'hu', 'hu' => 'hun'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('ice', 'is')); + $expected = array('ice' => 'is', 'is' => 'ice'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('isl', 'is')); + $expected = array('isl' => 'is', 'is' => 'ice'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('ind', 'id')); + $expected = array('ind' => 'id', 'id' => 'ind'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('gle', 'ga')); + $expected = array('gle' => 'ga', 'ga' => 'gle'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('ita', 'it')); + $expected = array('ita' => 'it', 'it' => 'ita'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('jpn', 'ja')); + $expected = array('jpn' => 'ja', 'ja' => 'jpn'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('kor', 'ko')); + $expected = array('kor' => 'ko', 'ko' => 'kor'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('lav', 'lv')); + $expected = array('lav' => 'lv', 'lv' => 'lav'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('lit', 'lt')); + $expected = array('lit' => 'lt', 'lt' => 'lit'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('mac', 'mk')); + $expected = array('mac' => 'mk', 'mk' => 'mac'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('mkd', 'mk')); + $expected = array('mkd' => 'mk', 'mk' => 'mac'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('may', 'ms')); + $expected = array('may' => 'ms', 'ms' => 'may'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('msa', 'ms')); + $expected = array('msa' => 'ms', 'ms' => 'may'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('mlt', 'mt')); + $expected = array('mlt' => 'mt', 'mt' => 'mlt'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('nor', 'no')); + $expected = array('nor' => 'no', 'no' => 'nor'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('nob', 'nb')); + $expected = array('nob' => 'nb', 'nb' => 'nob'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('nno', 'nn')); + $expected = array('nno' => 'nn', 'nn' => 'nno'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('pol', 'pl')); + $expected = array('pol' => 'pl', 'pl' => 'pol'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('por', 'pt')); + $expected = array('por' => 'pt', 'pt' => 'por'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('roh', 'rm')); + $expected = array('roh' => 'rm', 'rm' => 'roh'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('ron', 'ro')); + $expected = array('ron' => 'ro', 'ro' => 'rum'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('rum', 'ro')); + $expected = array('rum' => 'ro', 'ro' => 'rum'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('rus', 'ru')); + $expected = array('rus' => 'ru', 'ru' => 'rus'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('smi', 'sz')); + $expected = array('smi' => 'sz', 'sz' => 'smi'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('scc', 'sr')); + $expected = array('scc' => 'sr', 'sr' => 'scc'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('srp', 'sr')); + $expected = array('srp' => 'sr', 'sr' => 'scc'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('slk', 'sk')); + $expected = array('slk' => 'sk', 'sk' => 'slo'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('slo', 'sk')); + $expected = array('slo' => 'sk', 'sk' => 'slo'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('slv', 'sl')); + $expected = array('slv' => 'sl', 'sl' => 'slv'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('wen', 'sb')); + $expected = array('wen' => 'sb', 'sb' => 'wen'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('spa', 'es')); + $expected = array('spa' => 'es', 'es' => 'spa'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('swe', 'sv')); + $expected = array('swe' => 'sv', 'sv' => 'swe'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('tha', 'th')); + $expected = array('tha' => 'th', 'th' => 'tha'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('tso', 'ts')); + $expected = array('tso' => 'ts', 'ts' => 'tso'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('tsn', 'tn')); + $expected = array('tsn' => 'tn', 'tn' => 'tsn'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('tur', 'tr')); + $expected = array('tur' => 'tr', 'tr' => 'tur'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('ukr', 'uk')); + $expected = array('ukr' => 'uk', 'uk' => 'ukr'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('urd', 'ur')); + $expected = array('urd' => 'ur', 'ur' => 'urd'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('ven', 've')); + $expected = array('ven' => 've', 've' => 'ven'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('vie', 'vi')); + $expected = array('vie' => 'vi', 'vi' => 'vie'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('xho', 'xh')); + $expected = array('xho' => 'xh', 'xh' => 'xho'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('yid', 'yi')); + $expected = array('yid' => 'yi', 'yi' => 'yid'); + $this->assertEqual($result, $expected); + + $result = $l10n->map(array('zul', 'zu')); + $expected = array('zul' => 'zu', 'zu' => 'zul'); + $this->assertEqual($result, $expected); + } +/** + * testCatalog method + * + * @access public + * @return void + */ + function testCatalog() { + $l10n =& new L10n(); + + $result = $l10n->catalog(array('af')); + $expected = array( + 'af' => array('language' => 'Afrikaans', 'locale' => 'afr', 'localeFallback' => 'afr', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('ar', 'ar-ae', 'ar-bh', 'ar-dz', 'ar-eg', 'ar-iq', 'ar-jo', 'ar-kw', 'ar-lb', 'ar-ly', 'ar-ma', + 'ar-om', 'ar-qa', 'ar-sa', 'ar-sy', 'ar-tn', 'ar-ye')); + $expected = array( + 'ar' => array('language' => 'Arabic', 'locale' => 'ara', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-ae' => array('language' => 'Arabic (U.A.E.)', 'locale' => 'ar_ae', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-bh' => array('language' => 'Arabic (Bahrain)', 'locale' => 'ar_bh', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-dz' => array('language' => 'Arabic (Algeria)', 'locale' => 'ar_dz', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-eg' => array('language' => 'Arabic (Egypt)', 'locale' => 'ar_eg', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-iq' => array('language' => 'Arabic (Iraq)', 'locale' => 'ar_iq', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-jo' => array('language' => 'Arabic (Jordan)', 'locale' => 'ar_jo', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-kw' => array('language' => 'Arabic (Kuwait)', 'locale' => 'ar_kw', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-lb' => array('language' => 'Arabic (Lebanon)', 'locale' => 'ar_lb', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-ly' => array('language' => 'Arabic (Libya)', 'locale' => 'ar_ly', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-ma' => array('language' => 'Arabic (Morocco)', 'locale' => 'ar_ma', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-om' => array('language' => 'Arabic (Oman)', 'locale' => 'ar_om', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-qa' => array('language' => 'Arabic (Qatar)', 'locale' => 'ar_qa', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-sa' => array('language' => 'Arabic (Saudi Arabia)', 'locale' => 'ar_sa', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-sy' => array('language' => 'Arabic (Syria)', 'locale' => 'ar_sy', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-tn' => array('language' => 'Arabic (Tunisia)', 'locale' => 'ar_tn', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'ar-ye' => array('language' => 'Arabic (Yemen)', 'locale' => 'ar_ye', 'localeFallback' => 'ara', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('be')); + $expected = array( + 'be' => array('language' => 'Byelorussian', 'locale' => 'bel', 'localeFallback' => 'bel', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('bg')); + $expected = array( + 'bg' => array('language' => 'Bulgarian', 'locale' => 'bul', 'localeFallback' => 'bul', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('bs')); + $expected = array( + 'bs' => array('language' => 'Bosnian', 'locale' => 'bos', 'localeFallback' => 'bos', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('ca')); + $expected = array( + 'ca' => array('language' => 'Catalan', 'locale' => 'cat', 'localeFallback' => 'cat', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('cs')); + $expected = array( + 'cs' => array('language' => 'Czech', 'locale' => 'cze', 'localeFallback' => 'cze', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('da')); + $expected = array( + 'da' => array('language' => 'Danish', 'locale' => 'dan', 'localeFallback' => 'dan', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('de', 'de-at', 'de-ch', 'de-de', 'de-li', 'de-lu')); + $expected = array( + 'de' => array('language' => 'German (Standard)', 'locale' => 'deu', 'localeFallback' => 'deu', 'charset' => 'utf-8'), + 'de-at' => array('language' => 'German (Austria)', 'locale' => 'de_at', 'localeFallback' => 'deu', 'charset' => 'utf-8'), + 'de-ch' => array('language' => 'German (Swiss)', 'locale' => 'de_ch', 'localeFallback' => 'deu', 'charset' => 'utf-8'), + 'de-de' => array('language' => 'German (Germany)', 'locale' => 'de_de', 'localeFallback' => 'deu', 'charset' => 'utf-8'), + 'de-li' => array('language' => 'German (Liechtenstein)', 'locale' => 'de_li', 'localeFallback' => 'deu', 'charset' => 'utf-8'), + 'de-lu' => array('language' => 'German (Luxembourg)', 'locale' => 'de_lu', 'localeFallback' => 'deu', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('e', 'el')); + $expected = array( + 'e' => array('language' => 'Greek', 'locale' => 'gre', 'localeFallback' => 'gre', 'charset' => 'utf-8'), + 'el' => array('language' => 'Greek', 'locale' => 'gre', 'localeFallback' => 'gre', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('en', 'en-au', 'en-bz', 'en-ca', 'en-gb', 'en-ie', 'en-jm', 'en-nz', 'en-tt', 'en-us', 'en-za')); + $expected = array( + 'en' => array('language' => 'English', 'locale' => 'eng', 'localeFallback' => 'eng', 'charset' => 'utf-8'), + 'en-au' => array('language' => 'English (Australian)', 'locale' => 'en_au', 'localeFallback' => 'eng', 'charset' => 'utf-8'), + 'en-bz' => array('language' => 'English (Belize)', 'locale' => 'en_bz', 'localeFallback' => 'eng', 'charset' => 'utf-8'), + 'en-ca' => array('language' => 'English (Canadian)', 'locale' => 'en_ca', 'localeFallback' => 'eng', 'charset' => 'utf-8'), + 'en-gb' => array('language' => 'English (British)', 'locale' => 'en_gb', 'localeFallback' => 'eng', 'charset' => 'utf-8'), + 'en-ie' => array('language' => 'English (Ireland)', 'locale' => 'en_ie', 'localeFallback' => 'eng', 'charset' => 'utf-8'), + 'en-jm' => array('language' => 'English (Jamaica)', 'locale' => 'en_jm', 'localeFallback' => 'eng', 'charset' => 'utf-8'), + 'en-nz' => array('language' => 'English (New Zealand)', 'locale' => 'en_nz', 'localeFallback' => 'eng', 'charset' => 'utf-8'), + 'en-tt' => array('language' => 'English (Trinidad)', 'locale' => 'en_tt', 'localeFallback' => 'eng', 'charset' => 'utf-8'), + 'en-us' => array('language' => 'English (United States)', 'locale' => 'en_us', 'localeFallback' => 'eng', 'charset' => 'utf-8'), + 'en-za' => array('language' => 'English (South Africa)', 'locale' => 'en_za', 'localeFallback' => 'eng', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('es', 'es-ar', 'es-bo', 'es-cl', 'es-co', 'es-cr', 'es-do', 'es-ec', 'es-es', 'es-gt', 'es-hn', + 'es-mx', 'es-ni', 'es-pa', 'es-pe', 'es-pr', 'es-py', 'es-sv', 'es-uy', 'es-ve')); + $expected = array( + 'es' => array('language' => 'Spanish (Spain - Traditional)', 'locale' => 'spa', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-ar' => array('language' => 'Spanish (Argentina)', 'locale' => 'es_ar', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-bo' => array('language' => 'Spanish (Bolivia)', 'locale' => 'es_bo', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-cl' => array('language' => 'Spanish (Chile)', 'locale' => 'es_cl', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-co' => array('language' => 'Spanish (Colombia)', 'locale' => 'es_co', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-cr' => array('language' => 'Spanish (Costa Rica)', 'locale' => 'es_cr', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-do' => array('language' => 'Spanish (Dominican Republic)', 'locale' => 'es_do', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-ec' => array('language' => 'Spanish (Ecuador)', 'locale' => 'es_ec', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-es' => array('language' => 'Spanish (Spain)', 'locale' => 'es_es', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-gt' => array('language' => 'Spanish (Guatemala)', 'locale' => 'es_gt', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-hn' => array('language' => 'Spanish (Honduras)', 'locale' => 'es_hn', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-mx' => array('language' => 'Spanish (Mexican)', 'locale' => 'es_mx', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-ni' => array('language' => 'Spanish (Nicaragua)', 'locale' => 'es_ni', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-pa' => array('language' => 'Spanish (Panama)', 'locale' => 'es_pa', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-pe' => array('language' => 'Spanish (Peru)', 'locale' => 'es_pe', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-pr' => array('language' => 'Spanish (Puerto Rico)', 'locale' => 'es_pr', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-py' => array('language' => 'Spanish (Paraguay)', 'locale' => 'es_py', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-sv' => array('language' => 'Spanish (El Salvador)', 'locale' => 'es_sv', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-uy' => array('language' => 'Spanish (Uruguay)', 'locale' => 'es_uy', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'es-ve' => array('language' => 'Spanish (Venezuela)', 'locale' => 'es_ve', 'localeFallback' => 'spa', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('et')); + $expected = array( + 'et' => array('language' => 'Estonian', 'locale' => 'est', 'localeFallback' => 'est', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('eu')); + $expected = array( + 'eu' => array('language' => 'Basque', 'locale' => 'baq', 'localeFallback' => 'baq', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('fa')); + $expected = array( + 'fa' => array('language' => 'Farsi', 'locale' => 'per', 'localeFallback' => 'per', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('fi')); + $expected = array( + 'fi' => array('language' => 'Finnish', 'locale' => 'fin', 'localeFallback' => 'fin', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('fo')); + $expected = array( + 'fo' => array('language' => 'Faeroese', 'locale' => 'fao', 'localeFallback' => 'fao', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('fr', 'fr-be', 'fr-ca', 'fr-ch', 'fr-fr', 'fr-lu')); + $expected = array( + 'fr' => array('language' => 'French (Standard)', 'locale' => 'fre', 'localeFallback' => 'fre', 'charset' => 'utf-8'), + 'fr-be' => array('language' => 'French (Belgium)', 'locale' => 'fr_be', 'localeFallback' => 'fre', 'charset' => 'utf-8'), + 'fr-ca' => array('language' => 'French (Canadian)', 'locale' => 'fr_ca', 'localeFallback' => 'fre', 'charset' => 'utf-8'), + 'fr-ch' => array('language' => 'French (Swiss)', 'locale' => 'fr_ch', 'localeFallback' => 'fre', 'charset' => 'utf-8'), + 'fr-fr' => array('language' => 'French (France)', 'locale' => 'fr_fr', 'localeFallback' => 'fre', 'charset' => 'utf-8'), + 'fr-lu' => array('language' => 'French (Luxembourg)', 'locale' => 'fr_lu', 'localeFallback' => 'fre', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('ga')); + $expected = array( + 'ga' => array('language' => 'Irish', 'locale' => 'gle', 'localeFallback' => 'gle', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('gd', 'gd-ie')); + $expected = array( + 'gd' => array('language' => 'Gaelic (Scots)', 'locale' => 'gla', 'localeFallback' => 'gla', 'charset' => 'utf-8'), + 'gd-ie' => array('language' => 'Gaelic (Irish)', 'locale' => 'gd_ie', 'localeFallback' => 'gla', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('gl')); + $expected = array( + 'gl' => array('language' => 'Galician', 'locale' => 'glg', 'localeFallback' => 'glg', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('he')); + $expected = array( + 'he' => array('language' => 'Hebrew', 'locale' => 'heb', 'localeFallback' => 'heb', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('he')); + $expected = array( + 'he' => array('language' => 'Hebrew', 'locale' => 'heb', 'localeFallback' => 'heb', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('hi')); + $expected = array( + 'hi' => array('language' => 'Hindi', 'locale' => 'hin', 'localeFallback' => 'hin', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('hr')); + $expected = array( + 'hr' => array('language' => 'Croatian', 'locale' => 'hrv', 'localeFallback' => 'hrv', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('hu')); + $expected = array( + 'hu' => array('language' => 'Hungarian', 'locale' => 'hun', 'localeFallback' => 'hun', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('hy')); + $expected = array( + 'hy' => array('language' => 'Armenian - Armenia', 'locale' => 'hye', 'localeFallback' => 'hye', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('id', 'in')); + $expected = array( + 'id' => array('language' => 'Indonesian', 'locale' => 'ind', 'localeFallback' => 'ind', 'charset' => 'utf-8'), + 'in' => array('language' => 'Indonesian', 'locale' => 'ind', 'localeFallback' => 'ind', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('is')); + $expected = array( + 'is' => array('language' => 'Icelandic', 'locale' => 'ice', 'localeFallback' => 'ice', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('it', 'it-ch')); + $expected = array( + 'it' => array('language' => 'Italian', 'locale' => 'ita', 'localeFallback' => 'ita', 'charset' => 'utf-8'), + 'it-ch' => array('language' => 'Italian (Swiss) ', 'locale' => 'it_ch', 'localeFallback' => 'ita', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('ja')); + $expected = array( + 'ja' => array('language' => 'Japanese', 'locale' => 'jpn', 'localeFallback' => 'jpn', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('ko', 'ko-kp', 'ko-kr')); + $expected = array( + 'ko' => array('language' => 'Korean', 'locale' => 'kor', 'localeFallback' => 'kor', 'charset' => 'kr'), + 'ko-kp' => array('language' => 'Korea (North)', 'locale' => 'ko_kp', 'localeFallback' => 'kor', 'charset' => 'kr'), + 'ko-kr' => array('language' => 'Korea (South)', 'locale' => 'ko_kr', 'localeFallback' => 'kor', 'charset' => 'kr') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('koi8-r', 'ru', 'ru-mo')); + $expected = array( + 'koi8-r' => array('language' => 'Russian', 'locale' => 'koi8_r', 'localeFallback' => 'rus', 'charset' => 'koi8-r'), + 'ru' => array('language' => 'Russian', 'locale' => 'rus', 'localeFallback' => 'rus', 'charset' => 'utf-8'), + 'ru-mo' => array('language' => 'Russian (Moldavia)', 'locale' => 'ru_mo', 'localeFallback' => 'rus', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('lt')); + $expected = array( + 'lt' => array('language' => 'Lithuanian', 'locale' => 'lit', 'localeFallback' => 'lit', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('lv')); + $expected = array( + 'lv' => array('language' => 'Latvian', 'locale' => 'lav', 'localeFallback' => 'lav', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('mk', 'mk-mk')); + $expected = array( + 'mk' => array('language' => 'FYRO Macedonian', 'locale' => 'mk', 'localeFallback' => 'mac', 'charset' => 'utf-8'), + 'mk-mk' => array('language' => 'Macedonian', 'locale' => 'mk_mk', 'localeFallback' => 'mac', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('ms')); + $expected = array( + 'ms' => array('language' => 'Malaysian', 'locale' => 'may', 'localeFallback' => 'may', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('mt')); + $expected = array( + 'mt' => array('language' => 'Maltese', 'locale' => 'mlt', 'localeFallback' => 'mlt', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('n', 'nl', 'nl-be')); + $expected = array( + 'n' => array('language' => 'Dutch (Standard)', 'locale' => 'dut', 'localeFallback' => 'dut', 'charset' => 'utf-8'), + 'nl' => array('language' => 'Dutch (Standard)', 'locale' => 'dut', 'localeFallback' => 'dut', 'charset' => 'utf-8'), + 'nl-be' => array('language' => 'Dutch (Belgium)', 'locale' => 'nl_be', 'localeFallback' => 'dut', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('nb')); + $expected = array( + 'nb' => array('language' => 'Norwegian Bokmal', 'locale' => 'nob', 'localeFallback' => 'nor', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('nn', 'no')); + $expected = array( + 'nn' => array('language' => 'Norwegian Nynorsk', 'locale' => 'nno', 'localeFallback' => 'nor', 'charset' => 'utf-8'), + 'no' => array('language' => 'Norwegian', 'locale' => 'nor', 'localeFallback' => 'nor', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('p', 'pl')); + $expected = array( + 'p' => array('language' => 'Polish', 'locale' => 'pol', 'localeFallback' => 'pol', 'charset' => 'utf-8'), + 'pl' => array('language' => 'Polish', 'locale' => 'pol', 'localeFallback' => 'pol', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('pt', 'pt-br')); + $expected = array( + 'pt' => array('language' => 'Portuguese (Portugal)', 'locale' => 'por', 'localeFallback' => 'por', 'charset' => 'utf-8'), + 'pt-br' => array('language' => 'Portuguese (Brazil)', 'locale' => 'pt_br', 'localeFallback' => 'por', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('rm')); + $expected = array( + 'rm' => array('language' => 'Rhaeto-Romanic', 'locale' => 'roh', 'localeFallback' => 'roh', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('ro', 'ro-mo')); + $expected = array( + 'ro' => array('language' => 'Romanian', 'locale' => 'rum', 'localeFallback' => 'rum', 'charset' => 'utf-8'), + 'ro-mo' => array('language' => 'Romanian (Moldavia)', 'locale' => 'ro_mo', 'localeFallback' => 'rum', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('sb')); + $expected = array( + 'sb' => array('language' => 'Sorbian', 'locale' => 'wen', 'localeFallback' => 'wen', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('sk')); + $expected = array( + 'sk' => array('language' => 'Slovak', 'locale' => 'slo', 'localeFallback' => 'slo', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('sl')); + $expected = array( + 'sl' => array('language' => 'Slovenian', 'locale' => 'slv', 'localeFallback' => 'slv', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('sq')); + $expected = array( + 'sq' => array('language' => 'Albanian', 'locale' => 'alb', 'localeFallback' => 'alb', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('sr')); + $expected = array( + 'sr' => array('language' => 'Serbian', 'locale' => 'scc', 'localeFallback' => 'scc', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('sv', 'sv-fi')); + $expected = array( + 'sv' => array('language' => 'Swedish', 'locale' => 'swe', 'localeFallback' => 'swe', 'charset' => 'utf-8'), + 'sv-fi' => array('language' => 'Swedish (Findland)', 'locale' => 'sv_fi', 'localeFallback' => 'swe', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('sx')); + $expected = array( + 'sx' => array('language' => 'Sutu', 'locale' => 'sx', 'localeFallback' => 'sx', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('sz')); + $expected = array( + 'sz' => array('language' => 'Sami (Lappish)', 'locale' => 'smi', 'localeFallback' => 'smi', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('th')); + $expected = array( + 'th' => array('language' => 'Thai', 'locale' => 'tha', 'localeFallback' => 'tha', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('tn')); + $expected = array( + 'tn' => array('language' => 'Tswana', 'locale' => 'tsn', 'localeFallback' => 'tsn', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('tr')); + $expected = array( + 'tr' => array('language' => 'Turkish', 'locale' => 'tur', 'localeFallback' => 'tur', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('ts')); + $expected = array( + 'ts' => array('language' => 'Tsonga', 'locale' => 'tso', 'localeFallback' => 'tso', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('uk')); + $expected = array( + 'uk' => array('language' => 'Ukrainian', 'locale' => 'ukr', 'localeFallback' => 'ukr', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('ur')); + $expected = array( + 'ur' => array('language' => 'Urdu', 'locale' => 'urd', 'localeFallback' => 'urd', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('ve')); + $expected = array( + 've' => array('language' => 'Venda', 'locale' => 'ven', 'localeFallback' => 'ven', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('vi')); + $expected = array( + 'vi' => array('language' => 'Vietnamese', 'locale' => 'vie', 'localeFallback' => 'vie', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('xh')); + $expected = array( + 'xh' => array('language' => 'Xhosa', 'locale' => 'xho', 'localeFallback' => 'xho', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('yi')); + $expected = array( + 'yi' => array('language' => 'Yiddish', 'locale' => 'yid', 'localeFallback' => 'yid', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('zh', 'zh-cn', 'zh-hk', 'zh-sg', 'zh-tw')); + $expected = array( + 'zh' => array('language' => 'Chinese', 'locale' => 'chi', 'localeFallback' => 'chi', 'charset' => 'utf-8'), + 'zh-cn' => array('language' => 'Chinese (PRC)', 'locale' => 'zh_cn', 'localeFallback' => 'chi', 'charset' => 'GB2312'), + 'zh-hk' => array('language' => 'Chinese (Hong Kong)', 'locale' => 'zh_hk', 'localeFallback' => 'chi', 'charset' => 'utf-8'), + 'zh-sg' => array('language' => 'Chinese (Singapore)', 'locale' => 'zh_sg', 'localeFallback' => 'chi', 'charset' => 'utf-8'), + 'zh-tw' => array('language' => 'Chinese (Taiwan)', 'locale' => 'zh_tw', 'localeFallback' => 'chi', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('zu')); + $expected = array( + 'zu' => array('language' => 'Zulu', 'locale' => 'zul', 'localeFallback' => 'zul', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + + $result = $l10n->catalog(array('en-nz', 'es-do', 'sz', 'ar-lb', 'zh-hk', 'pt-br')); + $expected = array( + 'en-nz' => array('language' => 'English (New Zealand)', 'locale' => 'en_nz', 'localeFallback' => 'eng', 'charset' => 'utf-8'), + 'es-do' => array('language' => 'Spanish (Dominican Republic)', 'locale' => 'es_do', 'localeFallback' => 'spa', 'charset' => 'utf-8'), + 'sz' => array('language' => 'Sami (Lappish)', 'locale' => 'smi', 'localeFallback' => 'smi', 'charset' => 'utf-8'), + 'ar-lb' => array('language' => 'Arabic (Lebanon)', 'locale' => 'ar_lb', 'localeFallback' => 'ara', 'charset' => 'utf-8'), + 'zh-hk' => array('language' => 'Chinese (Hong Kong)', 'locale' => 'zh_hk', 'localeFallback' => 'chi', 'charset' => 'utf-8'), + 'pt-br' => array('language' => 'Portuguese (Brazil)', 'locale' => 'pt_br', 'localeFallback' => 'por', 'charset' => 'utf-8') + ); + $this->assertEqual($result, $expected); + } +} +?> \ No newline at end of file diff --git a/cake/tests/cases/libs/magic_db.test.php b/cake/tests/cases/libs/magic_db.test.php new file mode 100755 index 0000000..3eba7eb --- /dev/null +++ b/cake/tests/cases/libs/magic_db.test.php @@ -0,0 +1,204 @@ +<?php +/* SVN FILE: $Id$ */ +/** + * MagicDbTest file + * + * PHP versions 4 and 5 + * + * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) + * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v 1.2.0 + * @version $Revision$ + * @modifiedby $LastChangedBy$ + * @lastmodified $Date$ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + */ +uses('magic_db', 'object'); +/** + * The test class for the MagicDb class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class MagicDbTest extends UnitTestCase { +/** + * The MagicDb instance to be tested + * + * @var MagicDb + * @access public + */ + var $Db = null; +/** + * Sets up a MagicDb class instance for testing + * + * @access public + */ + function setUp() { + $this->Db =& new MagicDb(); + } +/** + * MagicDb::analyze should properly detect the file type and output additional info as requested. + * + * @access public + */ + function testAnalyze() { + $r = $this->Db->read(MagicDbTestData::get('magic.db')); + $this->assertTrue($r === true); + + $r = $this->Db->analyze(array()); + $this->assertTrue($r === false); + + $r = $this->Db->analyze(WWW_ROOT.'img'.DS.'cake.icon.gif'); + // TODO: Check several serialized file samples for accurate detection + } +/** + * MagicDb::read should properly read MagicDb databases from .php-/.db-files and plain data arguments passed in and return false if the file wasn't found or + * if the readed data did not validate. + * + * @access public + */ + function testRead() { + $this->Db->db = array(); + + $r = $this->Db->read(true); + $this->assertTrue($r === false); + $r = $this->Db->read(5); + $this->assertTrue($r === false); + + $this->Db->db = array('a'); + $r = $this->Db->read(array('foo' => 'bar')); + $this->assertTrue($r === false); + $this->assertTrue($this->Db->db === array('a')); + + $magicDb = array('header' => array(), 'database' => array()); + $r = $this->Db->read($magicDb); + $this->assertTrue($r === true); + $this->assertTrue($this->Db->db === $magicDb); + + // @TODO: Test parsing an actual magic.db file + + $r = $this->Db->read('does-not-exist.db'); + $this->assertTrue($r === false); + $this->assertTrue($this->Db->db === $magicDb); + + if (file_exists(VENDORS.'magic.php')) { + $r = $this->Db->read(VENDORS.'magic.php'); + $this->assertTrue($r === true); + $this->assertTrue($this->Db->db === array('header' => array(), 'database' => array())); + } + + $r = $this->Db->read(MagicDbTestData::get('magic.snippet.db')); + $this->assertTrue($r === true); + } +/** + * MagicDb::toArray should either return the MagicDb::db property, or the parsed array data if a magic.db dump is passed in as the first argument + * + * @access public + */ + function testToArray() { + $this->Db->db = array(); + + $r = $this->Db->toArray(); + $this->assertTrue($r === array()); + $this->Db->db = array('foo' => 'bar'); + $r = $this->Db->toArray(); + $this->assertTrue($r === array('foo' => 'bar')); + + $r = $this->Db->toArray(array('yeah')); + $this->assertTrue($r === array('yeah')); + + $r = $this->Db->toArray('foo'); + $this->assertTrue($r === array()); + + $r = $this->Db->toArray(MagicDbTestData::get('magic.snippet.db')); + $this->assertTrue($r === MagicDbTestData::get('magic.snippet.db.result')); + } +/** + * The MagicDb::validates function should return if the array passed to it or the local db property contains a valid MagicDb record set + * + * @access public + */ + function testValidates() { + $r = $this->Db->validates(array()); + $this->assertTrue($r === false); + + $r = $this->Db->validates(array('header' => true, 'database' => true)); + $this->assertTrue($r === false); + $magicDb = array('header' => array(), 'database' => array()); + $r = $this->Db->validates($magicDb); + $this->assertTrue($r === true); + + $this->Db->db = array(); + $r = $this->Db->validates(); + $this->assertTrue($r === false); + + $this->Db->db = $magicDb; + $r = $this->Db->validates(); + $this->assertTrue($r === true); + } +} +/** + * Test data holding object for MagicDb tests + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +/** + * MagicDbTestData class + * + * @package cake + * @subpackage cake.tests.cases.libs + */ +class MagicDbTestData extends Object { +/** + * Base64 encoded data + * + * @var array + * @access public + */ + var $data = array( + 'magic.snippet.db' => 'IyBGSUxFX0lEIERCDQojIERhdGU6MjAwNS0wMy0yOQ0KIyBTb3VyY2U6aHR0cDovL3d3dy5tYWdpY2RiLm9yZw0KDQojIE1hZ2ljIElEIGZvciBXb3JkcGVyZmVjdCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTQgYnkgQ2FybA0KMAlzdHJpbmcJXFx4RkZXUEMJW2ZpZD0wMDAwMDEwMDgtMDAtMDAwMDAwMTtleHQ9O21pbWU9O11Xb3JkcGVyZmVjdCBoZWxwIGZpbGUNCiY5CWJ5dGUJMHgwMgkNCj4xMCBieXRlCXgJLCB2ZXJzaW9uICVkDQo+MTEJYnl0ZQl4CS4lZA0KDQojIE1hZ2ljIElEIGZvciBXb3JkcGVyZmVjdCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTQgYnkgQ2FybA0KMAlzdHJpbmcJXFx4RkZXUEMJW2ZpZD0wMDAwMDEwMDgtMDAtMDAwMDAwMTtleHQ9O21pbWU9O11Xb3JkcGVyZmVjdCBhcHBsaWNhdGlvbiByZXNvdXJjZSBsaWJyYXJ5DQomOQlieXRlCTUxCQ0KPjEwCWJ5dGUJeAksIHZlcnNpb24gJWQNCj4xMQlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwMDAxO2V4dD07bWltZT07XVdvcmRwZXJmZWN0IGJsb2NrIGZpbGUNCiY5CWJ5dGUJMTMJDQo+MTAJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjExCWJ5dGUJeAkuJWQ=', + 'magic.snippet.db.result' => 'YToyOntzOjY6ImhlYWRlciI7YToyOntzOjQ6IkRhdGUiO3M6MTA6IjIwMDUtMDMtMjkiO3M6NjoiU291cmNlIjtzOjIyOiJodHRwOi8vd3d3Lm1hZ2ljZGIub3JnIjt9czo4OiJkYXRhYmFzZSI7YToyOntpOjA7YTo0OntpOjA7YTo0OntpOjA7czoxOiIwIjtpOjE7czo2OiJzdHJpbmciO2k6MjtzOjg6IlxceEZGV1BDIjtpOjM7czo1OToiW2ZpZD0wMDAwMDEwMDgtMDAtMDAwMDAwMTtleHQ9O21pbWU9O11Xb3JkcGVyZmVjdCBoZWxwIGZpbGUiO31pOjE7YTo0OntpOjA7czoyOiImOSI7aToxO3M6NDoiYnl0ZSI7aToyO3M6NDoiMHgwMiI7aTozO3M6MDoiIjt9aToyO2E6Mzp7aTowO3M6ODoiPjEwIGJ5dGUiO2k6MTtzOjE6IngiO2k6MjtzOjEyOiIsIHZlcnNpb24gJWQiO31pOjM7YTo0OntpOjA7czozOiI+MTEiO2k6MTtzOjQ6ImJ5dGUiO2k6MjtzOjE6IngiO2k6MztzOjM6Ii4lZCI7fX1pOjE7YTo0OntpOjA7YTo0OntpOjA7czoxOiIwIjtpOjE7czo2OiJzdHJpbmciO2k6MjtzOjg6IlxceEZGV1BDIjtpOjM7czo3ODoiW2ZpZD0wMDAwMDEwMDgtMDAtMDAwMDAwMTtleHQ9O21pbWU9O11Xb3JkcGVyZmVjdCBhcHBsaWNhdGlvbiByZXNvdXJjZSBsaWJyYXJ5Ijt9aToxO2E6NDp7aTowO3M6MjoiJjkiO2k6MTtzOjQ6ImJ5dGUiO2k6MjtzOjI6IjUxIjtpOjM7czowOiIiO31pOjI7YTo0OntpOjA7czozOiI+MTAiO2k6MTtzOjQ6ImJ5dGUiO2k6MjtzOjE6IngiO2k6MztzOjEyOiIsIHZlcnNpb24gJWQiO31pOjM7YTo0OntpOjA7czozOiI+MTEiO2k6MTtzOjQ6ImJ5dGUiO2k6MjtzOjE6IngiO2k6MztzOjM6Ii4lZCI7fX19fQ==', + 'magic.db' => 'IyBGSUxFX0lEIERCDQojIERhdGU6MjAwNS0wMy0yOQ0KIyBTb3VyY2U6aHR0cDovL3d3dy5tYWdpY2RiLm9yZw0KDQojIE1hZ2ljIElEIGZvciBXb3JkcGVyZmVjdCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTQgYnkgQ2FybA0KMAlzdHJpbmcJXFx4RkZXUEMJW2ZpZD0wMDAwMDEwMDgtMDAtMDAwMDAwMTtleHQ9O21pbWU9O11Xb3JkcGVyZmVjdCBoZWxwIGZpbGUNCiY5CWJ5dGUJMHgwMgkNCj4xMCBieXRlCXgJLCB2ZXJzaW9uICVkDQo+MTEJYnl0ZQl4CS4lZA0KDQojIE1hZ2ljIElEIGZvciBXb3JkcGVyZmVjdCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTQgYnkgQ2FybA0KMAlzdHJpbmcJXFx4RkZXUEMJW2ZpZD0wMDAwMDEwMDgtMDAtMDAwMDAwMTtleHQ9O21pbWU9O11Xb3JkcGVyZmVjdCBhcHBsaWNhdGlvbiByZXNvdXJjZSBsaWJyYXJ5DQomOQlieXRlCTUxCQ0KPjEwCWJ5dGUJeAksIHZlcnNpb24gJWQNCj4xMQlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwMDAxO2V4dD07bWltZT07XVdvcmRwZXJmZWN0IGJsb2NrIGZpbGUNCiY5CWJ5dGUJMTMJDQo+MTAJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjExCWJ5dGUJeAkuJWQNCg0KIyBNYWdpYyBJRCBmb3IgV29yZHBlcmZlY3QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE0IGJ5IENhcmwNCjAJc3RyaW5nCVxceEZGV1BDCVtmaWQ9MDAwMDAxMDA4LTAwLTAwMDAwMDE7ZXh0PTttaW1lPTtdV29yZHBlcmZlY3QgY29sdW1uIGJsb2NrDQomOQlieXRlCTE1CQ0KPjEwCWJ5dGUJeAksIHZlcnNpb24gJWQNCj4xMQlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwMDAxO2V4dD07bWltZT07XVdvcmRwZXJmZWN0IGRpY3Rpb25hcnkgZmlsZQ0KJjkJYnl0ZQkweDBCCQ0KPjEwCWJ5dGUJeAksIHZlcnNpb24gJWQNCj4xMQlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwMDAxO2V4dD07bWltZT07XVdvcmRwZXJmZWN0IGRpY3Rpb25hcnkgcnVsZXMgZmlsZQ0KJjkJYnl0ZQkzNAkNCj4xMAlieXRlCXgJLCB2ZXJzaW9uICVkDQo+MTEJYnl0ZQl4CS4lZA0KDQojIE1hZ2ljIElEIGZvciBXb3JkcGVyZmVjdCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTQgYnkgQ2FybA0KMAlzdHJpbmcJXFx4RkZXUEMJW2ZpZD0wMDAwMDEwMDgtMDAtMDAwMDAwMTtleHQ9O21pbWU9O11Xb3JkcGVyZmVjdCBleHRlcm5hbCBzcGVsbCBjb2RlIG1vZHVsZSBmaWxlDQomOQlieXRlCTQ2CQ0KPjEwCWJ5dGUJeAksIHZlcnNpb24gJWQNCj4xMQlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwMDAxO2V4dD07bWltZT07XVdvcmRwZXJmZWN0IGV4dGVybmFsIHNwZWxsIGRpY3Rpb25hcnkgZmlsZQ0KJjkJYnl0ZQk0NwkNCj4xMAlieXRlCXgJLCB2ZXJzaW9uICVkDQo+MTEJYnl0ZQl4CS4lZA0KDQojIE1hZ2ljIElEIGZvciBXb3JkcGVyZmVjdCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTQgYnkgQ2FybA0KMAlzdHJpbmcJXFx4RkZXUEMJW2ZpZD0wMDAwMDEwMDgtMDAtMDAwMDAwMTtleHQ9O21pbWU9O11Xb3JkcGVyZmVjdCBHcmFwaGljcyBzY3JlZW4gZHJpdmVyIGZpbGUNCiY5CWJ5dGUJMjYJDQo+MTAJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjExCWJ5dGUJeAkuJWQNCg0KIyBNYWdpYyBJRCBmb3IgV29yZHBlcmZlY3QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE0IGJ5IENhcmwNCjAJc3RyaW5nCVxceEZGV1BDCVtmaWQ9MDAwMDAxMDA4LTAwLTAwMDAwMDE7ZXh0PTttaW1lPTtdV29yZHBlcmZlY3QgaHlwaGVuYXRpb24gY29kZSBtb2R1bGUgZmlsZQ0KJjkJYnl0ZQkyMwkNCj4xMAlieXRlCXgJLCB2ZXJzaW9uICVkDQo+MTEJYnl0ZQl4CS4lZA0KDQojIE1hZ2ljIElEIGZvciBXb3JkcGVyZmVjdCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTQgYnkgQ2FybA0KMAlzdHJpbmcJXFx4RkZXUEMJW2ZpZD0wMDAwMDEwMDgtMDAtMDAwMDAwMTtleHQ9O21pbWU9O11Xb3JkcGVyZmVjdCBoeXBoZW5hdGlvbiBkYXRhIG1vZHVsZSBmaWxlDQomOQlieXRlCTI0CQ0KPjEwCWJ5dGUJeAksIHZlcnNpb24gJWQNCj4xMQlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwMDAxO2V4dD07bWltZT07XVdvcmRwZXJmZWN0IGh5cGhlbmF0aW9uIGxleCBtb2R1bGUNCiY5CWJ5dGUJMjcJDQo+MTAJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjExCWJ5dGUJeAkuJWQNCg0KIyBNYWdpYyBJRCBmb3IgV29yZHBlcmZlY3QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE0IGJ5IENhcmwNCjAJc3RyaW5nCVxceEZGV1BDCVtmaWQ9MDAwMDAxMDA4LTAwLTAwMDAwMDE7ZXh0PTttaW1lPTtdV29yZHBlcmZlY3QgaW5zdGFsbGF0aW9uIGluZm9ybWF0aW9uIGZpbGUNCiY5CWJ5dGUJNDEJDQo+MTAJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjExCWJ5dGUJeAkuJWQNCg0KIyBNYWdpYyBJRCBmb3IgV29yZHBlcmZlY3QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE0IGJ5IENhcmwNCjAJc3RyaW5nCVxceEZGV1BDCVtmaWQ9MDAwMDAxMDA4LTAwLTAwMDAwMDE7ZXh0PTttaW1lPTtdV29yZHBlcmZlY3Qga2V5Ym9hcmQgZGVmaW5pdGlvbiBmaWxlDQomOQlieXRlCTB4MDMJDQo+MTAJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjExCWJ5dGUJeAkuJWQNCg0KIyBNYWdpYyBJRCBmb3IgV29yZHBlcmZlY3QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE0IGJ5IENhcmwNCjAJc3RyaW5nCVxceEZGV1BDCVtmaWQ9MDAwMDAxMDA4LTAwLTAwMDAwMDE7ZXh0PTttaW1lPTtdV29yZHBlcmZlY3QgbWFjcm8gZGF0YSBmaWxlDQomOQlieXRlCTB4MDEJDQo+MTAJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjExCWJ5dGUJeAkuJWQNCg0KIyBNYWdpYyBJRCBmb3IgV29yZHBlcmZlY3QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE0IGJ5IENhcmwNCjAJc3RyaW5nCVxceEZGV1BDCVtmaWQ9MDAwMDAxMDA4LTAwLTAwMDAwMDE7ZXh0PTttaW1lPTtdV29yZHBlcmZlY3QgbWFjcm8gcmVzb3VyY2UgZmlsZQ0KJjkJYnl0ZQkyNQkNCj4xMAlieXRlCXgJLCB2ZXJzaW9uICVkDQo+MTEJYnl0ZQl4CS4lZA0KDQojIE1hZ2ljIElEIGZvciBXb3JkcGVyZmVjdCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTQgYnkgQ2FybA0KMAlzdHJpbmcJXFx4RkZXUEMJW2ZpZD0wMDAwMDEwMDgtMDAtMDAwMDAwMTtleHQ9O21pbWU9O11Xb3JkcGVyZmVjdCBwcmludGVyIFEgY29kZXMgKHVzZWQgYnkgVkFYL0RHKQ0KJjkJYnl0ZQkyOAkNCj4xMAlieXRlCXgJLCB2ZXJzaW9uICVkDQo+MTEJYnl0ZQl4CS4lZA0KDQojIE1hZ2ljIElEIGZvciBXb3JkcGVyZmVjdCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTQgYnkgQ2FybA0KMAlzdHJpbmcJXFx4RkZXUEMJW2ZpZD0wMDAwMDEwMDgtMDAtMDAwMDAwMTtleHQ9O21pbWU9O11Xb3JkcGVyZmVjdCByZWN0YW5ndWxhciBibG9jayBmaWxlDQomOQlieXRlCTE0CQ0KPjEwCWJ5dGUJeAksIHZlcnNpb24gJWQNCj4xMQlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwMDAxO2V4dD07bWltZT07XVdvcmRwZXJmZWN0IHNwZWxsIGNvZGUgbW9kdWxlIHJ1bGVzIGZpbGUNCiY5CWJ5dGUJMzMJDQo+MTAJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjExCWJ5dGUJeAkuJWQNCg0KIyBNYWdpYyBJRCBmb3IgV29yZHBlcmZlY3QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE0IGJ5IENhcmwNCjAJc3RyaW5nCVxceEZGV1BDCVtmaWQ9MDAwMDAxMDA4LTAwLTAwMDAwMDE7ZXh0PTttaW1lPTtdV29yZHBlcmZlY3Qgc3BlbGwgY29kZSBtb2R1bGUgd29yZCBsaXN0DQomOQlieXRlCTI5CQ0KPjEwCWJ5dGUJeAksIHZlcnNpb24gJWQNCj4xMQlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwMDAxO2V4dD07bWltZT07XVdvcmRwZXJmZWN0IHRoZXNhcnVzIGZpbGUNCiY5CWJ5dGUJMTIJDQo+MTAJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjExCWJ5dGUJeAkuJWQNCg0KIyBNYWdpYyBJRCBmb3IgV29yZHBlcmZlY3QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE0IGJ5IENhcmwNCjAJc3RyaW5nCVxceEZGV1BDCVtmaWQ9MDAwMDAxMDA4LTAwLTAwMDAwMDE7ZXh0PTttaW1lPTtdV29yZHBlcmZlY3QgVkFYIGtleWJvYXJkIGRlZmluaXRpb24gZmlsZQ0KJjkJYnl0ZQkweDA0CQ0KPjEwCWJ5dGUJeAksIHZlcnNpb24gJWQNCj4xMQlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwQUxMO2V4dD1hbGw7bWltZT07XVdvcmRwZXJmZWN0IHByaW50ZXIgcmVzb3VyY2UgZmlsZQ0KJjkJYnl0ZQkxOQkNCj4xMAlieXRlCXgJLCB2ZXJzaW9uICVkDQo+MTEJYnl0ZQl4CS4lZA0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTExIGJ5IENhcmwNCjAJc3RyaW5nCVJJRkYJW2ZpZD0wMDAwMDEwMDEtMDAtMDAwMENPTjtleHQ9Y29uO21pbWU9O11NaWNyb3NvZnQgQW5pbWF0ZWQgY3Vyc29yLCBsaXR0bGUtZW5kaWFuDQomOAlzdHJpbmcJQUNPTgkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQowCXN0cmluZwlSSUZYCVtmaWQ9MDAwMDAxMDAxLTAwLTAwMDBDT047ZXh0PWNvbjttaW1lPTtdTWljcm9zb2Z0IEFuaW1hdGVkIGN1cnNvciwgYmlnLWVuZGlhbg0KJjgJc3RyaW5nCUFDT04JDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwRE9DO2V4dD1kb2M7bWltZT07XU1hY2ludG9zaCBXb3JkcGVyZmVjdCBkb2N1bWVudCBmaWxlDQomOQlieXRlCTQ0CQ0KPjEwCWJ5dGUJeAksIHZlcnNpb24gJWQNCj4xMQlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwRE9DO2V4dD1kb2M7bWltZT07XVZBWCBXb3JkcGVyZmVjdCBkb2N1bWVudCBmaWxlDQomOQlieXRlCTQ1CQ0KPjEwCWJ5dGUJeAksIHZlcnNpb24gJWQNCj4xMQlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwRFJTO2V4dD1kcnM7bWltZT07XVdvcmRwZXJmZWN0IGRpc3BsYXkgcmVzb3VyY2UgZmlsZQ0KJjkJYnl0ZQkyMAkNCj4xMAlieXRlCXgJLCB2ZXJzaW9uICVkDQo+MTEJYnl0ZQl4CS4lZA0KDQojIE1hZ2ljIElEIGZvciBXb3JkcGVyZmVjdCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTQgYnkgQ2FybA0KMAlzdHJpbmcJXFx4RkZXUEMJW2ZpZD0wMDAwMDEwMDgtMDAtMDAwMEZJTDtleHQ9ZmlsO21pbWU9O11Xb3JkcGVyZmVjdCBvdmVybGF5IGZpbGUNCiY5CWJ5dGUJMjEJDQo+MTAJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjExCWJ5dGUJeAkuJWQNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlQTUNDCVtmaWQ9MDAwMDAxMDAxLTAwLTAwMDBHUlA7ZXh0PWdycDttaW1lPTtdTWljcm9zb2Z0IHdpbmRvd3MgZ3JvdXAgZmlsZQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE0IGJ5IENhcmwNCjAJYmVzaG9ydAkweEUzMTAJW2ZpZD0wMDAwMDEwMDctMDAtMDAwSU5GTztleHQ9aW5mbzttaW1lPTtdQW1pZ2Egc2hvcnRjdXQgLyBpY29uIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgV29yZHBlcmZlY3QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE0IGJ5IENhcmwNCjAJc3RyaW5nCVxceEZGV1BDCVtmaWQ9MDAwMDAxMDA4LTAwLTAwMDBJTlM7ZXh0PWluczttaW1lPTtdV29yZHBlcmZlY3QgaW5zdGFsbGF0aW9uIGluZm9ybWF0aW9uIGZpbGUNCiY5CWJ5dGUJNDMJDQo+MTAJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjExCWJ5dGUJeAkuJWQNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCWxlbG9uZwkweDAwMDAwMDRDCVtmaWQ9MDAwMDAxMDAxLTAwLTAwMDBMTks7ZXh0PWxuazttaW1lPTtdTWljcm9zb2Z0IFdpbmRvd3Mgc2hvcnRjdXQgZmlsZQ0KJjQJc3RyaW5nCVxceDAxXFx4MTRcXHgwMgkNCg0KIyBNYWdpYyBJRCBmb3IgV29yZHBlcmZlY3QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE0IGJ5IENhcmwNCjAJc3RyaW5nCVxceEZGV1BDCVtmaWQ9MDAwMDAxMDA4LTAwLTAwMDBQUlM7ZXh0PXByczttaW1lPTtdV29yZHBlcmZlY3QgcHJpbnRlciByZXNvdXJjZSBmaWxlDQomOQlieXRlCTE2CQ0KPjEwCWJ5dGUJeAksIHZlcnNpb24gJWQNCj4xMQlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwUVJTO2V4dD1xcnM7bWltZT07XVdvcmRwZXJmZWN0IDUuMSBlcXVhdGlvbiByZXNvdXJjZSBmaWxlDQomOQlieXRlCTMwCQ0KPjEwCWJ5dGUJeAksIHZlcnNpb24gJWQNCj4xMQlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwU0VUO2V4dD1zZXQ7bWltZT07XVdvcmRwZXJmZWN0IHNldHVwIGRhdGENCiY5CWJ5dGUJMTcJDQo+MTAJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjExCWJ5dGUJeAkuJWQNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0wOCBieSBDYXJsDQowCXN0cmluZwlSSUZGCVtmaWQ9MDAwMDAxMDAxLTBFLTAwMDBQQUw7ZXh0PXBhbCxyaWZmO21pbWU9O11NaWNyb3NvZnQgUGFsZXR0ZSwgbGl0dGxlLWVuZGlhbg0KJjgJc3RyaW5nCVBBTAkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0wOCBieSBDYXJsDQowCXN0cmluZwlSSUZYCVtmaWQ9MDAwMDAxMDAxLTBFLTAwMDBQQUw7ZXh0PXBhbCxyaWZ4O21pbWU9O11NaWNyb3NvZnQgUGFsZXR0ZSwgYmlnLWVuZGlhbg0KJjgJc3RyaW5nCVBBTAkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNCBieSBDYXJsDQowCXN0cmluZwlET1MJW2ZpZD0wMDAwMDEwMDctMEYtMDAwMEFERjtleHQ9YWRmO21pbWU9O11BbWlnYU9TIEZpbGUgc3lzdGVtDQomMwlieXRlJjB4ZjgJMAkNCj4zCWJ5dGUmMQkwCSwgT0ZTDQo+MwlieXRlJjEJMQksIEZGUw0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA1LTExIGJ5IENhcmwNCjAJYmVsb25nCTB4MDNGMwlbZmlkPTAwMDAwMTAwNy0xMC1MSUJSQVJZO2V4dD0sbGlicmFyeTttaW1lPTtdQW1pZ2EgQ2xhc3NpYyBleGVjdXRhYmxlIGZpbGUgKDY4MHgwIGZhbWlseSkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQowCXN0cmluZwlcXHg3ZkVMRglbZmlkPTAwMDAwMDAwMy0xMC0wMDAwMDBPO2V4dD0sbyxzbyxvdXQ7bWltZT07XUV4ZWN1dGFibGUgbGlua2FibGUgZmlsZSAoRUxGKQ0KJjQJYnl0ZQk9MQksIDMyLWJpdA0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTExIGJ5IENhcmwNCjAJc3RyaW5nCVxceDdmRUxGCVtmaWQ9MDAwMDAwMDAzLTEwLTAwMDAwME87ZXh0PSxvLHNvLG91dDttaW1lPTtdRXhlY3V0YWJsZSBsaW5rYWJsZSBmaWxlIChFTEYpDQomNAlieXRlCT0yCSwgNjQtYml0DQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMTQgYnkgQ2FybA0KMAlzdHJpbmcJTVoJW2ZpZD0wMDAwMDEwMDEtMTAtMDAwMEVYRTtleHQ9ZXhlLGRsbDttaW1lPTtdTmV3IGV4ZWN1dGFibGUgZmlsZQ0KJjB4MTgJbGVzaG9ydAk+MHgzRgkNCiYoNjAubCkJc3RyaW5nCU5FCQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTE0IGJ5IENhcmwNCjAJc3RyaW5nCVpNCVtmaWQ9MDAwMDAxMDAxLTEwLTAwMDBFWEU7ZXh0PWV4ZSxkbGw7bWltZT07XU5ldyBleGVjdXRhYmxlIGZpbGUNCiYweDE4CWxlc2hvcnQJPjB4M0YJDQomKDYwLmwpCXN0cmluZwlORQkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0xNCBieSBDYXJsDQowCXN0cmluZwlNWglbZmlkPTAwMDAwMTAwMS0xMC0wMDAwRVhFO2V4dD1leGUsZGxsO21pbWU9O11NaWNyb3NvZnQgV2luZG93cyAzLnggTmV3IEV4ZWN1dGFibGUgZmlsZQ0KJjB4MTgJbGVzaG9ydAk+MHgzRgkNCiYoNjAubCkJc3RyaW5nCU5FCQ0KJig2MC5sKzU0KQlieXRlCTB4MDIJDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMTQgYnkgQ2FybA0KMAlzdHJpbmcJWk0JW2ZpZD0wMDAwMDEwMDEtMTAtMDAwMEVYRTtleHQ9ZXhlLGRsbDttaW1lPTtdTWljcm9zb2Z0IFdpbmRvd3MgMy54IE5ldyBFeGVjdXRhYmxlIGZpbGUNCiYweDE4CWxlc2hvcnQJPjB4M0YJDQomKDYwLmwpCXN0cmluZwlORQkNCiYoNjAubCs1NCkJYnl0ZQkweDAyCQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTE0IGJ5IENhcmwNCjAJc3RyaW5nCU1aCVtmaWQ9MDAwMDAxMDA5LTEwLTAwMDBFWEU7ZXh0PWV4ZSxkbGw7bWltZT07XUlCTSBPUy8yIE5ldyBFeGVjdXRhYmxlIGZpbGUNCiYweDE4CWxlc2hvcnQJPjB4M0YJDQomKDYwLmwpCXN0cmluZwlORQkNCiYoNjAubCs1NCkJYnl0ZQkweDAxCQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTE0IGJ5IENhcmwNCjAJc3RyaW5nCVpNCVtmaWQ9MDAwMDAxMDA5LTEwLTAwMDBFWEU7ZXh0PWV4ZSxkbGw7bWltZT07XUlCTSBPUy8yIE5ldyBFeGVjdXRhYmxlIGZpbGUNCiYweDE4CWxlc2hvcnQJPjB4M0YJDQomKDYwLmwpCXN0cmluZwlORQkNCiYoNjAubCs1NCkJYnl0ZQkweDAxCQ0KDQojIE1hZ2ljIElEIGZvciBNaWNyb3NvZnQgV2luZG93cyBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMTMgYnkgQ2FybA0KMAlzdHJpbmcJTVoJW2ZpZD0wMDAwMDEwMDEtMTAtMDAwMEVYRTtleHQ9ZXhlLGRsbDttaW1lPTtdTWljcm9zb2Z0IFdpbmRvd3MgTlQgUG9ydGFibGUgRXhlY3V0YWJsZSBmaWxlDQomMHgxOAlsZXNob3J0CTB4NDAJDQomKDYwLmwpCXN0cmluZwlQRVxceDAwXFx4MDAJDQoNCiMgTWFnaWMgSUQgZm9yIE1pY3Jvc29mdCBXaW5kb3dzIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0xMyBieSBDYXJsDQowCXN0cmluZwlaTQlbZmlkPTAwMDAwMTAwMS0xMC0wMDAwRVhFO2V4dD1leGUsZGxsO21pbWU9O11NaWNyb3NvZnQgV2luZG93cyBOVCBQb3J0YWJsZSBFeGVjdXRhYmxlIGZpbGUNCiYweDE4CWxlc2hvcnQJMHg0MAkNCiYoNjAubCkJc3RyaW5nCVBFXFx4MDBcXHgwMAkNCg0KIyBNYWdpYyBJRCBmb3IgTWljcm9zb2Z0IFdpbmRvd3MsRE9TNEdXIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0xNCBieSBDYXJsDQowCXN0cmluZwlNWglbZmlkPTAwMDAwMTAwMS0xMC0wMDAwRVhFO2V4dD1leGUsZGxsLGRydjttaW1lPTtdTWljcm9zb2Z0IFdpbmRvd3MgTGluZWFyIGV4ZWN1dGFibGUNCiYweDE4CWxlc2hvcnQJPjB4M0YJDQomKDYwLmwpCXN0cmluZwlMRQkNCg0KIyBNYWdpYyBJRCBmb3IgTWljcm9zb2Z0IFdpbmRvd3MsRE9TNEdXIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0xNCBieSBDYXJsDQowCXN0cmluZwlaTQlbZmlkPTAwMDAwMTAwMS0xMC0wMDAwRVhFO2V4dD1leGUsZGxsLGRydjttaW1lPTtdTWljcm9zb2Z0IFdpbmRvd3MgTGluZWFyIGV4ZWN1dGFibGUNCiYweDE4CWxlc2hvcnQJPjB4M0YJDQomKDYwLmwpCXN0cmluZwlMRQkNCg0KIyBNYWdpYyBJRCBmb3IgT1MvMixET1M0R1cgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTE0IGJ5IENhcmwNCjAJc3RyaW5nCU1aCVtmaWQ9MDAwMDAxMDA5LTEwLTAwMDBFWEU7ZXh0PWV4ZSxkbGwsZHJ2O21pbWU9O11PUy8yIExpbmVhciBleGVjdXRhYmxlDQomMHgxOAlsZXNob3J0CT4weDNGCQ0KJig2MC5sKQlzdHJpbmcJTFgJDQoNCiMgTWFnaWMgSUQgZm9yIE9TLzIsRE9TNEdXIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0xNCBieSBDYXJsDQowCXN0cmluZwlaTQlbZmlkPTAwMDAwMTAwOS0xMC0wMDAwRVhFO2V4dD1leGUsZGxsLGRydjttaW1lPTtdT1MvMiBMaW5lYXIgZXhlY3V0YWJsZQ0KJjB4MTgJbGVzaG9ydAk+MHgzRgkNCiYoNjAubCkJc3RyaW5nCUxYCQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA3LTMwIGJ5IENhcmwNCjAJc3RyaW5nCU1TRlQJW2ZpZD0wMDAwMDEwMDEtMTAtMDAwMFRMQjtleHQ9dGxiO21pbWU9O11NaWNyb3NvZnQgY29tcG9uZW50IHR5cGUgbGlicmFyeQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA1LTExIGJ5IENhcmwNCjAJYmVzaG9ydAkweDYwMUEJW2ZpZD0wMDAwMDEwMDYtMTAtMDAwMFRUUDtleHQ9dHRwLGdlbSxwcmc7bWltZT07XUF0YXJpIE1pTlQgZXhlY3V0YWJsZS9vYmplY3QgZmlsZQ0KJjB4MTIJc3RyaW5nCU1pTlQJDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDUtMTEgYnkgQ2FybA0KMAliZXNob3J0CTB4NjAxQQlbZmlkPTAwMDAwMTAwNi0xMC0wMDAwVFRQO2V4dD10dHAsZ2VtLHByZzttaW1lPTtdQXRhcmkgVE9TIGV4ZWN1dGFibGUvb2JqZWN0IGZpbGUNCiYweDEyCWJlbG9uZwkweDAwMDAJDQoNCiMgTWFnaWMgSUQgZm9yIFZpcnR1YWwgUGFzY2FsIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNy0zMCBieSBDYXJsDQowCXN0cmluZwlWUEkJW2ZpZD0wMDAwMDAwMDAtMTAtMDAwMFZQSTtleHQ9dnBpO21pbWU9O11WaXJ0dWFsIHBhc2NhbCB1bml0IGZpbGUNCiYweDAzCWJ5dGUJPjQ3CQ0KJjB4MDMJYnl0ZQk8NTgJDQoNCiMgTWFnaWMgSUQgZm9yIEphdmEgY29tcGlsZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA1LTExIGJ5IENhcmwNCjAJYmVsb25nCTB4Q0FGRUJBQkUJW2ZpZD0wMDAwMDEwMTEtMTEtMDBDTEFTUztleHQ9Y2xhc3M7bWltZT07XUphdmFsIHZpcnR1YWwgbWFjaGluZSBjbGFzcyBmaWxlDQo+NgliZXNob3J0CXgJLCB2ZXJzaW9uICVkDQo+NAliZXNob3J0CXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIEJvcmxhbmQgRGVscGhpIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNy0zMCBieSBDYXJsDQowCXN0cmluZwlQS0cJW2ZpZD0wMDAwMDEwMDUtMTEtMDAwMERDUDtleHQ9ZGNwO21pbWU9O11Cb3JsYW5kIERlbHBoaSBjb21waWxlZCBwYWNrYWdlIGNvZGUgZmlsZQ0KJjB4MDMJYnl0ZQk+NDcJDQomMHgwMwlieXRlCTw1OAkNCg0KIyBNYWdpYyBJRCBmb3IgQm9ybGFuZCBEZWxwaGkgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA3LTMwIGJ5IENhcmwNCjAJc3RyaW5nCVxceERGXFx4MDBcXHgwMFxceDBGCVtmaWQ9MDAwMDAxMDA1LTExLTAwMDBEQ1U7ZXh0PWRjdTttaW1lPTtdQm9ybGFuZCBEZWxwaGkgY29kZSB1bml0IGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgVHVyYm8gUGFzY2FsIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNy0yOSBieSBDYXJsDQowCXN0cmluZwlUUFU5CVtmaWQ9MDAwMDAxMDA1LTExLTAwMDBUUFU7ZXh0PXRwdTttaW1lPTtdVHVyYm8gUGFzY2FsIDYuMCBjb2RlIHVuaXQgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBUdXJibyBQYXNjYWwsIEJvcmxhbmQgUGFzY2FsIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNy0yOSBieSBDYXJsDQowCXN0cmluZwlUUFVRCVtmaWQ9MDAwMDAxMDA1LTExLTAwMDBUUFU7ZXh0PXRwdSx0cHAsdHB3O21pbWU9O11Cb3JsYW5kIFBhc2NhbCA3LjAgY29kZSB1bml0IGZpbGUNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQoyCXN0cmluZwlBRExJQi0JW2ZpZD0wMDAwMDEwMTYtMjAtMDAwMEJOSztleHQ9Ym5rO21pbWU9O11BZGxpYiBGTSBpbnN0cnVtZW50IGJhbmsgZmlsZQ0KPjAJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjEJYnl0ZQl4CS4lZA0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE2IGJ5IENhcmwNCjAJc3RyaW5nCUlCS1xceDFBCVtmaWQ9MDAwMDAxMDEzLTIwLTAwMDBJQks7ZXh0PWliazttaW1lPTtdQ3JlYXRpdmUgTGFicyBGTSBpbnN0cnVtZW50IGJhbmsgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBNaWNyb3NvZnQgSW5zdHJ1bWVudCBEZWZpbml0aW9uIGZpbGUgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAzLTA0IGJ5IENhcmwNCjAJc3RyaW5nCVJJRkYJW2ZpZD0wMDAwMDEwMDEtMjAtMDAwMElERjtleHQ9aWRmO21pbWU9O11NaWNyb3NvZnQgaW5zdHJ1bWVudCBkZWZpbml0aW9uIGZpbGUsIGxpdHRsZS1lbmRpYW4NCiY4CXN0cmluZwlJREZcXCAJDQoNCiMgTWFnaWMgSUQgZm9yIE1pY3Jvc29mdCBJbnN0cnVtZW50IERlZmluaXRpb24gZmlsZSBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDQgYnkgQ2FybA0KMAlzdHJpbmcJUklGWAlbZmlkPTAwMDAwMTAwMS0yMC0wMDAwSURGO2V4dD1pZGY7bWltZT07XU1pY3Jvc29mdCBpbnN0cnVtZW50IGRlZmluaXRpb24gZmlsZSwgYmlnLWVuZGlhbg0KJjgJc3RyaW5nCUlERlxcIAkNCg0KIyBNYWdpYyBJRCBmb3IgRGlnaXRyYWtrZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAzLTA0IGJ5IENhcmwNCjAJc3RyaW5nCURJU1QJW2ZpZD0wMDAxMDAwODgtMjAtMDAwMElTVDtleHQ9aXN0O21pbWU9O11EaWdpdHJha2tlciBJbnN0cnVtZW50IGZpbGUNCiY0CWJ5dGUJPDIJDQoNCiMgTWFnaWMgSUQgZm9yIEltcHVsc2UgdHJhY2tlciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDQgYnkgQ2FybA0KMAlzdHJpbmcJSU1QSQlbZmlkPTAwMDEwMDAzMi0yMC0wMDAwSVRJO2V4dD1pdGk7bWltZT07XUltcHVsc2UgdHJhY2tlciBpbnN0cnVtZW50IGZpbGUNCj4weDIwCXN0cmluZwl4CVt0aXRsZT0lLjI2c10NCg0KIyBNYWdpYyBJRCBmb3IgTWFkdHJhY2tlciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDUgYnkgQ2FybA0KMAlzdHJpbmcJTUkyMQlbZmlkPTAwMDEwMDA5MS0yMC0wMDAwTVRJO2V4dD1tdGk7bWltZT07XU1hZHRyYWNrZXIgaW5zdHJ1bWVudCBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIEdyYXZpcyBVbHRyYXNvdW5kIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQowCXN0cmluZwlHRjFQQVRDSDEwMFxcMElEIzAwMDAwMlxcMAlbZmlkPTAwMDAwMTAxOC0yMC0wMDAwUEFUO2V4dD1wYXQ7bWltZT07XUdyYXZpcyBVbHRyYXNvdW5kIFBhdGNoIChvbGQgaW5zdHJ1bWVudCBkYXRhKQ0KDQojIE1hZ2ljIElEIGZvciBHcmF2aXMgVWx0cmFzb3VuZCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTUgYnkgQ2FybA0KMAlzdHJpbmcJR0YxUEFUQ0gxMTBcXDBJRCMwMDAwMDJcXDAJW2ZpZD0wMDAwMDEwMTgtMjAtMDAwMFBBVDtleHQ9cGF0O21pbWU9O11HcmF2aXMgVWx0cmFzb3VuZCBQYXRjaCAoaW5zdHJ1bWVudCBkYXRhKQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE1IGJ5IENhcmwNCjAJc3RyaW5nCVNCSVxceDFBCVtmaWQ9MDAwMDAxMDEzLTIwLTAwMDBTQkk7ZXh0PXNiaTttaW1lPTtdQ3JlYXRpdmUgTGFicyBGTSBpbnN0cnVtZW50IGRhdGEgZmlsZQ0KPjQJc3RyaW5nCT4wCVt0aXRsZT0lLjMyc10NCg0KIyBNYWdpYyBJRCBmb3IgU2lkcGxheSBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDUgYnkgQ2FybA0KMAlzdHJpbmcJU0lEUExBWVxcIElORk9GSUxFCVtmaWQ9MDAwMDAwMDAwLTIwLTAwMDBTSUQ7ZXh0PXNpZDttaW1lPTtdU0lEUGxheWVyIG11c2ljIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgRmFzdHRyYWNrZXIgMi4wIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQowCXN0cmluZwlFeHRlbmRlZFxcIEluc3RydW1lbnQ6XFwgCVtmaWQ9MDAwMTAwMDI2LTIwLTAwMDAwWEk7ZXh0PXhpO21pbWU9O11GYXN0VHJhY2tlciBJSSBpbnN0cnVtZW50IGZpbGUNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQowCXN0cmluZwlSSUZGCVtmaWQ9MDAwMDAxMDAxLTIxLTAwMDAwMDA7ZXh0PTttaW1lPTtdTUlESSBtdXNpYyBmaWxlLCBsaXR0bGUtZW5kaWFuDQomOAlzdHJpbmcJUk1JRAkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQowCXN0cmluZwlSSUZYCVtmaWQ9MDAwMDAxMDAxLTIxLTAwMDAwMDA7ZXh0PTttaW1lPTtdTUlESSBtdXNpYyBmaWxlLCBiaWctZW5kaWFuDQomOAlzdHJpbmcJUk1JRAkNCg0KIyBNYWdpYyBJRCBmb3IgQWJ5c3NcJ3MgaGlnaGVzdCBleHBlcmllbmNlIChBSFgpIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQowCXN0cmluZwlUSFgJW2ZpZD0wMDAxMDAwMjktMjEtMDAwMEFIWDtleHQ9YWh4O21pbWU9O11BSFggbW9kdWxlIG11c2ljIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgQW11c2ljIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQoxMDYyCXN0cmluZwlcXHgzY1xceDZmXFx4ZWZcXHg1MVxceDU1XFx4RUVcXHg1MlxceDZGXFx4NTIJW2ZpZD0wMDAxMDAwMzQtMjEtMDAwMEFNRDtleHQ9YW1kO21pbWU9O11BbXVzaWMgQWRsaWIgdHJhY2tlciBtdXNpYyBmaWxlDQo+MAlzdHJpbmcJPlxceDAwCVt0aXRsZT0lLjIzc10NCj4yNAlzdHJpbmcJPlxcMAlbY3JlYXRvcj0lLjI0c10NCg0KIyBNYWdpYyBJRCBmb3IgVmVsdmV0IFN0dWRpbyBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTUgYnkgQ2FybA0KMAlzdHJpbmcJQU1TaGRyXFx4MWEJW2ZpZD0wMDAwMDEyNzYtMjEtMDAwMEFNUztleHQ9YW1zO21pbWU9O11WZWx2ZXQgU3R1ZGlvIG1vZHVsZSBtdXNpYyBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIEF1ZGlvIHZpc3VhbCByZXNlYXJjaCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDQgYnkgQ2FybA0KMAlzdHJpbmcJMkJJVAlbZmlkPTAwMDAwMTMwMS0yMS0wMDAwQVZSO2V4dD1hdnI7bWltZT07XUF1ZGlvIHZpc3VhbCByZXNlYXJjaCBhdWRpbyBmaWxlDQo+NAlzdHJpbmcJeAlbdGl0bGU9JS44c10NCg0KIyBNYWdpYyBJRCBmb3IgU291bmRtb24gZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE1IGJ5IENhcmwNCjI2CXN0cmluZwlWLjIJW2ZpZD0wMDAxMDAwMjgtMjEtMDAwMDBCUDtleHQ9YnA7bWltZT07XVNvdW5kbW9uIG1vZHVsZSBtdXNpYyBmaWxlLCB2ZXJzaW9uIDIueA0KPjAJc3RyaW5nCT5cXDAJW3RpdGxlPSUuMjZzXQ0KDQojIE1hZ2ljIElEIGZvciBTb3VuZG1vbiBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTUgYnkgQ2FybA0KMjYJc3RyaW5nCVYuMwlbZmlkPTAwMDEwMDAyOC0yMS0wMDAwQlAzO2V4dD1icDM7bWltZT07XVNvdW5kbW9uIG1vZHVsZSBtdXNpYyBmaWxlLCB2ZXJzaW9uIDMueA0KPjAJc3RyaW5nCT5cXDAJW3RpdGxlPSUuMjZzXQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE1IGJ5IENhcmwNCjAJc3RyaW5nCUNUTUYJW2ZpZD0wMDAwMDEwMTMtMjEtMDAwMENNRjtleHQ9Y21mO21pbWU9O11DcmVhdGl2ZSBMYWJzIG11c2ljIGZpbGUNCj40CWJ5dGUJeAksIHZlcnNpb24gJWQNCj41CWJ5dGUJeAkuJWQNCg0KIyBNYWdpYyBJRCBmb3IgRGlnaWJvb3N0ZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAzLTA0IGJ5IENhcmwNCjAJc3RyaW5nCURJR0kgQm9vc3RlciBtb2R1bGVcXDAJW2ZpZD0wMDAwMDEzMDItMjEtMDAwRElHSTtleHQ9ZGlnaTttaW1lPTtdRGlnaWJvb3N0ZXIgbXVzaWMgZmlsZQ0KPjYxMAlzdHJpbmcJeAlbdGl0bGU9JS4zMnNdDQoNCiMgTWFnaWMgSUQgZm9yIERlbHVzaW9uIFh0cmFja2VyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQowCXN0cmluZwlERE1GCVtmaWQ9MDAwMDAwMDAwLTIxLTAwMDBETUY7ZXh0PWRtZjttaW1lPTtdRGVsdXNpb24gdHJhY2tlciBtb2R1bGUgbXVzaWMgZmlsZQ0KPjQJYnl0ZQl4CSwgdmVyc2lvbiAlZC4wDQo+MTMJc3RyaW5nCT5cXDAJW3RpdGxlPSUuMzBzXQ0KPjQzCXN0cmluZwk+XFwwCVtjcmVhdG9yPSUuMjBzXQ0KDQojIE1hZ2ljIElEIGZvciBET1MgU291bmQgaW50ZXJmYWNlIGtpdCAoRFNJSykgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE1IGJ5IENhcmwNCjAJc3RyaW5nCVJJRkYJW2ZpZD0wMDAwMDAwMDAtMjEtMDAwMERTTTtleHQ9ZHNtO21pbWU9O11ET1MgU291bmQgaW50ZXJmYWNlIGtpdCBtb2R1bGUgbXVzaWMgZmlsZQ0KJjgJc3RyaW5nCURTTUYJDQoNCiMgTWFnaWMgSUQgZm9yIEVkbGliIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNyBieSBDYXJsDQowCXN0cmluZwlcXHgwMFxceDA2XFx4RkVcXHhGRAlbZmlkPTAwMDEwMDAyNy0yMS0wMDAwRURMO2V4dD1lZGw7bWltZT07XUVkbGliIEZNIHRyYWNrZXIgbXVzaWMgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBRdWFkcmEgQ29tcG9zZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAzLTA0IGJ5IENhcmwNCjAJc3RyaW5nCUZPUk0JW2ZpZD0wMDAxMDAwODUtMjEtMDAwMEVNRDtleHQ9ZW1kO21pbWU9O11FbmhhbmNlZCBtb2R1bGUgbXVzaWMgZmlsZSAoSUZGKQ0KJjgJc3RyaW5nCUVNT0QJDQoNCiMgTWFnaWMgSUQgZm9yIEZhcmFuZG9sZSBjb21wb3NlciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTUgYnkgQ2FybA0KMAlzdHJpbmcJRkFSXFx4RkUJW2ZpZD0wMDAxMDAwODctMjEtMDAwMEZBUjtleHQ9ZmFyO21pbWU9O11GYXJhbmRvbGUgY29tcG9zZXIgbW9kdWxlIG11c2ljIGZpbGUNCj40CXN0cmluZwk+XFwwCVt0aXRsZT0lLjQwc10NCg0KIyBNYWdpYyBJRCBmb3IgRnVua3RyYWNrZXIgR29sZCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDQgYnkgQ2FybA0KMAlzdHJpbmcJRnVuawlbZmlkPTAwMDEwMDA4Ni0yMS0wMDAwRk5LO2V4dD1mbms7bWltZT07XUZ1bmt0cmFja2VyIEdvbGQgbXVzaWMgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBCZWxscywgV2hpc3RsZXMsIGFuZCBTb3VuZCBCb2FyZHMgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE2IGJ5IENhcmwNCjAJc3RyaW5nCUdETVxceEZFCVtmaWQ9MDAwMDAxMjgwLTIxLTAwMDBHRE07ZXh0PWdkbTttaW1lPTtdR2VuZXJhbCBEaWdpTXVzaWMgbW9kdWxlIG11c2ljIGZpbGUNCj40CXN0cmluZwk+XFx4MDAJW3RpdGxlPSUuMzJzXQ0KDQojIE1hZ2ljIElEIGZvciBHcmFvdW1mIFRyYWNrZXIgMiBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTUgYnkgQ2FybA0KMAlzdHJpbmcJR1QyCVtmaWQ9MDAwMTAwMDMxLTIxLTAwMDBHVDI7ZXh0PWd0MjttaW1lPTtdR3Jhb3VtZiBUcmFja2VyIG1vZHVsZSBtdXNpYyBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIEltYWdvIE1vcnBoZXVzIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNCBieSBDYXJsDQoweDNDCXN0cmluZwlJTTEwCVtmaWQ9MDAwMDAxMjc5LTIxLTAwMDBJTUY7ZXh0PWltZjttaW1lPTtdSW1hZ28gbW9ycGhldXMgbXVzaWMgZmlsZSwgMzIgY2hhbm5lbHMNCj4wCXN0cmluZwl4CVt0aXRsZT0lLjMxc10NCg0KIyBNYWdpYyBJRCBmb3IgSW1wdWxzZSB0cmFja2VyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQowCXN0cmluZwlJTVBNCVtmaWQ9MDAwMTAwMDMyLTIxLTAwMDAwSVQ7ZXh0PWl0O21pbWU9O11JbXB1bHNlIFRyYWNrZXIgbW9kdWxlIG11c2ljIGZpbGUNCj40CXN0cmluZwk+XFx4MDAJW3RpdGxlPSUuMjZzXQ0KDQojIE1hZ2ljIElEIGZvciBKYW1jcmFja2VyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNCBieSBDYXJsDQowCXN0cmluZwlCZUVwCVtmaWQ9MDAwMDAwMDAwLTIxLTAwMDBKQU07ZXh0PWphbTttaW1lPTtdSmFtY3JhY2tlciB0cmFja2VyIG1vZHVsZSBtdXNpYyBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIExpcXVpZCB0cmFja2VyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNCBieSBDYXJsDQowCXN0cmluZwlMaXF1aWQgTW9kdWxlOglbZmlkPTAwMDEwMDA5MC0yMS0wMDAwTElRO2V4dD1saXE7bWltZT07XUxpcXVpZCB0cmFja2VyIG1vZHVsZSBtdXNpYyBmaWxlDQomMHg0MAlieXRlCTB4MUEJDQo+MHgwRQlzdHJpbmcJPlxcMAlbdGl0bGU9JS4zMHNdDQo+MHgyYwlzdHJpbmcJPlxcMAlbY3JlYXRvcj0lLjIwc10NCg0KIyBNYWdpYyBJRCBmb3IgRGlnaXRyYWtrZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAzLTA0IGJ5IENhcmwNCjAJc3RyaW5nCURNREwJW2ZpZD0wMDAxMDAwODgtMjEtMDAwME1ETDtleHQ9bWRsO21pbWU9O11EaWdpdHJha2tlciBtb2R1bGUgbXVzaWMgZmlsZQ0KJjQJYnl0ZQk8MHgxMgkNCg0KIyBNYWdpYyBJRCBmb3IgTUVEIFNvdW5kc3R1ZGlvIC8gT2N0YU1FRCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTUgYnkgQ2FybA0KMAlzdHJpbmcJTU1EMAlbZmlkPTAwMDAwMTI3OC0yMS0wMDAwTUVEO2V4dD1tZWQ7bWltZT07XU9jdGFtZWQgdHJhY2tlciBtb2R1bGUgbXVzaWMgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBNRUQgU291bmRzdHVkaW8gLyBPY3RhTUVEIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQowCXN0cmluZwlNTUQxCVtmaWQ9MDAwMDAxMjc4LTIxLTAwMDBNRUQ7ZXh0PW1lZDttaW1lPTtdT2N0YW1lZCBQcm8gVHJhY2tlciBtb2R1bGUgbXVzaWMgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBNRUQgU291bmRzdHVkaW8gLyBPY3RhTUVEIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQowCXN0cmluZwlNTUQyCVtmaWQ9MDAwMDAxMjc4LTIxLTAwMDBNRUQ7ZXh0PW1lZDttaW1lPTtdT2N0YW1lZCBQcm8gVHJhY2tlciBtb2R1bGUgbXVzaWMgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBNRUQgU291bmRzdHVkaW8gLyBPY3RhTUVEIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQowCXN0cmluZwlNTUQzCVtmaWQ9MDAwMDAxMjc4LTIxLTAwMDBNRUQ7ZXh0PW1lZDttaW1lPTtdT2N0YW1lZCBTb3VuZCBTdHVkaW8gbW9kdWxlIG11c2ljIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgTXVzaWNsaW5lIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNSBieSBDYXJsDQowCXN0cmluZwlNTEVETU9ETAlbZmlkPTAwMDAwMTMwNC0yMS0wMDAwME1MO2V4dD1tbDttaW1lPTtdTXVzaWNsaW5lIG1vZHVsZSBtdXNpYyBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIFByb3RyYWNrZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE1IGJ5IENhcmwNCjEwODAJc3RyaW5nCU0hSyEJW2ZpZD0wMDAxMDAwMjItMjEtMDAwME1PRDtleHQ9bW9kO21pbWU9O11Qcm90cmFja2VyIDIuMyBtb2R1bGUgbXVzaWMgZmlsZQ0KPjAJc3RyaW5nCT5cXDAJW3RpdGxlPSUuMjBzXQ0KDQojIE1hZ2ljIElEIGZvciBQcm90cmFja2VyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQoxMDgwCXN0cmluZwlNLksuCVtmaWQ9MDAwMTAwMDIyLTIxLTAwMDBNT0Q7ZXh0PW1vZDttaW1lPTtdUHJvdHJhY2tlciBtb2R1bGUgbXVzaWMgZmlsZQ0KPjAJc3RyaW5nCT5cXDAJW3RpdGxlPSUuMjBzXQ0KDQojIE1hZ2ljIElEIGZvciBQcm90cmFja2VyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNSBieSBDYXJsDQowCXN0cmluZwlGT1JNCVtmaWQ9MDAwMTAwMDIyLTIxLTAwMDBNT0Q7ZXh0PW1vZDttaW1lPTtdUHJvdHJhY2tlciBtb2R1bGUgbXVzaWMgZmlsZSwgdmVyc2lvbiAzLngNCiY4CXN0cmluZwlNT0RMCQ0KDQojIE1hZ2ljIElEIGZvciBTdGFydHJla2tlciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTUgYnkgQ2FybA0KMTA4MAlzdHJpbmcJRkxUNAlbZmlkPTAwMDEwMDAyNC0yMS0wMDAwTU9EO2V4dD1tb2Q7bWltZT07XVN0YXJ0cmVra2VyIG1vZHVsZSBtdXNpYyBmaWxlLCA0IGNoYW5uZWxzDQo+MAlzdHJpbmcJPlxcMAlbdGl0bGU9JS4yMHNdDQoNCiMgTWFnaWMgSUQgZm9yIFN0YXJ0cmVra2VyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQoxMDgwCXN0cmluZwlGTFQ4CVtmaWQ9MDAwMTAwMDI0LTIxLTAwMDBNT0Q7ZXh0PW1vZDttaW1lPTtdU3RhcnRyZWtrZXIgbW9kdWxlIG11c2ljIGZpbGUsIDggY2hhbm5lbHMNCj4wCXN0cmluZwk+XFwwCVt0aXRsZT0lLjIwc10NCg0KIyBNYWdpYyBJRCBmb3IgRmFzdHRyYWNrZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE1IGJ5IENhcmwNCjEwODAJc3RyaW5nCTZDSE4JW2ZpZD0wMDAwMDEyNzUtMjEtMDAwME1PRDtleHQ9bW9kO21pbWU9O11GYXN0dHJhY2tlciBtb2R1bGUgbXVzaWMgZmlsZSwgNiBjaGFubmVscw0KPjAJc3RyaW5nCT5cXDAJW3RpdGxlPSUuMjBzXQ0KDQojIE1hZ2ljIElEIGZvciBGYXN0dHJhY2tlciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTUgYnkgQ2FybA0KMTA4MAlzdHJpbmcJOENITglbZmlkPTAwMDAwMTI3NS0yMS0wMDAwTU9EO2V4dD1tb2Q7bWltZT07XUZhc3R0cmFja2VyIG1vZHVsZSBtdXNpYyBmaWxlLCA2IGNoYW5uZWxzDQo+MAlzdHJpbmcJPlxcMAlbdGl0bGU9JS4yMHNdDQoNCiMgTWFnaWMgSUQgZm9yIE1hZHRyYWNrZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAzLTA1IGJ5IENhcmwNCjAJc3RyaW5nCU1UMjAJW2ZpZD0wMDAxMDAwOTEtMjEtMDAwME1UMjtleHQ9bXQyO21pbWU9O11NYWR0cmFja2VyIG1vZHVsZSBtdXNpYyBmaWxlDQo+NDIJc3RyaW5nCXgJW3RpdGxlPSUuNjRzXQ0KPjExMglsZXNob3J0CXgJW2Nobj0lZF0NCg0KIyBNYWdpYyBJRCBmb3IgTXVsdGl0cmFja2VyIE1vZHVsZSBlZGl0b3IgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE1IGJ5IENhcmwNCjAJc3RyaW5nCU1UTQlbZmlkPTAwMDEwMDA4OS0yMS0wMDAwTVRNO2V4dD1tdG07bWltZT07XU11bHRpVHJhY2tlciBtb2R1bGUgbXVzaWMgZmlsZQ0KPjQJc3RyaW5nCT5cXHgwMAlbdGl0bGU9JS4yMHNdDQoNCiMgTWFnaWMgSUQgZm9yIE1hZHRyYWNrZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAzLTA1IGJ5IENhcmwNCjAJc3RyaW5nCU1UUDIJW2ZpZD0wMDAxMDAwOTEtMjEtMDAwME1UUDtleHQ9bXRwO21pbWU9O11NYWR0cmFja2VyIHBhdHRlcm4gZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBBL05FUyBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDcgYnkgQ2FybA0KMAlzdHJpbmcJTkVTQQlbZmlkPTAwMDEwMDA5NC0yMS0wMDAwTlNBO2V4dD1uc2E7bWltZT07XUEvTkVTIHJpcHBlZCBhdWRpbyBmaWxlDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDcgYnkgQ2FybA0KMAlzdHJpbmcJTkVTTVxceDFBCVtmaWQ9MDAwMTAwMDkzLTIxLTAwMDBOU0Y7ZXh0PW5zZjttaW1lPTtdTkVTIHJpcHBlZCBhdWRpbyBmaWxlDQo+NQlieXRlCXgJLCB2ZXJzaW9uICVkLjANCj4weDBFCXN0cmluZwl4CVt0aXRsZT0lLjMyc10NCj4weDJFCXN0cmluZwl4CVtjcmVhdG9yPSUuMzJzXQ0KDQojIE1hZ2ljIElEIGZvciBOb2lzZXRyYWNrZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE1IGJ5IENhcmwNCjEwODAJc3RyaW5nCU0mSyEJW2ZpZD0wMDAxMDAwMjMtMjEtMDAwME5TVDtleHQ9bnN0O21pbWU9O11Ob2lzZXRyYWNrZXIgbW9kdWxlIG11c2ljIGZpbGUNCj4wCXN0cmluZwk+XFwwCVt0aXRsZT0lLjIwc10NCg0KIyBNYWdpYyBJRCBmb3IgT2t0YWx5emVyIHRyYWNrZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE1IGJ5IENhcmwNCjAJc3RyaW5nCU9LVEFTT05HCVtmaWQ9MDAwMTAwMDMwLTIxLTAwMDBPS1Q7ZXh0PW9rdDttaW1lPTtdT2t0YWx5emVyIG1vZHVsZSBtdXNpYyBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIFNCU3R1ZGlvIHNvdW5kIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNSBieSBDYXJsDQowCXN0cmluZwlQQUNHCVtmaWQ9MDAwMTAwMDIwLTIxLTAwMDBQQUM7ZXh0PXBhYzttaW1lPTtdU0JTdHVkaW8gbW9kdWxlIG11c2ljIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgUG9seXRyYWNrZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE1IGJ5IENhcmwNCjQ0CXN0cmluZwlQVE1GCVtmaWQ9MDAwMDAwMDAwLTIxLTAwMDBQVE07ZXh0PXB0bTttaW1lPTtdUG9seSBUcmFja2VyIG1vZHVsZSBtdXNpYyBmaWxlDQo+MzgJbGVzaG9ydAk+MAlbY2huPSVkXQ0KPjAJc3RyaW5nCT5cXDAJW3RpdGxlPSUuMjhzXQ0KDQojIE1hZ2ljIElEIGZvciBSZWFsaXR5IEFkbGliIHRyYWNrZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE1IGJ5IENhcmwNCjAJc3RyaW5nCVJBRFxcIGJ5CVtmaWQ9MDAwMDAwMDAwLTIxLTAwMDBSQUQ7ZXh0PXJhZDttaW1lPTtdUmVhbGl0eSBBZGxpYiB0cmFja2VyIG11c2ljIGZpbGUNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQowCXN0cmluZwlSSUZGCVtmaWQ9MDAwMDAwMDAwLTIxLTAwMDBSTUk7ZXh0PXJtaTttaW1lPWFwcGxpY2F0aW9uL3ZuZC5tdXNpYy1uaWZmO11Tb25nIG5vdGF0aW9uIGRhdGEgZmlsZSwgbGl0dGxlLWVuZGlhbg0KJjgJc3RyaW5nCU5JRkYJDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTUgYnkgQ2FybA0KMAlzdHJpbmcJUklGWAlbZmlkPTAwMDAwMDAwMC0yMS0wMDAwUk1JO2V4dD1ybWk7bWltZT1hcHBsaWNhdGlvbi92bmQubXVzaWMtbmlmZjtdU29uZyBub3RhdGlvbiBkYXRhIGZpbGUsIGJpZy1lbmRpYW4NCiY4CXN0cmluZwlOSUZGCQ0KDQojIE1hZ2ljIElEIGZvciBBZGxpYiBWaXN1YWwgQ29tcG9zZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE1IGJ5IENhcmwNCjAJbGVzaG9ydAkweDAwMDAJW2ZpZD0wMDAwMDEwMTYtMjEtMDAwMFJPTDtleHQ9cm9sO21pbWU9O11BZGxpYiBtdXNpYyBmaWxlDQomMglsZXNob3J0CTB4MDAwNAkNCg0KIyBNYWdpYyBJRCBmb3IgU2NyZWFtdHJhY2tlciAzIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQoweDJDCXN0cmluZwlTQ1JNCVtmaWQ9MDAwMTAwMDI1LTIxLTAwMDBTM007ZXh0PXMzbTttaW1lPTtdU2NyZWFtIHRyYWNrZXIgbW9kdWxlIG11c2ljIGZpbGUNCj4weDJBCWxlc2hvcnQJPjAJLCB2ZXJzaW9uICVkLjANCj4wCXN0cmluZwk+XFwwCVt0aXRsZT0lLjI4c10NCg0KIyBNYWdpYyBJRCBmb3IgU3VycHJpc2UhIEFkbGliIFRyYWNrZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE1IGJ5IENhcmwNCjAJc3RyaW5nCVNBZFQJW2ZpZD0wMDAxMDAwMjEtMjEtMDAwMFNBMjtleHQ9c2EyO21pbWU9O11TdXJwcmlzZSBQcm9kdWN0aW9ucyBBZGxpYiB0cmFja2VyIG11c2ljIGZpbGUNCj40CWJ5dGUJeAksIHZlcnNpb24gMC4lZA0KDQojIE1hZ2ljIElEIGZvciBTb3VuZEZYIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQo2MAlzdHJpbmcJU09ORwlbZmlkPTAwMDAwMTI3Ny0yMS0wMDAwU0ZYO2V4dD1zZng7bWltZT07XVNvdW5kRlggVHJhY2tlciBtb2R1bGUgbXVzaWMgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBQbGF5U0lELCBTaWRwbGF5IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNSBieSBDYXJsDQowCXN0cmluZwlQU0lECVtmaWQ9MDAwMDAwMDAwLTIxLTAwMDBTSUQ7ZXh0PXNpZDttaW1lPTtdUGxheVNJRCBtdXNpYyBmaWxlDQo+MTYJc3RyaW5nCXgJW3RpdGxlPSUuMjBzXQ0KPjM2CXN0cmluZwl4CVtjcmVhdG9yPSUuMjBzXQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE1IGJ5IENhcmwNCjAJc3RyaW5nCU1UaGQJW2ZpZD0wMDAwMDEwMTctMjEtMDAwMFNNRjtleHQ9c21mLG1pZGk7bWltZT07XVN0YW5kYXJkIE1JREkgbXVzaWMgZmlsZQ0KPjEwCWJlc2hvcnQJPjAJW2Nobj0lZF0NCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNSBieSBDYXJsDQowCXN0cmluZwlGT1JNCVtmaWQ9MDAwMDAxMDEwLTIxLTAwMFNNVVM7ZXh0PXNtdXMsbXVzO21pbWU9O11JRkYgU2ltcGxlIE11c2ljYWwgU2NvcmUgZmlsZQ0KJjgJc3RyaW5nCVNNVVMJDQoNCiMgTWFnaWMgSUQgZm9yIFNuZHRvb2wyLG5lenBsYXkgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAzLTA1IGJ5IENhcmwNCjAJc3RyaW5nCVNORFxceDFBCVtmaWQ9MDAwMDAwMDAwLTIxLTAwMDBTTkQ7ZXh0PXNuZDttaW1lPTtdTmludGVuZG8gRW50ZXJ0YWlubWVudCBTeXN0ZW0gYXVkaW8gZmlsZSAoTkVTKQ0KJjQJYnl0ZQkzCSwgdmVyc2lvbiAlMy4wDQo+NQlieXRlCXgJW2Nobj0lZF0NCg0KIyBNYWdpYyBJRCBmb3IgaU5FUyBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDUgYnkgQ2FybA0KMAlzdHJpbmcJU05EXFx4MUEJW2ZpZD0wMDAxMDAwOTItMjEtMDAwMFNORDtleHQ9c25kO21pbWU9O11pTkVTIGVtdWxhdG9yIGF1ZGlvIGZpbGUNCiY0CWJ5dGUJMQksIHZlcnNpb24gJTEuMA0KPjUJYnl0ZQl4CVtjaG49JWRdDQoNCiMgTWFnaWMgSUQgZm9yIFNUTUlLIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQoweDE1CXN0cmluZwlTY3JlYW0hCVtmaWQ9MDAwMTAwMDI1LTIxLTAwMDBTVFg7ZXh0PXN0eDttaW1lPTtdU1RNSUsgbW9kdWxlIG11c2ljIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgVGhlIEZpbmFsIE11c2ljc3lzdGVtIGVYdGVuZGVkIChURk1YKSBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDcgYnkgQ2FybA0KMAlzdHJpbmcJVEZNWC1TT05HXFwgCVtmaWQ9MDAwMTAwMDk2LTIxLTAwMDBURlg7ZXh0PXRmeCx0Zm14O21pbWU9O11URk1YIHRyYWNrZXIgbW9kdWxlIG11c2ljIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgVWx0cmEgVHJhY2tlciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDcgYnkgQ2FybA0KMAlzdHJpbmcJTUFTX1VUcmFja19WMDAJW2ZpZD0wMDAxMDAwOTctMjEtMDAwMFVMVDtleHQ9dWx0O21pbWU9O11VbHRyYSBUcmFja2VyIG1vZHVsZSBtdXNpYyBmaWxlDQo+MTUJc3RyaW5nCXgJW3RpdGxlPSUuMzJzXQ0KDQojIE1hZ2ljIElEIGZvciBBUGxheWVyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQowCXN0cmluZwlBUFVOXFx4MDEJW2ZpZD0wMDAwMDAwMDAtMjEtMDAwMFVOSTtleHQ9dW5pO21pbWU9O11BUGxheWVyIG1vZHVsZSBtdXNpYyBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIE1pa21vZCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTUgYnkgQ2FybA0KMAlzdHJpbmcJVU4wCVtmaWQ9MDAwMDAwMDAwLTIxLTAwMDBVTkk7ZXh0PXVuaTttaW1lPTtdTWlrbW9kIG1vZHVsZSBtdXNpYyBmaWxlDQo+NAlieXRlCXgJW2Nobj0lZF0NCg0KIyBNYWdpYyBJRCBmb3IgRmFzdHRyYWNrZXIgMi4wIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQowCXN0cmluZwlFeHRlbmRlZFxcIE1vZHVsZTpcXCAJW2ZpZD0wMDAxMDAwMjYtMjEtMDAwMDBYTTtleHQ9eG07bWltZT07XUZhc3RUcmFja2VyIElJIG1vZHVsZSBtdXNpYyBmaWxlDQo+NTkJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjU4CWJ5dGUJeAkuMCVkDQo+MTcJc3RyaW5nCXgJW3RpdGxlPSUuMjBzXQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTExIGJ5IENhcmwNCjAJc3RyaW5nCUZPUk0JW2ZpZD0wMDAwMDEwMTAtMjItMDAwOFNWWDtleHQ9OHN2eDttaW1lPTtdQW1pZ2EgU2FtcGxlZCBhdWRpbyBmaWxlDQomOAlzdHJpbmcJOFNWWAkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQowCXN0cmluZwlGT1JNCVtmaWQ9MDAwMDAxMDAyLTIyLTAwMEFJRkM7ZXh0PWFpZmMsYWlmO21pbWU9O11BdWRpbyBDb21wcmVzc2VkIEludGVyY2hhbmdlIEZpbGUgRm9ybWF0DQomOAlzdHJpbmcJQUlGQwkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQowCXN0cmluZwlGT1JNCVtmaWQ9MDAwMDAxMDAyLTIyLTAwMEFJRkY7ZXh0PWFpZmYsYWlmO21pbWU9O11BdWRpbyBJbnRlcmNoYW5nZSBGaWxlIEZvcm1hdA0KJjgJc3RyaW5nCUFJRkYJDQoNCiMgTWFnaWMgSUQgZm9yIE1vbmtleUF1ZGlvIHNvZnR3YXJlIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNS0yMCBieSBDYXJsDQowCXN0cmluZwlNQUNcXCAJW2ZpZD0wMDAxMDAxMjAtMjItMDAwMEFQRTtleHQ9YXBlO21pbWU9O11Nb25rZXlBdWRpbyBjb21wcmVzc2VkIGF1ZGlvIGZpbGUNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQowCXN0cmluZwkuc25kCVtmaWQ9MDAwMDAxMDExLTIyLTAwMDAwQVU7ZXh0PWF1LHNuZDttaW1lPTtdU3VuIC8gTmVYdCBzYW1wbGVkIGF1ZGlvIGZpbGUNCj4xNgliZWxvbmcJPjAJW2ZyZXE9JWRdDQoNCiMgTWFnaWMgSUQgZm9yIFNwcGFjayBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDQgYnkgQ2FybA0KMjUyCWJlc2hvcnQJMHg0MEMzCVtmaWQ9MDAwMDAxMDE5LTIyLTAwMDAwMEQ7ZXh0PWQ7bWltZT07XVNwcGFjayBhdWRpbyBzYW1wbGUgZmlsZQ0KJjI1NAliZXNob3J0CTB4RkMwRQkNCg0KIyBNYWdpYyBJRCBmb3IgRmxhYyBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDggYnkgQ2FybA0KMAlzdHJpbmcJZkxhQwlbZmlkPTAwMDEwMDA5OC0yMi0wMDBGTEFDO2V4dD1mbGFjO21pbWU9O11GcmVlIExvc3NsZXNzIEF1ZGlvIENvZGVjIHJhdyBhdWRpbyBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIEZhcmFuZG9sZSBDb21wb3NlciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDQgYnkgQ2FybA0KMAlzdHJpbmcJRlNNXFx4RkUJW2ZpZD0wMDAxMDAwODctMjItMDAwMEZTTTtleHQ9ZnNtO21pbWU9O11GYXJhbmRvbGUgY29tcG9zZXIgYXVkaW8gc2FtcGxlIGZpbGUNCj40CXN0cmluZwl4CVt0aXRsZT0lLjMyc10NCg0KIyBNYWdpYyBJRCBmb3IgTUFVRCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDQgYnkgQ2FybA0KMAlzdHJpbmcJRk9STQlbZmlkPTAwMDAwMDAwMC0yMi0wMDBNQVVEO2V4dD1tYXVkO21pbWU9O11NQVVEIGF1ZGlvIHNhbXBsZSBmaWxlDQomNAlzdHJpbmcJTUFVRAkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNS0wNCBieSBDYXJsDQowCWJlc2hvcnQmMHhGRkUwCTB4ZmZlMAlbZmlkPTAwMDAwMDAwMS0yMi0wMDExMTcyO2V4dD1tcDEsbXAyLG1wMzttaW1lPWF1ZGlvL21wZWc7XU1QMyBBdWRpbyBzdHJlYW0gZmlsZQ0KJloxMjgJc3RyaW5nCVRBRwkNCj5aMTI1CXN0cmluZwl4CVt0aXRsZT0lLjMwc10NCj5aOTUJc3RyaW5nCXgJW2NyZWF0b3I9JS4zMHNdDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDUtMDQgYnkgQ2FybA0KMAlzdHJpbmcJSUQzCVtmaWQ9MDAwMDAwMDAxLTIyLTAwMTExNzI7ZXh0PW1wMSxtcDIsbXAzO21pbWU9YXVkaW8vbXBlZztdTVAzIEF1ZGlvIHN0cmVhbSBmaWxlDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTEgYnkgQ2FybA0KMAlzdHJpbmcJXFwwXFwwMDFcXDI0M1xcMTQ0CVtmaWQ9MDAwMDAxMDE0LTIyLTAwMDAwU0Y7ZXh0PXNmO21pbWU9O11JUkNBTSBhdWRpbyBzYW1wbGUgZmlsZQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTExIGJ5IENhcmwNCjAJc3RyaW5nCVxcMFxcMDAyXFwyNDNcXDE0NAlbZmlkPTAwMDAwMTAxNC0yMi0wMDAwMFNGO2V4dD1zZjttaW1lPTtdSVJDQU0gYXVkaW8gc2FtcGxlIGZpbGUsIGxpdHRsZS1lbmRpYW4NCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQowCXN0cmluZwlcXDBcXDAwM1xcMjQzXFwxNDQJW2ZpZD0wMDAwMDEwMTQtMjItMDAwMDBTRjtleHQ9c2Y7bWltZT07XUlSQ0FNIGF1ZGlvIHNhbXBsZSBmaWxlLCBiaWctZW5kaWFuDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTEgYnkgQ2FybA0KMAlzdHJpbmcJXFwxNDRcXDI0M1xcMDAxXFwwCVtmaWQ9MDAwMDAxMDE0LTIyLTAwMDAwU0Y7ZXh0PXNmO21pbWU9O11JUkNBTSBhdWRpbyBzYW1wbGUgZmlsZQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTExIGJ5IENhcmwNCjAJc3RyaW5nCVxcMTQ0XFwyNDNcXDAwMlxcMAlbZmlkPTAwMDAwMTAxNC0yMi0wMDAwMFNGO2V4dD1zZjttaW1lPTtdSVJDQU0gYXVkaW8gc2FtcGxlIGZpbGUsIGJpZy1lbmRpYW4NCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQowCXN0cmluZwlcXDE0NFxcMjQzXFwwMDNcXDAJW2ZpZD0wMDAwMDEwMTQtMjItMDAwMDBTRjtleHQ9c2Y7bWltZT07XUlSQ0FNIGF1ZGlvIHNhbXBsZSBmaWxlLCBsaXR0bGUtZW5kaWFuDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTEgYnkgQ2FybA0KMAlzdHJpbmcJXFwxNDRcXDI0M1xcMDA0XFwwCVtmaWQ9MDAwMDAxMDE0LTIyLTAwMDAwU0Y7ZXh0PXNmO21pbWU9O11JUkNBTSBhdWRpbyBzYW1wbGUgZmlsZSwgYmlnLWVuZGlhbg0KDQojIE1hZ2ljIElEIGZvciBTY3JlYW10cmFja2VyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNSBieSBDYXJsDQoweDRDCXN0cmluZwlTQ1JTCVtmaWQ9MDAwMTAwMDI1LTIyLTAwMDBTTVA7ZXh0PXNtcDttaW1lPTtdU2NyZWFtdHJhY2tlciBhdWRpbyBzYW1wbGUNCj4weDMwCXN0cmluZwl4CVt0aXRsZT0lLjMwc10NCg0KIyBNYWdpYyBJRCBmb3IgU291bmRUb29sIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNyBieSBDYXJsDQowCXN0cmluZwlTT1VORFxceDFBCVtmaWQ9MDAwMTAwMDk1LTIyLTAwMDBTTkQ7ZXh0PXNuZDttaW1lPTtdU291bmQgdG9vbCBhdWRpbyBkYXRhIGZpbGUNCj4xNAlsZXNob3J0CXgJW2ZyZXE9JWRdDQoNCiMgTWFnaWMgSUQgZm9yIFNCU3R1ZGlvIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQowCXN0cmluZwlTTkRcXCAJW2ZpZD0wMDAxMDAwMjAtMjItMDAwMFNPVTtleHQ9c291O21pbWU9O11TQlN0dWRpbyBzYW1wbGVkIGF1ZGlvIGZpbGUNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNS0xOCBieSBDYXJsDQowCXN0cmluZwlTcGVleAlbZmlkPTAwMDAwMDAwMC0yMi0wMFNQRUVYO2V4dD1zcGVleDttaW1lPTtdU3BlZXggTG9zc3kgQXVkaW8gQ29kZWMgcmF3IGF1ZGlvIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgU291bmQgQmxhc3RlciBTREsgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTExIGJ5IENhcmwNCjAJc3RyaW5nCUNyZWF0aXZlXFwgVm9pY2VcXCBGaWxlXFx4MUEJW2ZpZD0wMDAwMDEwMTMtMjItMDAwMFZPQztleHQ9dm9jO21pbWU9O11DcmVhdGl2ZSBWb2ljZSBhdWRpbyBmaWxlDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDUtMTggYnkgQ2FybA0KMAlzdHJpbmcJdm9yYmlzCVtmaWQ9MDAwMDAwMDAwLTIyLTBWT1JCSVM7ZXh0PXZvcmJpczttaW1lPTtdVm9yYmlzIExvc3N5IEF1ZGlvIENvZGVjIHJhdyBhdWRpbyBmaWxlDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTEgYnkgQ2FybA0KMAlzdHJpbmcJUklGRglbZmlkPTAwMDAwMTAwMS0yMi0wMDAwV0FWO2V4dD13YXY7bWltZT07XU1pY3Jvc29mdCBXYXZlZm9ybSBBdWRpbyBmaWxlLCBsaXR0bGUtZW5kaWFuDQomOAlzdHJpbmcJV0FWRQkNCj4yNAlsZWxvbmcJPjAJW2ZyZXE9JWRdDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTEgYnkgQ2FybA0KMAlzdHJpbmcJUklGWAlbZmlkPTAwMDAwMTAwMS0yMi0wMDAwV0FWO2V4dD13YXY7bWltZT07XU1pY3Jvc29mdCBXYXZlZm9ybSBBdWRpbyBmaWxlLCBiaWctZW5kaWFuDQomOAlzdHJpbmcJV0FWRQkNCj4yNAliZWxvbmcJPjAJW2ZyZXE9JWRdDQoNCiMgTWFnaWMgSUQgZm9yIE1heWEgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTEyIGJ5IENhcmwNCjAJc3RyaW5nCUZPUjQJW2ZpZD0wMDAwMDEzMTItMzEtMDAwMDAwMDtleHQ9O21pbWU9O11NYXlhIGltYWdlIGZpbGUNCiY4CXN0cmluZwlDSU1HCQ0KDQojIE1hZ2ljIElEIGZvciBNYXlhIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0xMiBieSBDYXJsDQowCXN0cmluZwlGT1I4CVtmaWQ9MDAwMDAxMzEyLTMxLTAwMDAwMDA7ZXh0PTttaW1lPTtdTWF5YSBpbWFnZSBmaWxlDQomOAlzdHJpbmcJQ0lNRwkNCg0KIyBNYWdpYyBJRCBmb3IgQU9MIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0wMiBieSBDYXJsDQowCXN0cmluZwlKR1xceDA0XFx4MEUJW2ZpZD0wMDAwMDEwMjMtMzEtMDAwMEFSVDtleHQ9YXJ0O21pbWU9O11BT0wvSm9obnNvbi1HcmFjZSBpbWFnZSBmaWxlLCB2ZXJzaW9uIDIuMA0KPjB4MEQJbGVzaG9ydAl4CVtyZXM9JWR4DQo+MHgwRglsZXNob3J0CXgJJWRdDQoNCiMgTWFnaWMgSUQgZm9yIEJNRiBpbWFnZSBjb21wcmVzc29yIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0yNyBieSBDYXJsDQowCXN0cmluZwlcXHg4MVxceDhBMAlbZmlkPTAwMDEwMDA0OC0zMS0wMDAwQk1GO2V4dD1ibWY7bWltZT07XUJNRiBpbWFnZSBmaWxlDQo+MglzdHJpbmcJeAksIHZlcnNpb24gJS4xcw0KPjMJc3RyaW5nCXgJLiUuMXMNCg0KIyBNYWdpYyBJRCBmb3IgQk1GIGltYWdlIGNvbXByZXNzb3IgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTI3IGJ5IENhcmwNCjAJc3RyaW5nCVxceDgxXFx4OEEyCVtmaWQ9MDAwMTAwMDQ4LTMxLTAwMDBCTUY7ZXh0PWJtZjttaW1lPTtdQk1GIGltYWdlIGZpbGUNCj4yCXN0cmluZwl4CSwgdmVyc2lvbiAlLjFzDQo+MwlzdHJpbmcJeAkuJS4xcw0KDQojIE1hZ2ljIElEIGZvciBCTUYgaW1hZ2UgY29tcHJlc3NvciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMjcgYnkgQ2FybA0KMAlzdHJpbmcJXFx4ODFcXHg4QTEJW2ZpZD0wMDAxMDAwNDgtMzEtMDAwMEJNRjtleHQ9Ym1mO21pbWU9O11CTUYgaW1hZ2UgZmlsZQ0KPjIJc3RyaW5nCXgJLCB2ZXJzaW9uICUuMXMNCj4zCXN0cmluZwl4CS4lLjFzDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTQgYnkgQ2FybA0KMAlzdHJpbmcJQk0JW2ZpZD0wMDAwMDEwMDEtMzEtMDAwMEJNUDtleHQ9Ym1wO21pbWU9O11XaW5kb3dzIG9yIE9TLzIgQml0bWFwIGltYWdlIGZpbGUNCiY2CWxlbG9uZwkwCQ0KDQojIE1hZ2ljIElEIGZvciBBdXRvZGVzayBBbmltYXRvciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMDggYnkgQ2FybA0KMAlsZXNob3J0CTB4OTExOQlbZmlkPTAwMDAwMTI1NC0zMS0wMDAwQ0VMO2V4dD1jZWwscGljO21pbWU9O11BdXRvZGVzayBhbmltYXRvciBpbWFnZSBmaWxlDQomMTAJYnl0ZQk4CQ0KJjExCWJ5dGUJMAkNCj4yCWxlc2hvcnQJeAlbcmVzPSVkDQo+NAlsZXNob3J0CXgJeCVkeDhicHBdDQoNCiMgTWFnaWMgSUQgZm9yIEFuZHJldyBVc2VyIEludGVyZmFjZSBTeXN0ZW0gZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTAyIGJ5IENhcmwNCjEJc3RyaW5nCWJlZ2luZGF0YXtyYXN0ZXIJW2ZpZD0wMDAwMDEzMTUtMzEtMDAwMENNVTtleHQ9Y211O21pbWU9O11BbmRyZXcgdG9vbGtpdCByYXN0ZXIgaW1hZ2UgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBBbml2Z2EgdG9vbGtpdCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMDIgYnkgQ2FybA0KMzgJc3RyaW5nCUtSXFx4MDFcXHgwMAlbZmlkPTAwMDEwMDEwNC0zMS0wMDAwQ09EO2V4dD1jb2Q7bWltZT07XUFuaXZnYSBzcHJpdGUgaW1hZ2UgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBXaW5kb3dzIEN1cnNvciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMDEgYnkgQ2FybA0KMAlzdHJpbmcJXFx4MDBcXHgwMFxceDAyXFx4MDAJW2ZpZD0wMDAwMDEwMDEtMzEtMDAwMENVUjtleHQ9Y3VyO21pbWU9O11NaWNyb3NvZnQgd2luZG93cyBjdXJzb3IgaW1hZ2UgZmlsZQ0KJjB4MDgJYnl0ZQkwCQ0KPjQJbGVzaG9ydAl4CSwgJWQgY3Vyc29yKHMpDQo+NglieXRlCXgJW3Jlcz0lZHgNCj43CWJ5dGUJeAklZHg4YnBwXQ0KDQojIE1hZ2ljIElEIGZvciBXaW5kb3dzIEN1cnNvciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMDEgYnkgQ2FybA0KMAlzdHJpbmcJXFx4MDBcXHgwMFxceDAyXFx4MDAJW2ZpZD0wMDAwMDEwMDEtMzEtMDAwMENVUjtleHQ9Y3VyO21pbWU9O11NaWNyb3NvZnQgd2luZG93cyBjdXJzb3IgaW1hZ2UgZmlsZQ0KJjB4MDgJYnl0ZQkxNgkNCj40CWxlc2hvcnQJeAksICVkIGN1cnNvcihzKQ0KPjYJYnl0ZQl4CVtyZXM9JWR4DQo+NwlieXRlCXgJJWR4NGJwcF0NCg0KIyBNYWdpYyBJRCBmb3IgV2luZG93cyBDdXJzb3IgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTAxIGJ5IENhcmwNCjAJc3RyaW5nCVxceDAwXFx4MDBcXHgwMlxceDAwCVtmaWQ9MDAwMDAxMDAxLTMxLTAwMDBDVVI7ZXh0PWN1cjttaW1lPTtdTWljcm9zb2Z0IHdpbmRvd3MgY3Vyc29yIGltYWdlIGZpbGUNCiYweDA4CWJ5dGUJMgkNCj40CWxlc2hvcnQJeAksICVkIGN1cnNvcihzKQ0KPjYJYnl0ZQl4CVtyZXM9JWR4DQo+NwlieXRlCXgJJWR4MWJwcF0NCg0KIyBNYWdpYyBJRCBmb3IgV2luZG93cyBDdXJzb3IgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTAxIGJ5IENhcmwNCjAJc3RyaW5nCVxceDAwXFx4MDBcXHgwMlxceDAwCVtmaWQ9MDAwMDAxMDAxLTMxLTAwMDBDVVI7ZXh0PWN1cjttaW1lPTtdTWljcm9zb2Z0IHdpbmRvd3MgY3Vyc29yIGltYWdlIGZpbGUNCiYweDA4CWJ5dGUJMzIJDQo+NAlsZXNob3J0CXgJLCAlZCBjdXJzb3IocykNCj42CWJ5dGUJeAlbcmVzPSVkeA0KPjcJYnl0ZQl4CSVkeDVicHBdDQoNCiMgTWFnaWMgSUQgZm9yIFdpbmRvd3MgQ3Vyc29yIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0wMSBieSBDYXJsDQowCXN0cmluZwlcXHgwMFxceDAwXFx4MDJcXHgwMAlbZmlkPTAwMDAwMTAwMS0zMS0wMDAwQ1VSO2V4dD1jdXI7bWltZT07XU1pY3Jvc29mdCB3aW5kb3dzIGN1cnNvciBpbWFnZSBmaWxlDQomMHgwOAlieXRlCTY0CQ0KPjQJbGVzaG9ydAl4CSwgJWQgY3Vyc29yKHMpDQo+NglieXRlCXgJW3Jlcz0lZHgNCj43CWJ5dGUJeAklZHg2YnBwXQ0KDQojIE1hZ2ljIElEIGZvciBXaW5kb3dzIEN1cnNvciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMDEgYnkgQ2FybA0KMAlzdHJpbmcJXFx4MDBcXHgwMFxceDAyXFx4MDAJW2ZpZD0wMDAwMDEwMDEtMzEtMDAwMENVUjtleHQ9Y3VyO21pbWU9O11NaWNyb3NvZnQgd2luZG93cyBjdXJzb3IgaW1hZ2UgZmlsZQ0KJjB4MDgJYnl0ZQk4CQ0KPjQJbGVzaG9ydAl4CSwgJWQgY3Vyc29yKHMpDQo+NglieXRlCXgJW3Jlcz0lZHgNCj43CWJ5dGUJeAklZHgzYnBwXQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTEzIGJ5IENhcmwNCjEyOAlzdHJpbmcJRElDTQlbZmlkPTAwMDAwMDAwNC0zMS0wMDBESUNNO2V4dD1kaWNtLGRjbTttaW1lPTtdRGlnaXRhbCBpbWFnaW5nIGFuZCBjb21tdW5pY2F0aW9uIGluIG1lZGVjaW5lIGltZy4NCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0wMSBieSBDYXJsDQowCXN0cmluZwlTRFBYCVtmaWQ9MDAwMDAxMzA5LTMxLTAwMDBEUFg7ZXh0PWRweDttaW1lPTtdRGlnaXRhbCBNb3ZpbmctUGljdHVyZSBFeGNoYW5nZSBpbWFnZSBmaWxlDQo+MTYwCXN0cmluZwk+XFx4MDAJW2NyZWF0b3I9JS4xMDBzXQ0KPjI2MAlzdHJpbmcJPlxceDAwCVt0aXRsZT0lLjIwMHNdDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMDEgYnkgQ2FybA0KMAlzdHJpbmcJWFBEUwlbZmlkPTAwMDAwMTMwOS0zMS0wMDAwRFBYO2V4dD1kcHg7bWltZT07XURpZ2l0YWwgTW92aW5nLVBpY3R1cmUgRXhjaGFuZ2UgaW1hZ2UgZmlsZQ0KPjE2MAlzdHJpbmcJPlxceDAwCVtjcmVhdG9yPSUuMTAwc10NCj4yNjAJc3RyaW5nCT5cXHgwMAlbdGl0bGU9JS4yMDBzXQ0KDQojIE1hZ2ljIElEIGZvciBMaWdodHdhdmUgM0QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTEyIGJ5IENhcmwNCjAJc3RyaW5nCUZPUk0JW2ZpZD0wMDAwMDEyNTEtMzEtMDAwRlBCTTtleHQ9ZnBibTttaW1lPTtdRmxleGlibGUgUHJlY2lzaW9uIEJ1ZmZlciBNYXAgaW1hZ2UgZmlsZQ0KJjgJc3RyaW5nCUZQQk0JDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTQgYnkgQ2FybA0KMAlzdHJpbmcJR0lGOAlbZmlkPTAwMDAwMTI3NC0zMS0wMDAwR0lGO2V4dD1naWY7bWltZT1pbWFnZS9naWY7XUdJRiBpbWFnZSBmaWxlDQomMTAJYnl0ZSYweDcwCSEweDcwCQ0KPjQJc3RyaW5nCXgJLCB2ZXJzaW9uIDglLjJzDQo+NglsZXNob3J0CT4wCVtyZXM9JWR4DQo+OAlsZXNob3J0CT4wCSVkXQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE0IGJ5IENhcmwNCjAJc3RyaW5nCUdJRjgJW2ZpZD0wMDAwMDEyNzQtMzEtMDAwMEdJRjtleHQ9Z2lmO21pbWU9aW1hZ2UvZ2lmO11HSUYgaW1hZ2UgZmlsZQ0KJjEwCWJ5dGUmMHg3MAkweDcwCQ0KPjQJc3RyaW5nCXgJLCB2ZXJzaW9uIDglLjJzDQo+NglsZXNob3J0CT4wCVtyZXM9JWR4DQo+OAlsZXNob3J0CT4wCSVkeDhicHBdDQoNCiMgTWFnaWMgSUQgZm9yIFdpbmRvd3MgSWNvbiBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMDEgYnkgQ2FybA0KMAlzdHJpbmcJXFx4MDBcXHgwMFxceDAxXFx4MDAJW2ZpZD0wMDAwMDEwMDEtMzEtMDAwMElDTztleHQ9aWNvO21pbWU9aW1hZ2Uvdm5kLm1pY3Jvc29mdC5pY29uO11NaWNyb3NvZnQgd2luZG93cyBJY29uIGltYWdlIGZpbGUNCiYweDA4CWJ5dGUJMAkNCj40CWxlc2hvcnQJeAksICVkIGljb24ocykNCj42CWJ5dGUJeAlbcmVzPSVkeA0KPjcJYnl0ZQl4CSVkeDhicHBdDQoNCiMgTWFnaWMgSUQgZm9yIFdpbmRvd3MgSWNvbiBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMDEgYnkgQ2FybA0KMAlzdHJpbmcJXFx4MDBcXHgwMFxceDAxXFx4MDAJW2ZpZD0wMDAwMDEwMDEtMzEtMDAwMElDTztleHQ9aWNvO21pbWU9aW1hZ2Uvdm5kLm1pY3Jvc29mdC5pY29uO11NaWNyb3NvZnQgd2luZG93cyBJY29uIGltYWdlIGZpbGUNCiYweDA4CWJ5dGUJMTYJDQo+NAlsZXNob3J0CXgJLCAlZCBpY29uKHMpDQo+NglieXRlCXgJW3Jlcz0lZHgNCj43CWJ5dGUJeAklZHg0YnBwXQ0KDQojIE1hZ2ljIElEIGZvciBXaW5kb3dzIEljb24gZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTAxIGJ5IENhcmwNCjAJc3RyaW5nCVxceDAwXFx4MDBcXHgwMVxceDAwCVtmaWQ9MDAwMDAxMDAxLTMxLTAwMDBJQ087ZXh0PWljbzttaW1lPWltYWdlL3ZuZC5taWNyb3NvZnQuaWNvbjtdTWljcm9zb2Z0IHdpbmRvd3MgSWNvbiBpbWFnZSBmaWxlDQomMHgwOAlieXRlCTIJDQo+NAlsZXNob3J0CXgJLCAlZCBpY29uKHMpDQo+NglieXRlCXgJW3Jlcz0lZHgNCj43CWJ5dGUJeAklZHgxYnBwXQ0KDQojIE1hZ2ljIElEIGZvciBXaW5kb3dzIEljb24gZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTAxIGJ5IENhcmwNCjAJc3RyaW5nCVxceDAwXFx4MDBcXHgwMVxceDAwCVtmaWQ9MDAwMDAxMDAxLTMxLTAwMDBJQ087ZXh0PWljbzttaW1lPWltYWdlL3ZuZC5taWNyb3NvZnQuaWNvbjtdTWljcm9zb2Z0IHdpbmRvd3MgSWNvbiBpbWFnZSBmaWxlDQomMHgwOAlieXRlCTMyCQ0KPjQJbGVzaG9ydAl4CSwgJWQgaWNvbihzKQ0KPjYJYnl0ZQl4CVtyZXM9JWR4DQo+NwlieXRlCXgJJWR4NWJwcF0NCg0KIyBNYWdpYyBJRCBmb3IgV2luZG93cyBJY29uIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0wMSBieSBDYXJsDQowCXN0cmluZwlcXHgwMFxceDAwXFx4MDFcXHgwMAlbZmlkPTAwMDAwMTAwMS0zMS0wMDAwSUNPO2V4dD1pY287bWltZT1pbWFnZS92bmQubWljcm9zb2Z0Lmljb247XU1pY3Jvc29mdCB3aW5kb3dzIEljb24gaW1hZ2UgZmlsZQ0KJjB4MDgJYnl0ZQk2NAkNCj40CWxlc2hvcnQJeAksICVkIGljb24ocykNCj42CWJ5dGUJeAlbcmVzPSVkeA0KPjcJYnl0ZQl4CSVkeDZicHBdDQoNCiMgTWFnaWMgSUQgZm9yIFdpbmRvd3MgSWNvbiBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMDEgYnkgQ2FybA0KMAlzdHJpbmcJXFx4MDBcXHgwMFxceDAxXFx4MDAJW2ZpZD0wMDAwMDEwMDEtMzEtMDAwMElDTztleHQ9aWNvO21pbWU9aW1hZ2Uvdm5kLm1pY3Jvc29mdC5pY29uO11NaWNyb3NvZnQgd2luZG93cyBJY29uIGltYWdlIGZpbGUNCiYweDA4CWJ5dGUJOAkNCj40CWxlc2hvcnQJeAksICVkIGljb24ocykNCj42CWJ5dGUJeAlbcmVzPSVkeA0KPjcJYnl0ZQl4CSVkeDNicHBdDQoNCiMgTWFnaWMgSUQgZm9yIFN1bk9TIEljb24gZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTAyIGJ5IENhcmwNCjAJc3RyaW5nCS8qXFwgRm9ybWF0X3ZlcnNpb249MSxcXCAJW2ZpZD0wMDAwMDEwMTEtMzEtMDAwSUNPTjtleHQ9aWNvbjttaW1lPTtdU3VuT1MgaWNvbiBpbWFnZSBmaWxlDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMzEgYnkgQ2FybA0KMAliZXNob3J0CTB4MDEJW2ZpZD0wMDAwMDEyNzMtMzEtMDAwMElNRztleHQ9aW1nO21pbWU9O11HRU0gQml0IEltYWdlDQomMgliZXNob3J0CTB4MDgJDQo+MTIJYmVzaG9ydAk+MAlbcmVzPSVkeA0KPjE0CWJlc2hvcnQJPjAJJWQNCj40CWJlc2hvcnQJPjAJeCVkYnBwXQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTAyIGJ5IENhcmwNCjAJc3RyaW5nCVxceDhiSk5HXFx4MGRcXHgwYVxceDFhXFx4MGEJW2ZpZD0wMDAwMDAwMDAtMzEtMDAwMEpORztleHQ9am5nO21pbWU9O11KUEVHIE5ldHdvcmsgZ3JhcGhpY3MgaW1hZ2UgZmlsZQ0KJjEyCXN0cmluZwlKSERSCQ0KPjE2CWJlbG9uZwl4CVtyZXM9JWQNCj4yMAliZWxvbmcJeAl4JWRdDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMTMgYnkgQ2FybA0KMAlzdHJpbmcJXFx4MDBcXHgwMFxceDAwXFx4MGNqUFxceDIwXFx4MjAJW2ZpZD0wMDAwMDAwMDEtMzEtMDAxNTQ0NDtleHQ9anAyO21pbWU9aW1hZ2UvanAyO11KUEVHIDIwMDAgaW1hZ2UgZmlsZQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTEzIGJ5IENhcmwNCjAJc3RyaW5nCVxceGZmXFx4NGZcXHhmZlxceDUxCVtmaWQ9MDAwMDAwMDAxLTMxLTAwMTU0NDQ7ZXh0PWpwYzttaW1lPTtdSlBFRyAyMDAwIGNvZGUgc3RyZWFtIGltYWdlIGZpbGUNCiZaMgliZXNob3J0CTB4RkZEOQkNCj44CWJlbG9uZwl4CVtyZXM9JWR4DQo+MTIJYmVsb25nCXgJJWRdDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMzEgYnkgQ2FybA0KMAliZWxvbmcJMHhmZmQ4ZmZlMAlbZmlkPTAwMDAwMTMwNS0zMS0wMDBKUEVHO2V4dD1qcGVnLGpwZzttaW1lPWltYWdlL2pwZWc7XUpvaW50IFBob3RvZ3JhcGhpYyBFeHBlcnRzIEdyb3VwIEpGSUYgaW1hZ2UgZmlsZQ0KJjYJc3RyaW5nCUpGSUZcXHgwMAkNCj4xMQlieXRlCXgJLCB2ZXJzaW9uICVk