From 731d2b4b204fdbfa5959dfa0ef35a25844328c7d Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 14 Sep 2010 14:09:26 +0100 Subject: [PATCH 001/164] tidy up - stripped out a lot of depreciated code that has been replaced by the casters. --- include/core.php | 240 ----------------------------------------------- 1 file changed, 240 deletions(-) diff --git a/include/core.php b/include/core.php index b32c1e6..4095ba3 100644 --- a/include/core.php +++ b/include/core.php @@ -3,39 +3,6 @@ * Atsumi core functions * */ -/* - -// sprintf shorthand -function sf($args_etc) { - $args = func_get_args(); - - //print_r(call_user_func_array('sprintf', $args)); - if(is_array($args)) return call_user_func_array('sprintf', $args); - return call_user_func('sprintf', $args); -} -function sfl($args_etc) { - - $args = func_get_args(); - - return sf($args)."\n"; -} - -// prints sprintf -function pf($args_etc) { - $args = func_get_args(); - print(sf ($args)); -} -function pfl($args_etc) { - $args = func_get_args(); - print(sfl($args)); -} -*/ -/* -// prints & flushes sprintf -function ff() { print(sf (func_get_args())); flush(); } -function ffl() { print(sfl (func_get_args())); flush(); } -*/ -// handy funcs to handle nulls function functions_exist($function_name, $_ = null) { $functions = func_get_args(); @@ -135,213 +102,6 @@ function mktimez($year, $month, $day, $hour, $minute, $second) { } - - - - - - - - - - -/* - -function quotef_special($args) { - $ret = array(); - for($i = 0; $i < count($args); ) { - $format = $args [$i++]; - $params = array_slice($args, $i, $this->num_percents($format)); - $i += count($params); - array_unshift($params, $format); - $ret[] = call_user_func_array(array($this, 'quotef'), $params); - } - return $ret; -} - -function sfl($format_etc) { - $args = func_get_args(); - return(sf($args)."\n"); -} - -function sf($format_etc) { - //print("
");print_r($format_etc);print("
"); - $args = func_get_args(); - return implode('', call_user_func_array('sf_special', $args)); -} - -function sf_special($format_etc) { - - // get args - $args = func_get_args(); - if(count($args) == 1 && is_array($args [0])) { - $real_args = $args [0]; - } else if(count($args) == 2 && is_string($args [0]) && is_array($args [1])) { - $real_args = array_merge(array($args [0]), $args [1]); - } else { - $real_args = $args; - } - $args = $real_args; - - $ret = array(); - for($i = 0; $i < count($args); ) { - $format = $args [$i++]; - $params = array_slice($args, $i, sf_num_percents($format)); - $i += count($params); - $ret[] = sf_real($format, $params); - } - return $ret; -} - -function sf_num_percents($s) { - if(!is_string($s)) throw new Exception('Parameter $s should be of type string'); - $pos = 0; - $count = 0; - while(true) { - $pos = strpos($s, '%', $pos); - if($pos === false) return $count; - if($pos + 2 > strlen($s)) - throw new Exception('Invalid format string'); - if(substr($s, $pos + 1, 1) != "%") - $count++; - $pos += 2; - } -} - -function sf_real($format, $args) { - $format = sf_escapes($format); - $ret = ""; - $pos0 = 0; - $i = 0; - while(true) { - $pos1 = strpos($format, '%', $pos0); - if($pos1 === false) - return $ret . substr($format, $pos0); - if($pos1 + 2 > strlen($format)) - throw new Exception('Invalid format string'); - $ret .= substr($format, $pos0, $pos1 - $pos0); - $ch = substr($format, $pos1 + 1, 1); - switch($ch) { - - case 's': - $ret .= $args [$i++]; - $pos0 = $pos1 + 2; - break; - - case 'd': - $ret .= sprintf('%d', $args [$i++]); - $pos0 = $pos1 + 2; - break; - - case 'h': - $ret .= htmlspecialchars($args [$i++], ENT_COMPAT, 'UTF-8'); - $pos0 = $pos1 + 2; - break; - - case 'H': - $ret .= nl2br(htmlspecialchars($args [$i++], ENT_COMPAT, 'UTF-8')); - $pos0 = $pos1 + 2; - break; - - case 'j': - $ret .= js_escape($args [$i++]); - $pos0 = $pos1 + 2; - break; - - case 'u': - $ret .= urlencode($args [$i++]); - $pos0 = $pos1 + 2; - break; - - case 'U': - $ret .= urldecode($args [$i++]); - $pos0 = $pos1 + 2; - break; - case '{': - $s = $args [$i++]; - $pos2 = strpos($format, '}', $pos1 + 2); - if($pos2 == false) - throw new Exception('Invalid format string'); - $bits = substr($format, $pos1 + 2, $pos2 - $pos1 - 2); - for($j = strlen($bits) - 1; $j >= 0; $j--) - $s = sf_real("%" . substr($bits, $j, 1), array($s)); - $ret .= $s; - $pos0 = $pos2 + 1; - break; - - case '%': - $ret .= '%'; - $pos0 = $pos1 + 2; - break; - - default: - throw new Exception(sf('Unrecognised format char: %s(%s)', $ch, $format)); - } - } -} - -function sf_escapes($str) { - $matches = null; - preg_match_all('/ [^\\\\] | \\\\ . | \\\\ /x', $str, $matches); - $ret = ''; - foreach($matches [0] as $match) { - if($match == '\\') - throw new Exception('Illegal input(lone backslash at end)'); - if($match {0} == '\\') { - switch($match {1}) { - case '_': - $ret .= '\\'; - break; - case 'n': - $ret .= "\n"; - break; - case 'r': - $ret .= "\r"; - break; - default: - throw new Exception('Illegal escape character: ' . $match {1} .' in string:
'.$str.'
'); - } - } else { - $ret .= $match; - } - } - return $ret; -} - -function js_escape($str) { - return str_replace( - array('\\', '\'', '"', '/', "\n", "\r"), - array('\\\\', '\\\'', '\\"', '\\/', '\\n', '\\r'), - $str); -} -/** - * Calls sf to construct a string from a format string then prints it. - -function pfl($format_etc) { - $args = func_get_args(); - print(sf($args)."\n"); -} - -/** - * Calls sf to construct a string from a format string then prints it. - -function pf($format_etc) { - $args = func_get_args(); - print(sf($args)); -} - -/** - * Calls pf to print the format string then flushed output. - -function ff($format_etc) { - $args = func_get_args(); - print(sf($args)); - flush(); -} -*/ - - - /* String manipulation short hand */ function sf ($args) { From 01516c48a65d29c7b6c816732cb79a0b15fc6680 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Sun, 26 Sep 2010 13:12:55 +0100 Subject: [PATCH 002/164] minor fix for an exception name in error handler --- classes/core/error/atsumi_ErrorHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/core/error/atsumi_ErrorHandler.php b/classes/core/error/atsumi_ErrorHandler.php index d1bfd80..ec32acf 100644 --- a/classes/core/error/atsumi_ErrorHandler.php +++ b/classes/core/error/atsumi_ErrorHandler.php @@ -139,7 +139,7 @@ private function blockedByFloodControl($type, $line, $file) { if($this->cacheHandler->get($key, false, 'errorHandler')) return true; else return false; - } catch(cache_NotInCacheException $e) { + } catch(cache_NotFoundException $e) { return false; } } From 47b1a08bb8ecd96f3a4fdc927cdf954d849dfc35 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Sun, 26 Sep 2010 13:13:42 +0100 Subject: [PATCH 003/164] minor fox for spelling mistake in memcache handler --- classes/cache/cache_MemcacheHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/cache/cache_MemcacheHandler.php b/classes/cache/cache_MemcacheHandler.php index d9b4dd5..a550a0c 100644 --- a/classes/cache/cache_MemcacheHandler.php +++ b/classes/cache/cache_MemcacheHandler.php @@ -65,7 +65,7 @@ protected function _get($key, $default = null, $namespace = 'default') { if(!is_array($return)) { if($this->useExceptions) - throw new cache_NotFoundException(sf('Could not fine \'%s\' variable', $key)); + throw new cache_NotFoundException(sf('Could not find \'%s\' variable', $key)); $return = array($default); } From e2384fa9cfccf7416e0f360cd53e1a883027ec05 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 27 Sep 2010 20:01:10 +0100 Subject: [PATCH 004/164] Added SSL/HTTPS support to AbstractController --- classes/mvc/controllers/mvc_AbstractController.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/classes/mvc/controllers/mvc_AbstractController.php b/classes/mvc/controllers/mvc_AbstractController.php index 9272ad4..0285893 100644 --- a/classes/mvc/controllers/mvc_AbstractController.php +++ b/classes/mvc/controllers/mvc_AbstractController.php @@ -127,9 +127,12 @@ public function reload($anchor = null) { $anchor = !is_null($anchor) ? "#".$anchor:""; + if (!isset($_SERVER['HTTPS']) || strtolower($_SERVER['HTTPS']) != 'on') $protocol = 'http://'; + else $protocol = 'https://'; + array_key_exists('REDIRECT_SCRIPT_URI',$_SERVER) ? header('Location: ' . $_SERVER['REDIRECT_SCRIPT_URI']. $anchor) - : header('Location: ' . 'http://' . $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']. $anchor); + : header('Location: ' . $protocol . $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']. $anchor); exit; } @@ -137,14 +140,17 @@ public function reload($anchor = null) { // redirect to a new page public function redirect($url, $httpResponseCode = atsumi_Http::REDIRECT_FOUND) { - if(substr($url, 0, 7) == "http://") { + if(substr($url, 0, 7) == "http://" || substr($url, 0, 8) == "https://") { header('Location: ' . $url, true, $httpResponseCode); exit; } + if (!isset($_SERVER['HTTPS']) || strtolower($_SERVER['HTTPS']) != 'on') $protocol = 'http://'; + else $protocol = 'https://'; + $domain = $_SERVER['HTTP_HOST']; unset($_POST); - header('Location: ' . 'http://' . $domain.$url, true, $httpResponseCode); + header('Location: ' . $protocol . $domain.$url, true, $httpResponseCode); exit; } From 0ecb7582c68f153d5e5e8b0f6939335952e74e6c Mon Sep 17 00:00:00 2001 From: apparatusdeus Date: Wed, 29 Sep 2010 20:10:15 +0800 Subject: [PATCH 005/164] Can now set session life and enable presistent sessions. --- classes/session/session_Handler.php | 143 ++++++++++++++---- .../storage/session_DatabaseStorage.php | 6 +- 2 files changed, 119 insertions(+), 30 deletions(-) diff --git a/classes/session/session_Handler.php b/classes/session/session_Handler.php index 1d0daa7..701ff1b 100644 --- a/classes/session/session_Handler.php +++ b/classes/session/session_Handler.php @@ -1,15 +1,40 @@ configure($options); // Fetch the session storage handler - $storage =(isset($options['storage']) ? $options['storage'] : self::DEFAULT_STORAGE); + $storage = (isset($options['storage']) ? $options['storage'] : self::DEFAULT_STORAGE); if(is_subclass_of($storage, 'session_AbstractStorage')) $this->storage = new $storage($options); // Start the session - $this->start(); + $this->start($options); // Add debug information atsumi_Debug::record( 'Session Created', - 'The atsumi session constructor completed', true, + 'The atsumi session constructor completed', null, true, atsumi_Debug::AREA_SESSION ); } @@ -68,17 +95,14 @@ public function __destruct() { } catch (Exception $e) { } } - public function getId() { - return session_id(); - } + /* GET METHODS */ /** * Returns a singleton instance of atsumi session - * * @param $options Session setup options * @return object A session_Handler object */ - static public function getInstance($options = array()) { + public static function getInstance($options = array()) { static $instance; if(!is_object($instance)) @@ -87,8 +111,13 @@ static public function getInstance($options = array()) { return $instance; } - /* GET FUNCTIONS */ - + /** + * Returns a variable from the current active session. + * @param string $name The name of the variable to return. + * @param mixed $default The variable to return if the value does not exist. + * @param string $namespace The namespace under which the variable is stored. + * @return mixed The variable under that name or the default. + */ public function &get($name, $default = null, $namespace = self::DEFAULT_NAMESPACE) { // Add prefix to prevent namespace collisions $namespace = '__'.$namespace; @@ -99,7 +128,23 @@ public function &get($name, $default = null, $namespace = self::DEFAULT_NAMESPAC return $default; } - /* SET FUNCTIONS */ + /** + * Returns the current session id. + * @return string The session id. + */ + public function getId() { + return session_id(); + } + + /* SET METHODS */ + + /** + * Sets a variable to the current active session and return the old value if set. + * @param string $name The name of the variable to return. + * @param mixed $value The variable to be stored under that name. + * @param string $namespace The namespace under which the variable is stored. + * @return mixed The old value under the name if it exists. + */ public function set($name, $value, $namespace = self::DEFAULT_NAMESPACE) { // Add prefix to prevent namespace collisions $namespace = '__'.$namespace; @@ -114,6 +159,9 @@ public function set($name, $value, $namespace = self::DEFAULT_NAMESPACE) { return $old; } + /* MAGIC METHODS */ + /* METHODS */ + public function push($name, $value, $namespace = self::DEFAULT_NAMESPACE) { // Add prefix to prevent namespace collisions $namespace = '__'.$namespace; @@ -125,18 +173,17 @@ public function push($name, $value, $namespace = self::DEFAULT_NAMESPACE) { } - /* HAS FUNCTIONS */ - public function has($name, $namespace = self::DEFAULT_NAMESPACE) { + public function exists($name, $namespace = self::DEFAULT_NAMESPACE) { // Add prefix to prevent namespace collisions $namespace = '__'.$namespace; return isset($_SESSION[$namespace][$name]); } - public function del($name, $namespace = self::DEFAULT_NAMESPACE) { + public function delete($name, $namespace = self::DEFAULT_NAMESPACE) { $this->set($name, null, $namespace); } - protected function start() { + protected function start($options = array()) { if(headers_sent() === true) throw new session_HeadersSentException; @@ -146,6 +193,9 @@ protected function start() { session_cache_limiter('none'); session_start(); + if(isset($options['persistent']) && $options['persistent'] === true) + $this->renewCookie($options['life']); + $this->state = self::STATE_ACTIVE; } @@ -177,20 +227,51 @@ protected function configure($options = array()) { ini_set('session.save_handler', 'files'); ini_set('session.use_trans_sid', false); + if(isset($options['life'])) ini_set('session.cookie_lifetime', $options['life']); + + $this->configCookie($options); + if(isset($options['name'])) session_name($options['name']); else session_name('atsumi_session'); + } - if(isset($options['domain'])) - ini_set('session.cookie_domain', $options['domain']); + public function configCookie($options = array()) { + $old = session_get_cookie_params(); + session_set_cookie_params( + (isset($options['life']) ? $options['life'] : $old['lifetime']), + (isset($options['cookie_path']) ? $options['cookie_path'] : $old['path']), + (isset($options['cookie_domain']) ? $options['cookie_domain'] : $old['domain']), + (isset($options['cookie_secure']) ? $options['cookie_secure'] : $old['secure']), + (isset($options['cookie_httponly']) ? $options['cookie_httponly'] : $old['httponly']) + ); } protected function destroyCookie() { + $name = session_name(); + if(!isset($_COOKIE[$name])) return; + $params = session_get_cookie_params(); - setcookie(session_name(), '', time() - 42000, - $params["path"], $params["domain"], - $params["secure"], $params["httponly"] + setcookie($name, '', time() - 42000, + $params['path'], $params['domain'], + $params['secure'], $params['httponly'] + ); + } + + protected function renewCookie($life = 0) { + $name = session_name(); + if(!isset($_COOKIE[$name])) return; + + $params = session_get_cookie_params(); + setcookie( + $name, + session_id(), + ($life > 0 ? (time() + $life) : 0), + $params['path'], + $params['domain'], + $params['secure'], + $params['httponly'] ); } @@ -204,5 +285,15 @@ protected function generateId($len = 32) { session_id($id); return $id; } + + /* DEPRECATED METHODS */ + + public function has($name, $namespace = self::DEFAULT_NAMESPACE) { + $this->exists($name, $namespace); + } + + public function del($name, $namespace = self::DEFAULT_NAMESPACE) { + $this->delete($name, $namespace); + } } ?> \ No newline at end of file diff --git a/classes/session/storage/session_DatabaseStorage.php b/classes/session/storage/session_DatabaseStorage.php index 2e8c219..2c529a5 100644 --- a/classes/session/storage/session_DatabaseStorage.php +++ b/classes/session/storage/session_DatabaseStorage.php @@ -11,7 +11,7 @@ public function __construct($options) { $this->database = $options['database']; ini_set('session.gc_divisor', 1); - ini_set('session.gc_maxlifetime', 1440); + ini_set('session.gc_maxlifetime', (isset($options['life']) ? $options['life'] : 7200)); ini_set('session.gc_probability', 100); parent::__construct($options); @@ -48,8 +48,6 @@ public function read($id) { public function write($id, $sessionData) { try { if($this->database->exists('session', 'checksum = %i AND session_id = %s', crc32($id), $id)) { - - atsumi_Debug::record( 'Updating Session', sf('The session(%s) is being updated to the DB', $id ), @@ -82,7 +80,7 @@ public function write($id, $sessionData) { return true; /* this is a little drastic but will most likley segfault if Exception bubbles up */ - } catch (Exception $e) { die("Could not write session to database."); } + } catch (Exception $e) { die('Could not write session to database.'); } } public function destroy($id) { From 0c77e5cb2a04d516cb9e8ab5259c6883e5d84f8a Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Wed, 20 Oct 2010 20:47:26 +0100 Subject: [PATCH 006/164] Added IP address validator --- .../exceptions/ValidationException.php | 2 +- classes/validators/validate_IpAddress.php | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 classes/validators/validate_IpAddress.php diff --git a/classes/validators/exceptions/ValidationException.php b/classes/validators/exceptions/ValidationException.php index 729e7dd..4786941 100644 --- a/classes/validators/exceptions/ValidationException.php +++ b/classes/validators/exceptions/ValidationException.php @@ -3,6 +3,6 @@ class ValidationException extends Exception { public function __construct($message = null, $code = null) { - parent::__construct('Validation Failed: '.$message, $code); + parent::__construct($message, $code); } } \ No newline at end of file diff --git a/classes/validators/validate_IpAddress.php b/classes/validators/validate_IpAddress.php new file mode 100644 index 0000000..ef335a5 --- /dev/null +++ b/classes/validators/validate_IpAddress.php @@ -0,0 +1,22 @@ + From c5eb004b832a801e4af21855e4e0bd054f2171f5 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Fri, 6 May 2011 14:19:14 +0100 Subject: [PATCH 007/164] Added CSS styling options to elements --- .../form/elements/widget_AbstractElement.php | 36 ++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/classes/widgets/form/elements/widget_AbstractElement.php b/classes/widgets/form/elements/widget_AbstractElement.php index c23c87b..b0ca6a8 100644 --- a/classes/widgets/form/elements/widget_AbstractElement.php +++ b/classes/widgets/form/elements/widget_AbstractElement.php @@ -12,8 +12,17 @@ abstract class widget_AbstractElement { protected $validators = null; protected $errors = array(); protected $required = false; + + + /** + * @deprecated + */ protected $style = null; + protected $cssStyle = null; + protected $cssClass = null; + + // abstract function outputSpecific(); @@ -47,10 +56,12 @@ static public function makeInputSafe($in) { public function render() { $out = $this->preRender(); - $out .= sfl('
%s%s%s
', + $out .= sfl('
%s%s%s
', $this->style ? " " . $this->style : "", + $this->cssClass ? " " . $this->cssClass : "", ($this->submitted && !$this->validates) ? " error" : "", $this->name, + $this->cssStyle ? " style='" . $this->cssStyle . "'": "", $this->renderErrors(), ($this->label != '' || $this->getRequired() ? $this->renderLabel() : ''), $this->renderElement()); @@ -124,6 +135,7 @@ protected function getValidationData() { function setSubmitted($in) { $this->submitted = $in; } + public function setValidators($in) { $this->validators = $in; } @@ -132,23 +144,30 @@ public function setName($in) { if(!is_string($in)) throw new Exception("Name must be of type String"); $this->name = $in; } + public function setLabel($in) { $this->label = $in; } + public function setError(Exception $e) { $this->errors[] = $e->getMessage(); } - public function setStyle($in) { - $this->style = $in; - } public function setValue($input, $files = array()) { $this->value = isset($input[$this->name]) ? $input[$this->name] : null; } - + public function setDefault($in) { $this->defaultValue = $in; } + + public function setCssClass($in) { + $this->cssCLass = $in; + } + + public function setCssStyle($in) { + $this->cssStyle = $in; + } public function setForceDefault($in) { if(!is_bool($in)) throw new Exception("Force Default must be of type Bool"); $this->forceDefault = $in; @@ -175,6 +194,13 @@ public function getForceDefault() { public function getRequired() { return $this->required; } + + /** + * @deprecated + */ + public function setStyle($in) { + $this->style = $in; + } } From fbf74242beafe690721f65572ecc86bcda46606b Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Fri, 6 May 2011 14:20:48 +0100 Subject: [PATCH 008/164] Form handler includes elements custom css classes and style --- classes/widgets/form/widget_Form.php | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/classes/widgets/form/widget_Form.php b/classes/widgets/form/widget_Form.php index 9e0a896..c8f9723 100644 --- a/classes/widgets/form/widget_Form.php +++ b/classes/widgets/form/widget_Form.php @@ -26,9 +26,10 @@ public function __construct($formName = null, $autoLoadFormData = true) { public function __get($name) { $matches = null; if(preg_match('/^value_(.+)$/', $name, $matches)) { - return call_user_func(array($this, 'value'), $matches [1]); - } - throw new Exception('Undefined method:' . $name); + return call_user_func(array($this, 'value'), $matches [1]); + } elseif (preg_match('/^element_(.+)$/', $name, $matches)) { + return call_user_func(array($this, 'element'), $matches [1]); + } else throw new Exception('Undefined method:' . $name); } public function setName($in) { @@ -96,7 +97,15 @@ public function add($elementClass, $args) { // css style if(array_key_exists('style',$args)) $element->setStyle($args['style']); - + + // css style + if(array_key_exists('cssStyle',$args)) + $element->setCssStyle($args['cssStyle']); + + // css style + if(array_key_exists('cssClass',$args)) + $element->setCssClass($args['cssClass']); + if(array_key_exists('force_default', $args)) $element->setForceDefault($args['force_default']); @@ -122,6 +131,15 @@ public function value($elementName) { return $this->elementMap[$elementName]->getValue(); } + /* Returns an element */ + public function element ($elementName) { + + if(!array_key_exists($elementName, $this->elementMap)) + throw new Exception("Element not found: ".$elementName); + + return $this->elementMap[$elementName]; + } + public function getSubmitted() { return $this->submitted; } From 343c7208d914cbd586c97e70943208e4f325cddb Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Fri, 6 May 2011 14:22:24 +0100 Subject: [PATCH 009/164] Fixed bug with AgeRangeElement value storage --- classes/widgets/form/elements/widget_AgeRangeElement.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/classes/widgets/form/elements/widget_AgeRangeElement.php b/classes/widgets/form/elements/widget_AgeRangeElement.php index 8c1c3cf..8f8f0ee 100644 --- a/classes/widgets/form/elements/widget_AgeRangeElement.php +++ b/classes/widgets/form/elements/widget_AgeRangeElement.php @@ -16,10 +16,10 @@ function renderElement() { $date = $this->getValue(); - - $minValue = is_array($date) && array_key_exists(0,$date)?$date[0]:null; - $maxValue = is_array($date) && array_key_exists(1,$date)?$date[1]:null; - + + $minValue = is_array($date) && array_key_exists('min',$date)?$date['min']:null; + $maxValue = is_array($date) && array_key_exists('max',$date)?$date['max']:null; + $minOptions = sfl(""); From b305164c36cd1dbd4e0ff97352ad40a3d3462221 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 10 May 2011 15:48:13 +0100 Subject: [PATCH 010/164] added hooks for js onblur onfocus onkeydown onchange in the widget_TextElement --- .../form/elements/widget_TextElement.php | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/classes/widgets/form/elements/widget_TextElement.php b/classes/widgets/form/elements/widget_TextElement.php index 81c6cae..7660588 100644 --- a/classes/widgets/form/elements/widget_TextElement.php +++ b/classes/widgets/form/elements/widget_TextElement.php @@ -5,21 +5,42 @@ class widget_TextElement extends widget_AbstractElement { protected $htmlType = 'text'; protected $cssClassName = 'inputText'; protected $placeholder = ''; + protected $onChange = null; + protected $onKeydown = null; + protected $onFocus = null; + protected $onBlur = null; protected $rows; public function __construct($args) { if (array_key_exists('placeholder', $args) && strlen($args['placeholder'])) $this->placeholder = $args['placeholder']; + + if (array_key_exists('onChange', $args) && strlen($args['onChange'])) + $this->onChange = $args['onChange']; + + if (array_key_exists('onKeydown', $args) && strlen($args['onKeydown'])) + $this->onKeydown = $args['onKeydown']; + + if (array_key_exists('onFocus', $args) && strlen($args['onFocus'])) + $this->onFocus = $args['onFocus']; + + if (array_key_exists('onBlur', $args) && strlen($args['onBlur'])) + $this->onBlur = $args['onBlur']; + } function renderElement() { - return(sf('', + return(sf('', $this->htmlType, $this->getName(), parent::makeInputSafe($this->getValue()), $this->getName(), $this->cssClassName, - strlen($this->placeholder)?sf(' placeholder="%s"', $this->placeholder):'' + strlen($this->placeholder)?sf(' placeholder="%s"', $this->placeholder):'', + !is_null($this->onChange) && strlen($this->onChange)?sf(' onChange="%s"', $this->onChange):'', + !is_null($this->onKeydown) && strlen($this->onKeydown)?sf(' onKeydown="%s"', $this->onKeydown):'', + !is_null($this->onFocus) && strlen($this->onFocus)?sf(' onFocus="%s"', $this->onFocus):'', + !is_null($this->onBlur) && strlen($this->onBlur)?sf(' onBlur="%s"', $this->onBlur):'' )); } } From 467f6ac39a666d023eacfa203290f627960384b5 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Wed, 11 May 2011 14:51:50 +0100 Subject: [PATCH 011/164] Added onClick event hook-in to checkbox element --- classes/widgets/form/elements/widget_CheckBoxElement.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/classes/widgets/form/elements/widget_CheckBoxElement.php b/classes/widgets/form/elements/widget_CheckBoxElement.php index 623a01c..e554f95 100644 --- a/classes/widgets/form/elements/widget_CheckBoxElement.php +++ b/classes/widgets/form/elements/widget_CheckBoxElement.php @@ -2,9 +2,13 @@ class widget_CheckBoxElement extends widget_AbstractElement { + protected $onClick; public function __construct($args) { + if (array_key_exists('onClick', $args) && strlen($args['onClick'])) + $this->onClick = $args['onClick']; + } public function getValue() { @@ -15,10 +19,11 @@ public function setValue($input, $files = null) { $this->value =(isset($input[$this->name]) && $input[$this->name] == 'on') ? true : false; } function renderElement() { - return(sf("", + return(sf("", $this->getName(), $this->getValue() ? "checked='checked'" : "", - $this->getName() + $this->getName(), + !is_null($this->onClick) && strlen($this->onClick)?sf(' onClick="%s"', $this->onClick):'' )); } } From 4e0217137dcbeb4028a07f380a817126cef108ca Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Sat, 21 May 2011 12:18:50 +0100 Subject: [PATCH 012/164] Made the generateUrl method in pagination public rather than private. Can be useful for the view. --- classes/widgets/pagination/widget_Paginate.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/classes/widgets/pagination/widget_Paginate.php b/classes/widgets/pagination/widget_Paginate.php index b4a5ce8..0edb2f5 100644 --- a/classes/widgets/pagination/widget_Paginate.php +++ b/classes/widgets/pagination/widget_Paginate.php @@ -67,7 +67,7 @@ public function __toString() { return $this->render(); } - private function generateUrl($page) { + public function generateUrl($page) { return str_replace("[PAGE]", $page, $this->url); } @@ -76,6 +76,11 @@ public function getPageCount() { return $this->pageCount; } + /* returns total page count */ + public function getTotalResults() { + return $this->recordCount; + } + /* returns current page */ public function getCurrentPage() { return $this->currentPage; From 62c843f37701a6f4525b52103bcc5f847165fb52 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Wed, 25 May 2011 14:53:39 +0100 Subject: [PATCH 013/164] added getResultsPerPage method to paginate. Didn't want to use limit incase we require that later for something else. --- classes/widgets/pagination/widget_Paginate.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/classes/widgets/pagination/widget_Paginate.php b/classes/widgets/pagination/widget_Paginate.php index 0edb2f5..f68fc8e 100644 --- a/classes/widgets/pagination/widget_Paginate.php +++ b/classes/widgets/pagination/widget_Paginate.php @@ -101,6 +101,12 @@ public function getLimit() { return $this->resultsPerPage; } + + + public function getResultsPerPage() { + return $this->resultsPerPage; + } + /* set the format template (default is classic) */ public function setFormatTemplate ($template = 1) { From 6ab34bd99ec465f7c34687bf1487c006582ae908 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Sat, 4 Jun 2011 19:33:28 +0100 Subject: [PATCH 014/164] Registered the class autoloader and made compatible with Smarty 3.x --- classes/core/loader/atsumi_Loader.php | 15 ++++++++------- .../mvc/view_handlers/mvc_SmartyViewHandler.php | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/classes/core/loader/atsumi_Loader.php b/classes/core/loader/atsumi_Loader.php index 0ce3111..66fbfc5 100644 --- a/classes/core/loader/atsumi_Loader.php +++ b/classes/core/loader/atsumi_Loader.php @@ -264,9 +264,12 @@ public function _registerClass($classname, $filePath) { */ public function _loadClass($classname) { $classname = strtolower($classname); - if(!array_key_exists($classname, $this->classes)) - throw new loader_ClassNotFoundException('Atsumi failed to find the class required \''.$classname.'\'', $classname); + if(!array_key_exists($classname, $this->classes)) + return; + + //throw new loader_ClassNotFoundException('Atsumi failed to find the class required \''.$classname.'\'', $classname); + require_once($this->classes[$classname]); } @@ -299,10 +302,8 @@ static public function loadClass($className) { } /** - * Used internally by PHP to help attempt to load missing classes - * @param $className The class to load + * register the autoloader */ -function __autoload($classname) { - atsumi_Loader::loadClass($classname); -} +spl_autoload_register(array('atsumi_Loader', 'loadClass')); + ?> \ No newline at end of file diff --git a/classes/mvc/view_handlers/mvc_SmartyViewHandler.php b/classes/mvc/view_handlers/mvc_SmartyViewHandler.php index bf52385..858ce7e 100644 --- a/classes/mvc/view_handlers/mvc_SmartyViewHandler.php +++ b/classes/mvc/view_handlers/mvc_SmartyViewHandler.php @@ -16,7 +16,7 @@ public function render($viewName, $viewData) { if(is_null($this->smarty)) throw new mvc_TemplateEngineNotSupplied("Smarty has not been supplied"); - $this->smarty->clear_all_assign(); + $this->smarty->clearAllAssign(); $this->smarty->assign($viewData); $this->smarty->display($viewName); From 234b762f194b1ad758c8aed9871242e044fffb81 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Sun, 5 Jun 2011 17:39:12 +0100 Subject: [PATCH 015/164] Cut down the object formatting in the debug bar as was causing slow down when very large objects (eg: smarty 3.x) are passed down to the view. --- classes/core/debug/atsumi_Debug.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/classes/core/debug/atsumi_Debug.php b/classes/core/debug/atsumi_Debug.php index 1df4985..d877005 100644 --- a/classes/core/debug/atsumi_Debug.php +++ b/classes/core/debug/atsumi_Debug.php @@ -366,6 +366,7 @@ public function _addArea($name, $color) { * @return string A html5 valid string representation of the variable */ protected function format($value) { + if(is_null($value)) return 'NULL'; @@ -393,6 +394,8 @@ protected function format($value) { } if(is_object($value)) { + return sf('(Class) %s', get_class($value)); + /* if(method_exists($value,'toString')) return $value->toString(); @@ -405,6 +408,7 @@ protected function format($value) { $ret .= sf('
[%s] => %s
', $key, $this->format($item)); $ret .= ')'; return $ret; + */ } return sf('(%s)%s', gettype($value), $value); } @@ -667,6 +671,7 @@ public function _render() { if(!$this->active) return; + $this->startTimer(); $display =(isset($_COOKIE['debugDisplay']) ? strtolower($_COOKIE['debugDisplay']) : 'console'); ob_start(); From 628d145ac6b66bad3c6af4880d3f27a95b258151 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Wed, 8 Jun 2011 12:53:46 +0100 Subject: [PATCH 016/164] added a has() method to controllers to check if view data has been set --- classes/mvc/controllers/mvc_AbstractController.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/classes/mvc/controllers/mvc_AbstractController.php b/classes/mvc/controllers/mvc_AbstractController.php index 0285893..ad50595 100644 --- a/classes/mvc/controllers/mvc_AbstractController.php +++ b/classes/mvc/controllers/mvc_AbstractController.php @@ -12,6 +12,10 @@ abstract class mvc_AbstractController { // GET FUNCTIONS + public function has($key) { + return (array_key_exists($key, $this->data)); + } + public function get($key) { if(!array_key_exists($key, $this->data)) throw new Exception("Item does not exist in view data.."); From eec2b0e08f189180f289ca66c8929a5949774f82 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Fri, 10 Jun 2011 14:23:04 +0100 Subject: [PATCH 017/164] minor change to inline comments --- classes/core/app/atsumi_AppHandler.php | 4 ++-- classes/session/session_Handler.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/classes/core/app/atsumi_AppHandler.php b/classes/core/app/atsumi_AppHandler.php index 288f0da..e15abb3 100644 --- a/classes/core/app/atsumi_AppHandler.php +++ b/classes/core/app/atsumi_AppHandler.php @@ -246,7 +246,7 @@ public function process() { atsumi_Debug::record('Controller PostProcess', 'After the controllers method was called the post-process function was executed', null, true); // Log the whole processing time - atsumi_Debug::record('Controller Processing Compleate', 'All processing was compleated successfully', null, true); + atsumi_Debug::record('Controller Processing Complete', 'All processing was completed successfully', null, true); } /** @@ -294,7 +294,7 @@ public function render() { atsumi_Debug::record('Controller PostRender', 'After the rendering was processed the post-render function was executed', null, true); // Log the whole processing time - atsumi_Debug::record('Rendering Compleate', 'All rendering was compleated successfully', null, true); + atsumi_Debug::record('Rendering Complete', 'All rendering was completed successfully', null, true); } } ?> \ No newline at end of file diff --git a/classes/session/session_Handler.php b/classes/session/session_Handler.php index 701ff1b..f7a90f0 100644 --- a/classes/session/session_Handler.php +++ b/classes/session/session_Handler.php @@ -64,7 +64,7 @@ class session_Handler { * Constructor - Must use getInstance to get a session singalton object */ protected function __construct($options = array()) { - // Start timer for constructor compleate time + // Start timer for constructor complete time atsumi_Debug::startTimer(); $this->configure($options); From 45f7307c839f1342d2469d9f1217f0bf90e84b10 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Wed, 15 Jun 2011 17:02:04 +0100 Subject: [PATCH 018/164] minor var name fix --- classes/validators/validate_MinAge.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/validators/validate_MinAge.php b/classes/validators/validate_MinAge.php index 3ec4fea..97e8087 100644 --- a/classes/validators/validate_MinAge.php +++ b/classes/validators/validate_MinAge.php @@ -2,7 +2,7 @@ class validate_MinAge extends validate_AbstractValidator { - protected $maxAge; + protected $minAge; public function __construct($minAge) { $this->minAge = $minAge; From 541247fa513f61086b8106b3009e7c9f444a02cb Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 16 Aug 2011 11:18:57 +0100 Subject: [PATCH 019/164] added Atsumi::error__listen(Exception ) method. This tells the error handler to listen to the exception but don't recover from it. Useful for debugging --- classes/core/Atsumi.php | 7 ++ classes/core/error/atsumi_ErrorHandler.php | 12 ++- license.txt | 102 ++++++++++----------- 3 files changed, 67 insertions(+), 54 deletions(-) diff --git a/classes/core/Atsumi.php b/classes/core/Atsumi.php index d294521..4f86c3f 100644 --- a/classes/core/Atsumi.php +++ b/classes/core/Atsumi.php @@ -208,6 +208,13 @@ public static function error__setFloodControl(cache_HandlerInterface $cacheManag $args = func_get_args(); return self::__callStatic(__FUNCTION__, $args); } + + public static function error__listen(Exception $e) { + $args = func_get_args(); + return self::__callStatic(__FUNCTION__, $args); + } + + } ?> \ No newline at end of file diff --git a/classes/core/error/atsumi_ErrorHandler.php b/classes/core/error/atsumi_ErrorHandler.php index ec32acf..a73b25e 100644 --- a/classes/core/error/atsumi_ErrorHandler.php +++ b/classes/core/error/atsumi_ErrorHandler.php @@ -182,7 +182,7 @@ public function handleError($errNumber, $errString, $errFile, $errLine, $errCont * @access public * @param Exception $e The exception that was thrown */ - public function handleException(Exception $e) { + public function handleException(Exception $e, $recover = true) { try { // fires the exception_fc event if not blocked by flood control @@ -191,8 +191,11 @@ public function handleException(Exception $e) { // fire the exception event regardless of flood control $this->fireEvent(self::EVENT_EXCEPTION, new atsumi_ErrorEventArgs($e, &$this->recoverer)); - - $this->recoverer->recover($e); + + if ($recover) + $this->recoverer->recover($e); + else + return; } catch(Exception $e) { exit(__CLASS__.' Error: '.$e->getMessage()."\n".$e->getFile().' #'.$e->getLine(). PHP_EOL.$e->getTraceAsString()); } @@ -224,5 +227,8 @@ protected function fireEvent($eventType, atsumi_EventArgs $args = null) { if ($eventType == self::EVENT_EXCEPTION_FC) $this->recordInFloodControl(get_class($args->exception), $args->exception->getLine(), $args->exception->getFile()); } + public function listen ($e) { + $this->handleException($e, false); + } } ?> \ No newline at end of file diff --git a/license.txt b/license.txt index 114d2e0..48e76ca 100644 --- a/license.txt +++ b/license.txt @@ -1,52 +1,52 @@ -Copyright (c) 2008, James A. Forrester-Fellowes -All rights reserved. - +Copyright (c) 2008, James A. Forrester-Fellowes +All rights reserved. + This license is a legal agreement between you and James A. Forrester-Fellowes. -for the use of Atsumi Framrwork (the "Software"). By obtaining the Software you -agree to comply with the terms and conditions of this license. - -PERMITTED USE -You are permitted to use, copy, modify, and distribute the Software and its -documentation, with or without modification, for any purpose, provided that -the following conditions are met: - -1. A copy of this license agreement must be included with the distribution. - -2. Redistributions of source code must retain the above copyright notice in - all source code files. - -3. Redistributions in binary form must reproduce the above copyright notice - in the documentation and/or other materials provided with the distribution. - -4. Any files that have been modified must carry notices stating the nature - of the change and the names of those who changed them. - -5. Products derived from the Software must include an acknowledgment that - they are derived from Atsumi in their documentation and/or other - materials provided with the distribution. - -6. Products derived from the Software may not be called "Atsumi", - nor may "Atsumi" appear in their name, without prior written - permission from James A. Forrester-Fellowes. - - -INDEMNITY -You agree to indemnify and hold harmless the authors of the Software and -any contributors for any direct, indirect, incidental, or consequential -third-party claims, actions or suits, as well as any related expenses, -liabilities, damages, settlements or fees arising from your use or misuse -of the Software, or a violation of any terms of this license. - -DISCLAIMER OF WARRANTY -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR -IMPLIED, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF QUALITY, PERFORMANCE, -NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. - -LIMITATIONS OF LIABILITY -YOU ASSUME ALL RISK ASSOCIATED WITH THE INSTALLATION AND USE OF THE SOFTWARE. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS OF THE SOFTWARE BE LIABLE -FOR CLAIMS, DAMAGES OR OTHER LIABILITY ARISING FROM, OUT OF, OR IN CONNECTION -WITH THE SOFTWARE. LICENSE HOLDERS ARE SOLELY RESPONSIBLE FOR DETERMINING THE -APPROPRIATENESS OF USE AND ASSUME ALL RISKS ASSOCIATED WITH ITS USE, INCLUDING -BUT NOT LIMITED TO THE RISKS OF PROGRAM ERRORS, DAMAGE TO EQUIPMENT, LOSS OF -DATA OR SOFTWARE PROGRAMS, OR UNAVAILABILITY OR INTERRUPTION OF OPERATIONS. +for the use of Atsumi Framrwork (the "Software"). By obtaining the Software you +agree to comply with the terms and conditions of this license. + +PERMITTED USE +You are permitted to use, copy, modify, and distribute the Software and its +documentation, with or without modification, for any purpose, provided that +the following conditions are met: + +1. A copy of this license agreement must be included with the distribution. + +2. Redistributions of source code must retain the above copyright notice in + all source code files. + +3. Redistributions in binary form must reproduce the above copyright notice + in the documentation and/or other materials provided with the distribution. + +4. Any files that have been modified must carry notices stating the nature + of the change and the names of those who changed them. + +5. Products derived from the Software must include an acknowledgment that + they are derived from Atsumi in their documentation and/or other + materials provided with the distribution. + +6. Products derived from the Software may not be called "Atsumi", + nor may "Atsumi" appear in their name, without prior written + permission from James A. Forrester-Fellowes. + + +INDEMNITY +You agree to indemnify and hold harmless the authors of the Software and +any contributors for any direct, indirect, incidental, or consequential +third-party claims, actions or suits, as well as any related expenses, +liabilities, damages, settlements or fees arising from your use or misuse +of the Software, or a violation of any terms of this license. + +DISCLAIMER OF WARRANTY +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR +IMPLIED, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF QUALITY, PERFORMANCE, +NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. + +LIMITATIONS OF LIABILITY +YOU ASSUME ALL RISK ASSOCIATED WITH THE INSTALLATION AND USE OF THE SOFTWARE. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS OF THE SOFTWARE BE LIABLE +FOR CLAIMS, DAMAGES OR OTHER LIABILITY ARISING FROM, OUT OF, OR IN CONNECTION +WITH THE SOFTWARE. LICENSE HOLDERS ARE SOLELY RESPONSIBLE FOR DETERMINING THE +APPROPRIATENESS OF USE AND ASSUME ALL RISKS ASSOCIATED WITH ITS USE, INCLUDING +BUT NOT LIMITED TO THE RISKS OF PROGRAM ERRORS, DAMAGE TO EQUIPMENT, LOSS OF +DATA OR SOFTWARE PROGRAMS, OR UNAVAILABILITY OR INTERRUPTION OF OPERATIONS. From 614bafb8815306278123776cf96d452c61d928bf Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Fri, 19 Aug 2011 11:24:29 +0100 Subject: [PATCH 020/164] Added content-length header to the text view --- classes/mvc/views/mvc_TxtView.php | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/classes/mvc/views/mvc_TxtView.php b/classes/mvc/views/mvc_TxtView.php index 076cfcc..6ef2d4a 100644 --- a/classes/mvc/views/mvc_TxtView.php +++ b/classes/mvc/views/mvc_TxtView.php @@ -1,14 +1,11 @@ get_txt); } @@ -16,11 +13,9 @@ public function render() { // headers public function setHeaders() { header(sf('Content-Type: text/plain; charset=%s', $this->getCharset())); + header(sf('Content-Length: %s', strlen($this->get_txt))); } - - - - + } ?> From e7aba5c9228e904f98106a9a7a810b0f3a2e6378 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Fri, 19 Aug 2011 16:28:00 +0100 Subject: [PATCH 021/164] Added a Math utility class --- classes/helpers/math/atsumi_Math.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 classes/helpers/math/atsumi_Math.php diff --git a/classes/helpers/math/atsumi_Math.php b/classes/helpers/math/atsumi_Math.php new file mode 100644 index 0000000..12c7ed7 --- /dev/null +++ b/classes/helpers/math/atsumi_Math.php @@ -0,0 +1,21 @@ + $intMax) $out = $in - $intMax * 2 - 2; + else $out = $in; + + return $out; + } + +} +?> \ No newline at end of file From c5154f7d8c88a16af7e3c4e7252a1aa678695a43 Mon Sep 17 00:00:00 2001 From: Jonathan Stirling Date: Fri, 2 Sep 2011 09:37:59 +0100 Subject: [PATCH 022/164] Changes to open framework up to CLI items --- .../exceptions/app_InvalidUsageException.php | 37 +++++++++++++++++++ .../core/app/parser/claparser_Interface.php | 23 ++++++++++++ .../core/app/parser/claparser_Standard.php | 19 ++++++++++ 3 files changed, 79 insertions(+) create mode 100644 classes/core/app/exceptions/app_InvalidUsageException.php create mode 100644 classes/core/app/parser/claparser_Interface.php create mode 100644 classes/core/app/parser/claparser_Standard.php diff --git a/classes/core/app/exceptions/app_InvalidUsageException.php b/classes/core/app/exceptions/app_InvalidUsageException.php new file mode 100644 index 0000000..14c0fce --- /dev/null +++ b/classes/core/app/exceptions/app_InvalidUsageException.php @@ -0,0 +1,37 @@ + diff --git a/classes/core/app/parser/claparser_Interface.php b/classes/core/app/parser/claparser_Interface.php new file mode 100644 index 0000000..f9028ff --- /dev/null +++ b/classes/core/app/parser/claparser_Interface.php @@ -0,0 +1,23 @@ + diff --git a/classes/core/app/parser/claparser_Standard.php b/classes/core/app/parser/claparser_Standard.php new file mode 100644 index 0000000..52f034d --- /dev/null +++ b/classes/core/app/parser/claparser_Standard.php @@ -0,0 +1,19 @@ + From 971d73904154d2c8f23c5e4ce2e269704dede4cd Mon Sep 17 00:00:00 2001 From: Jonathan Stirling Date: Fri, 2 Sep 2011 09:39:08 +0100 Subject: [PATCH 023/164] Support CLI items --- classes/core/app/atsumi_AppHandler.php | 69 ++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 9 deletions(-) diff --git a/classes/core/app/atsumi_AppHandler.php b/classes/core/app/atsumi_AppHandler.php index e15abb3..90ce15a 100644 --- a/classes/core/app/atsumi_AppHandler.php +++ b/classes/core/app/atsumi_AppHandler.php @@ -44,6 +44,13 @@ class atsumi_AppHandler { */ private $uri; + /** + * The command to be parsed and processed + * @access private + * @var string + */ + private $command; + /** * Holds the base path of atsumi relative to the domain * @access private @@ -58,6 +65,13 @@ class atsumi_AppHandler { */ private $uriParser = 'uriparser_Gyokuro'; + /** + * The command parser classname or instance to use + * @access private + * @var string|object + */ + private $claParser = 'claparser_Standard'; + /** * The controller instance generated based on the parser data * @access private @@ -108,6 +122,16 @@ public function getParserMetaData() { // SET FUNCTIONS + /** + * Sets the command to be parsed and processed + * @access public + * @param string $path + */ + public function setCommand($command) { + $this->command = $command; + } + + /** * Sets the uri to be parsed and processed * @access public @@ -166,13 +190,18 @@ public function createUri($controller, $method) { * @param string $uri The uri to processed */ public function go($uri) { - - $scriptArr = explode('/',$_SERVER['SCRIPT_NAME']); - $baseUri = str_replace(array($_SERVER['DOCUMENT_ROOT'], end($scriptArr)), '', $_SERVER['SCRIPT_FILENAME']); - $this->setBaseUri($baseUri); - - $this->setUri($uri); - $this->parseUri(); + if($this->settings->get_cli === true) { + $this->setBaseUri('.'); + $this->setCommand($uri); + $this->parseCommand(); + } else { + $scriptArr = explode('/',$_SERVER['SCRIPT_NAME']); + $baseUri = str_replace(array($_SERVER['DOCUMENT_ROOT'], end($scriptArr)), '', $_SERVER['SCRIPT_FILENAME']); + $this->setBaseUri($baseUri); + + $this->setUri($uri); + $this->parseUri(); + } $this->process(); } @@ -205,6 +234,29 @@ public function parseUri() { array_merge(array('path' => $this->uri), $this->parserData), true); } + public function parseCommand() { + atsumi_Debug::startTimer(); + if(!in_array('claparser_Interface', class_implements($this->claParser))) { + throw new Exception('Command Line Argument parser must implement claparser_Interface'); + } + if(is_string($this->claParser)) { + $this->claParser = new $this->claParser(); + } + $parseData = $this->claParser->parseCommand($this->command, $this->settings->init_specification); + + $this->parserData = array( + 'controller' => $parseData['controller'], + 'method' => $parseData['method'], + 'args' => $parseData['args'] + ); + + atsumi_Debug::setParserData($parseData); + atsumi_Debug::record('Command Parsing', + 'Command was parsed to determine the controller, method and args.', + array_merge(array('path' => $this->command), $this->parserData), true); + + } + /** * Processes the controller and method choosen by the parser * Note: parseUri method must be execute before this method @@ -214,7 +266,6 @@ public function process() { // Could possibly be a fragment of the spec if(!is_string($this->parserData['controller'])) throw new Exception('Path parsing error, please report to developement team'); - if(!class_exists($this->parserData['controller'])) throw new Exception('Could not find required controller: '.$this->parserData['controller']); @@ -297,4 +348,4 @@ public function render() { atsumi_Debug::record('Rendering Complete', 'All rendering was completed successfully', null, true); } } -?> \ No newline at end of file +?> From 86bbedcb3d57104d1e79f08f57b9b36e8edb08f6 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Fri, 16 Sep 2011 13:06:12 +0100 Subject: [PATCH 024/164] fix bug in db classes still referenced parsers rather than casters --- classes/database/db_AbstractDatabase.php | 2 +- classes/database/vender/postgresql/db_PostgreSQL.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/classes/database/db_AbstractDatabase.php b/classes/database/db_AbstractDatabase.php index a437b13..0a374a3 100644 --- a/classes/database/db_AbstractDatabase.php +++ b/classes/database/db_AbstractDatabase.php @@ -157,7 +157,7 @@ protected function connectReal($conString, $config) { */ protected function cast($args) { if (is_null($this->caster)) - throw new db_Exception('Parser not loaded.'); + throw new db_Exception('Caster not loaded.'); return $this->caster->castString(func_get_args()); } diff --git a/classes/database/vender/postgresql/db_PostgreSQL.php b/classes/database/vender/postgresql/db_PostgreSQL.php index 95fa269..c4b8377 100644 --- a/classes/database/vender/postgresql/db_PostgreSQL.php +++ b/classes/database/vender/postgresql/db_PostgreSQL.php @@ -46,7 +46,7 @@ public function connect($config = array()) { * Initalise the vender type caster */ protected function initCaster() { - $this->parser = new caster_PostgreSQL(); + $this->caster = new caster_PostgreSQL(); } /** From 0f46cb250fb9f2f8d625dd7eadde44ae5da2c7cf Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Fri, 16 Sep 2011 15:57:39 +0100 Subject: [PATCH 025/164] Started to add row abstraction to database results --- classes/database/db_AbstractDatabase.php | 15 ++++++++- classes/database/dp_AbstractRow.php | 31 +++++++++++++++++++ .../db_RowColumnNotFoundException.php | 9 ++++++ .../vender/postgresql/db_PostgreSQL.php | 5 +++ .../vender/postgresql/db_PostgreSQLRow.php | 12 +++++++ 5 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 classes/database/dp_AbstractRow.php create mode 100644 classes/database/exceptions/db_RowColumnNotFoundException.php create mode 100644 classes/database/vender/postgresql/db_PostgreSQLRow.php diff --git a/classes/database/db_AbstractDatabase.php b/classes/database/db_AbstractDatabase.php index 0a374a3..87614cd 100644 --- a/classes/database/db_AbstractDatabase.php +++ b/classes/database/db_AbstractDatabase.php @@ -170,6 +170,19 @@ public function disconnect() { $this->pdo = null; $this->connected = false; } + + + public function formatResult ($rows) { + + $rowArr = array(); + + foreach ($rows as $idx => $row) + $rowArr[] = $this->createRow($row); + + return $rowArr; + } + + /** * Executes a basic query with casted variables @@ -179,7 +192,7 @@ public function disconnect() { */ public function query($query, $args = null, $_ = null) { $args = func_get_args(); - return $this->queryReal(call_user_func_array(array(&$this, 'cast'), $args)); + return $this->formatResult($this->queryReal(call_user_func_array(array(&$this, 'cast'), $args))); } /** diff --git a/classes/database/dp_AbstractRow.php b/classes/database/dp_AbstractRow.php new file mode 100644 index 0000000..7036230 --- /dev/null +++ b/classes/database/dp_AbstractRow.php @@ -0,0 +1,31 @@ +data = $rowData; + } + + public function cast ($format, $column) { + + if (!array_key_exists($column, $this->rowData)) + throw new db_RowColumnNotFoundException('Column not found: '.$column); + + $data = $this->date[$column]; + + $caster = $this->initCaster(); + + return $caster->cast('%'.$format, $data); + + + } + + +} + +?> \ No newline at end of file diff --git a/classes/database/exceptions/db_RowColumnNotFoundException.php b/classes/database/exceptions/db_RowColumnNotFoundException.php new file mode 100644 index 0000000..6882c0e --- /dev/null +++ b/classes/database/exceptions/db_RowColumnNotFoundException.php @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/classes/database/vender/postgresql/db_PostgreSQL.php b/classes/database/vender/postgresql/db_PostgreSQL.php index c4b8377..e9f461e 100644 --- a/classes/database/vender/postgresql/db_PostgreSQL.php +++ b/classes/database/vender/postgresql/db_PostgreSQL.php @@ -42,6 +42,11 @@ public function connect($config = array()) { $this->connectReal($conString, $config); } + protected function createRow ($rowData) { + return new db_PostgreSQLRow($rowData); + } + + /** * Initalise the vender type caster */ diff --git a/classes/database/vender/postgresql/db_PostgreSQLRow.php b/classes/database/vender/postgresql/db_PostgreSQLRow.php new file mode 100644 index 0000000..343ead3 --- /dev/null +++ b/classes/database/vender/postgresql/db_PostgreSQLRow.php @@ -0,0 +1,12 @@ +caster = new caster_PostgreSQLToPhp(); + } + +} + +?> + From 5d17d6cc5b016954091fcb3b9e30c975fa315065 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 19 Sep 2011 12:56:35 +0100 Subject: [PATCH 026/164] Added castArray and castArraySets to casters, used by DB. Abstract Db now parsing update queries. --- classes/caster/caster_Abstract.php | 17 +++++++++++++++++ classes/database/db_AbstractDatabase.php | 13 ++++++++----- classes/database/dp_AbstractRow.php | 5 +++++ 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/classes/caster/caster_Abstract.php b/classes/caster/caster_Abstract.php index 61b7752..6bb4fe3 100644 --- a/classes/caster/caster_Abstract.php +++ b/classes/caster/caster_Abstract.php @@ -79,6 +79,23 @@ public function castString($string, $args = null, $_ = null) { } return implode('', $ret); } + + public function castArray ($params) { + $format = array_shift ($params); + return $this->castReal ($format, $params); + } + + public function castArraySets ($args) { + $ret = array (); + for ($i = 0; $i < count ($args); ) { + $format = $args [$i++]; + $params = array_slice ($args, $i, $this->numberOfPercents($format)); + $i += count ($params); + array_unshift ($params, $format); + $ret[] =$this->castArray ($params); + } + return $ret; + } /** * Casts args into a given string by the class held caster spec diff --git a/classes/database/db_AbstractDatabase.php b/classes/database/db_AbstractDatabase.php index 87614cd..e58523c 100644 --- a/classes/database/db_AbstractDatabase.php +++ b/classes/database/db_AbstractDatabase.php @@ -220,8 +220,7 @@ public function queryReal($sql) { throw new PDOException((isset($error[2]) ? $error[2] : 'Database Error: '.$error[0])); } - $data = $return->fetchAll(PDO::FETCH_OBJ); - + $data = $return->fetchAll(PDO::FETCH_ASSOC); if(!is_array($data)) throw new PDOException('Failed to return data array'); @@ -363,14 +362,18 @@ public function updateOne($args) { } public function parseUpdateQuery($args) { + + if (is_null($this->caster)) + throw new db_Exception('Caster not loaded.'); + $args = func_get_args (); - $sets = $this->quotef_special($args); + $sets = $this->caster->castArraySets($args); $update = array_shift ($sets); $where = array_shift ($sets); if (count ($sets) == 0) $sets = array ($where); - + /* return update query string */ - return $this->format( + return $this->caster->castString( 'UPDATE %l SET %l WHERE %l', $update, implode(', ', $sets), $where ); } diff --git a/classes/database/dp_AbstractRow.php b/classes/database/dp_AbstractRow.php index 7036230..7bedc0d 100644 --- a/classes/database/dp_AbstractRow.php +++ b/classes/database/dp_AbstractRow.php @@ -24,6 +24,11 @@ public function cast ($format, $column) { } + public function get ($format, $column) { + + return $this->data[$column]; + + } } From 56ef08a31f5c1ce903a0b1059453bdf02a5f9881 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 19 Sep 2011 16:49:49 +0100 Subject: [PATCH 027/164] Db abstraction (postgres) now supports insert() update() select/query() fetch(). Needs delete() count() insertIfNotExists() --- classes/caster/caster_Abstract.php | 2 +- classes/database/db_AbstractDatabase.php | 170 ++++---- classes/database/vender/mysql/db_MySql.php | 397 ------------------ .../vender/postgresql/db_PostgreSQL.php | 2 +- 4 files changed, 95 insertions(+), 476 deletions(-) diff --git a/classes/caster/caster_Abstract.php b/classes/caster/caster_Abstract.php index 6bb4fe3..bf19f67 100644 --- a/classes/caster/caster_Abstract.php +++ b/classes/caster/caster_Abstract.php @@ -37,7 +37,7 @@ abstract class caster_Abstract { * @param string $s The string to parse */ protected function numberOfPercents($s) { - if(!is_string($s)) throw new caster_Exception('Parameter $s should be of type string'); + if(!is_string($s)) throw new caster_Exception('Caster expecting format string, received '.gettype($s).': '.$s); $pos = 0; $count = 0; while(true) { diff --git a/classes/database/db_AbstractDatabase.php b/classes/database/db_AbstractDatabase.php index e58523c..d775a6d 100644 --- a/classes/database/db_AbstractDatabase.php +++ b/classes/database/db_AbstractDatabase.php @@ -175,7 +175,6 @@ public function disconnect() { public function formatResult ($rows) { $rowArr = array(); - foreach ($rows as $idx => $row) $rowArr[] = $this->createRow($row); @@ -244,25 +243,20 @@ public function queryReal($sql) { return $data; } catch(PDOException $e) { - throw new db_QueryFailedException($e->getMessage() . "
" . $sql); + throw new db_QueryFailedException($e->getMessage() . "

SQL:

" . $sql); } } - public function select($_) { + + + /* + * SELECT + * */ + public function select($args) { $args = func_get_args(); return call_user_func_array(array(&$this, 'query'), $args); } - - /** - * Performs a query, returning a single result - * @param $colums string The colum or colums to select - * @param $tables string The table or tables to query from - * @param $where string The clause to query by - * @param $args mixed Any args that should be used in the clause - * @param $_ Arg repeater (Repeat the last arg as many times as needed) - * @return The query results - */ - public function selectOne($colums, $tables, $where, $args = null, $_ = null) { + public function selectOne($args) { $args = func_get_args(); $result = call_user_func_array(array(&$this, 'select'), $args); @@ -272,23 +266,65 @@ public function selectOne($colums, $tables, $where, $args = null, $_ = null) { return array_key_exists(0, $result) ? $result[0] : null; } - /** - * Performs a query, returning all colums of one result in an array - * @param $tables string The table or tables to query from - * @param $where string The clause to query by - * @param $args mixed Any args that should be used in the clause - * @param $_ Arg repeater (Repeat the last arg as many times as needed) - * @return The query results - */ - public function fetchOne($tables, $where, $args = null, $_ = null) { + + + + /* FETCH + * abstract select - for simple queries (no joins) */ + public function fetch ($cols, $table, $where = null, $offset = null, $limit = null) { + /* parse the query */ $args = func_get_args(); - array_unshift($args, '*'); + $query = call_user_func_array (array ($this, 'parseFetchQuery'), $args); - return call_user_func_array(array(&$this, 'selectOne'), $args); + /* perform query */ + $this->query('%l', $query); + + return true; } + public function fetchOne($cols, $table, $where = null) { + $args = func_get_args(); + $result = call_user_func_array(array(&$this, 'fetch'), $args); + + if(count($result) > 1) + throw new db_QueryFailedException('fetchOne returned more than one result'); + return array_key_exists(0, $result) ? $result[0] : null; + } + public function parseFetchQuery($cols, $table, $where = null, $offset = null, $limit = null) { + + if (is_null($this->caster)) + throw new db_Exception('Caster not loaded.'); + + $args = func_get_args (); + $cols = array_shift ($args); + $table = array_shift ($args); + + // handle offset limit + $offset = null; + $limit = null; + + if (is_int(end($args))) $limit = array_pop($args); + if (is_int(end($args))) $offset = array_pop($args); + + if (count($args) && $args[0] !== null) { + $sets = $this->caster->castArraySets($args); + $where = array_shift ($sets); + } + // return select query string + return $this->caster->castString( + 'SELECT %l FROM %@%l%l%l', $cols, $table, + is_null($where)?'':$this->caster->castString(' WHERE %l', $where), + !is_int($offset)?'':$this->caster->castString(' OFFSET %i', $offset), + !is_int($limit)?'':$this->caster->castString(' LIMIT %i', $limit) + + ); + } + + + + /** - * Performs a insert query + * INSERT * * Example: * $db->insert('tableA', 'column1 = %s', 'a string', 'column2 = %i', 12345); @@ -300,42 +336,44 @@ public function fetchOne($tables, $where, $args = null, $_ = null) { * @throws db_Exception If the number of values does not match the number of columns */ public function insert($table, $column, $value = null, $_ = null) { + /* parse the query */ $args = func_get_args(); + $query = call_user_func_array (array ($this, 'parseInsertQuery'), $args); - $table = array_shift($args); - - // If there are still args left we have variable pairs - $names = array(); - $types = array(); - $values = array(); - while(count($args) >= 2) { - $column = array_shift($args); - - $column = explode('=', $column, 2); + /* perform query */ + $this->query('%l', $query); - $names[] = trim($column[0]); - $types[] = trim($column[1]); - $values[] = array_shift($args); + return true; + } + public function parseInsertQuery($args) { + + if (is_null($this->caster)) + throw new db_Exception('Caster not loaded.'); + + $args = func_get_args(); + $table = array_shift($args); + + + $rows = $this->caster->castArraySets($args); + $column = array(); + $data = array(); + + foreach($rows as $row) { + $rowParts = explode('=', $row); + $column[] = trim($rowParts[0]); + $data[] = trim($rowParts[1]); } - if(count($args) > 0) - throw new db_Exception('Uneven number of column value paires'); - - /* parse values */ - $valueString = implode(', ', $types); - array_unshift($values, $valueString); - $valueString = call_user_func_array(array($this, 'parse'), $values); - - - $query = $this->parse( - 'INSERT INTO %@ (%l) VALUES(%l)', $table, implode(', ', $names), $valueString + return $this->caster->castString( + 'INSERT INTO %@ (%l) VALUES(%l)', $table, implode(', ', $column), implode(', ', $data) ); - - $this->queryReal($query); } + - // TODO: public function insert($table, $column, $value = null, $_ = null) - + /* + * UPDATE + * + * */ public function update($args) { /* parse the query */ $args = func_get_args(); @@ -346,7 +384,6 @@ public function update($args) { return true; } - public function updateOne($args) { /* call update */ $args = func_get_args (); @@ -360,7 +397,6 @@ public function updateOne($args) { return true; } - public function parseUpdateQuery($args) { if (is_null($this->caster)) @@ -374,30 +410,10 @@ public function parseUpdateQuery($args) { /* return update query string */ return $this->caster->castString( - 'UPDATE %l SET %l WHERE %l', $update, implode(', ', $sets), $where + 'UPDATE %@ SET %l WHERE %l', $update, implode(', ', $sets), $where ); } - /* DEPRECATED METHODS */ - /** - * Selects a single record from the database - * @deprecated Back compatability functions. DO NOT USE! - * @param unknown_type $_ - */ - public function select_1($_) { - $args = func_get_args (); - return call_user_func_array (array (&$this, 'selectOne'), $args); - } - - /** - * Updates a single record in the database. - * @deprecated Back compatability functions. DO NOT USE! - * @param unknown_type $_ - */ - public function update_1($_) { - $args = func_get_args (); - return call_user_func_array (array (&$this, 'updateOne'), $args); - } } ?> \ No newline at end of file diff --git a/classes/database/vender/mysql/db_MySql.php b/classes/database/vender/mysql/db_MySql.php index 24d067b..865e554 100755 --- a/classes/database/vender/mysql/db_MySql.php +++ b/classes/database/vender/mysql/db_MySql.php @@ -7,402 +7,5 @@ */ class db_MySql extends db_AbstractDatabase { - protected $transaction = false; - - /** - * MySql Connect function, config options are: - * - * host : The hostname on which the database server resides - * port : The port number where the database server is listening - * dbname : The name of the database - * unix_socket : The MySQL Unix socket(ignore if host or port is set) - * username : The username used to authentificate - * password : The password used to authentificate - */ - public function connect($config = array()) { - $host =(isset($config['host']) ? $config['host'] : null); - $port =(isset($config['port']) ? $config['port'] : null); - $dbname =(isset($config['dbname']) ? $config['dbname'] : null); - - $unixSocket = null; - if(is_null($host) && is_null($port)) - $unixSocket =(isset($config['unix_socket']) ? $config['unix_socket'] : null); - - $conString = sf( - 'mysql:%s%s%s%s', - (!is_null($host) ? sf('host=%s;', $host) : ''), - (!is_null($port) ? sf('port=%s;', $port) : ''), - (!is_null($dbname) ? sf('dbname=%s;', $dbname) : ''), - (!is_null($unixSocket) ? sf('unix_socket=%s;', $unixSocket) : '') - ); - - $this->connectReal($conString, $config); - } - - /* CHARACTER ESCAPE FUNCTIONS */ - - /** - * Transforms a array into a query literal. - * @param string $val The value to escape. - * @param bool $nullValid If the value can be null - * @return string The escaped and quoted array. - */ - protected function a($val, $nullValid = false) { - return $val; - } - - /** - * Transforms a boolean into a query literal. - * @param string $val The value to escape. - * @param bool $nullValid If the value can be null - * @return string The escaped and quoted boolean. - */ - protected function b($val, $nullValid = false) { - return $val; - } - - /** - * Transforms a date into a query literal. - * @param string $val The value to escape. - * @param bool $nullValid If the value can be null - * @return string The escaped and quoted date. - */ - protected function d($val, $nullValid = false) { - return $val; - } - - /** - * Transforms a string into a query literal ecripted representation. - * @param string $val The value to escape. - * @param bool $nullValid If the value can be null - * @return string The escaped, quoted and encripted string. - */ - protected function e($val, $nullValid = false) { - return $val; - } - - /** - * Transforms a float into a query literal. - * @param string $val The value to escape. - * @param bool $nullValid If the value can be null - * @return string The escaped and quoted float. - */ - protected function f($val, $nullValid = false) { - return $val; - } - - /** - * Transforms a string into a query literal hash. - * @param string $val The value to escape. - * @param bool $nullValid If the value can be null - * @return string The escaped, quoted and hashed string. - */ - protected function h($val, $nullValid = false) { - return $val; - } - - /** - * Transforms an integer into a query literal. - * @param int $val The value to escape. - * @param bool $nullValid If the value can be null - * @return string The escaped and quoted integer. - */ - protected function i($val, $nullValid = false) { - $val =(int)$val; - - if(!$nullValid && !is_int($val)) - throw new db_TypeException('Value not an Integer'); - elseif(!is_int($val) || is_null($val)) - throw new db_TypeException('Value not an Integer or Null '.$val); - - return sf('%s',(is_null($val) ? 'NULL' : $val)); - } - - /** - * Transforms a string into a query literal. - * @param string $val The value to escape. - * @param bool $nullValid If the value can be null - * @return string The escaped and quoted string. - */ - protected function s($val, $nullValid = false) { - return sf("'%s'", $val); - } - - /** - * Transforms a timestamp into a query literal. - * @param string $val The value to escape. - * @param bool $nullValid If the value can be null - * @return string The escaped and quoted timestamp. - */ - protected function t($val, $nullValid = false) { - return $val; - } - - /* TRANSACTION FUNCTIONS */ - - /** - * Returns true if the database supports transaction - * - * @return boolean If the database supports transactions - */ - public function transactionSupport() { - return true; - } - - /** - * Returns true if the database is in transaction - * - * @return boolean If the database is in transaction - */ - public function transaction() { - return $this->transaction; - } - - /** - * Begins a database transaction. Will fail if there is already - * a transaction in progress - * - * @return boolean If beginning the transaction was successful - */ - public function transactionBegin() { - if($this->transaction) - throw new db_Exception('Cannot call beginTransaction() while already in transaction'); - - $this->query('BEGIN'); - $this->transaction = true; - return true; - } - - /** - * Commits a database transaction. Will fail if there is no - * transaction in progress - * - * @return boolean If commiting the transaction was successful - */ - public function transactionCommit() { - if(!$this->transaction) - throw new db_Exception('Cannot call commitTransaction() while not in transaction'); - - $this->transaction = false; - return $this->query('COMMIT'); - } - - /** - * Rolls back a database transaction. Will fail if there is no - * transaction in progress - * - * @return boolean If rolling back the transaction was successful - */ - public function transactionRollback() { - if(!$this->transaction) - throw new db_Exception('Cannot call rollbackTransaction() While not in transaction'); - - $this->transaction = false; - return $this->query('ROLLBACK'); - } - - /** - * Rolls back a database transaction, if there is one in - * progress. This is designed for use in catch blocks to ensure - * a rollback in case of error - * - * @return null - */ - public function transactionAutoRollback() { - if($this->transaction) - $this->rollbackTransaction(); - } - - /* SELECT QUERIES */ - - /** - * Performs a query, returning all results in an array - * - * @param $colums string The colum or colums to select - * @param $tables string The table or tables to query from - * @param $where string The clause to query by - * @param $args mixed Any args that should be used in the clause - * @param $_ - * @return The query results - */ - public function select($colums, $tables, $where = null, $args = null, $_ = null) { - $args = func_get_args(); - - $colums = array_shift($args); - $tables = array_shift($args); - - // If there are still args left we have a where clause and possible where args - if(count($args) > 0) - $where = array_shift($args); - - if(is_array($colums)) - $colums = implode(', ', $colums); - - $query = sf( - 'SELECT %s FROM %s%s', - $colums, - $tables, - (isset($where) ? sf(' WHERE %s', $where) : '') - ); - - array_unshift($args, $query); - - return call_user_func_array(array(&$this, 'query'), $args); - } - - /** - * Performs a query, returning all colums of all results in an array - * - * @param $tables string The table or tables to query from - * @param $where string The clause to query by - * @param $args mixed Any args that should be used in the clause - * @param $_ - * @return The query results - */ - public function fetch($tables, $where = null, $args = null, $_ = null) { - $args = func_get_args(); - - array_unshift($args, '*'); - - return call_user_func_array(array(&$this, 'select'), $args); - } - - /** - * Checks for the existance of a record - * - * @param $tables string The table or tables to query from - * @param $where string The clause to query by - * @param $args mixed Any args that should be used in the clause - * @param $_ - * @return boolean If the row exists - */ - public function exists($tables, $where = null, $args = null, $_ = null) { - $args = func_get_args(); - - $tables = array_shift($args); - - // If there are still args left we have a where clause and possible where args - if(count($args) > 0) - $where = array_shift($args); - - $query = sf( - 'SELECT EXISTS(SELECT * FROM %s%s) AS \'exists\'', - $tables, - (isset($where) ? sf(' WHERE %s', $where) : '') - ); - - array_unshift($args, $query); - - $result = call_user_func_array(array(&$this, 'query'), $args); - - return(bool)$result[0]->exists; - } - - /** - * Counts the number of records - * - * @param $tables string The table or tables to query from - * @param $where string The clause to query by - * @param $args mixed Any args that should be used in the clause - * @param $_ - * @return integer The number of records - */ - public function count($tables, $where = null, $args = null, $_ = null) { - $args = func_get_args(); - - array_unshift($args, 'COUNT(*) as \'count\''); - - $result = call_user_func_array(array(&$this, 'select'), $args); - - return(int)$result[0]->count; - } - - /* INSERT/UPDATE/DELETE QUERIES */ - - public function insert($table, $column, $value = null, $_ = null) { - $args = func_get_args(); - - $table = array_shift($args); - - // If there are still args left we have variable pairs - $names = array(); - $types = array(); - $values = array(); - while(count($args) >= 2) { - $column = array_shift($args); - - $column = explode('=', $column, 2); - - $names[] = trim($column[0]); - $types[] = trim($column[1]); - $values[] = array_shift($args); - } - - if(count($args) > 0) - throw new db_Exception('Uneven number of column value paires'); - - $query = sf( - 'INSERT INTO %s(%s) VALUES(%s)', - $table, - implode(', ', $names), - implode(', ', $types) - ); - - array_unshift($values, $query); - - return call_user_func_array(array(&$this, 'query'), $values); - } - - public function insertBulk($table, $columns, $data) {} - - public function insertIfNotExists($table, $where, $args = null, $_ = null) {} - - public function insertOrUpdateOne($table, $where, $args = null, $_ = null) {} - - public function update($table, $where, $args = null, $_ = null, $column = null, $value = null, $_ = null) { - $args = func_get_args(); - - $table = array_shift($args); - $where = array_shift($args); - - $where_arg_count = preg_match_all('/%[a-zA-Z]+/', $where, $matches); - if(count($args) < $where_arg_count) - throw new db_Exception('Incorrect number of args for where clause'); - - $where_args = array_slice($args, 0, $where_arg_count); - $args = array_slice($args, $where_arg_count); - - $names = array(); - $values = array(); - while(count($args) >= 2) { - $names[] = array_shift($args); - $values[] = array_shift($args); - } - - if(count($args) > 0) - throw new db_Exception('Uneven number of column value paires'); - - $query = sf( - 'UPDATE %s SET %s WHERE %s', $table, implode(', ', $names), $where - ); - - $query = array_merge(array($query), $values, $where_args); - - return call_user_func_array(array(&$this, 'query'), $query); - } - - public function updateOne($table, $where, $args = null, $_ = null, $column = null, $value = null, $_ = null) {} - - public function delete($table, $where, $args = null, $_ = null) {} - - public function deleteOne($table, $where, $args = null, $_ = null) {} - - public function deleteAndInsert($table, $where, $args = null, $_ = null) {} - - /* DATA DEFINITION */ - - public function tableCreate($table, $columns, $data = null) {} - - public function tableDrop($table) {} } ?> \ No newline at end of file diff --git a/classes/database/vender/postgresql/db_PostgreSQL.php b/classes/database/vender/postgresql/db_PostgreSQL.php index e9f461e..86de1ca 100644 --- a/classes/database/vender/postgresql/db_PostgreSQL.php +++ b/classes/database/vender/postgresql/db_PostgreSQL.php @@ -66,7 +66,7 @@ public function transactionSupport() { * Returns true if the database is in transaction * @return boolean If the database is in transaction */ - public function transaction() { + public function inTransaction() { return $this->transaction; } From 3f3c47135f41be4f735bec0ac1c91bb12eac38f6 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 19 Sep 2011 17:41:18 +0100 Subject: [PATCH 028/164] Added postgresql -> php caster to return values from a db row --- classes/database/dp_AbstractRow.php | 15 +- .../postgresql/caster_PostgreSQLToPhp.php | 143 ++++++++++++++++++ .../vender/postgresql/db_PostgreSQLRow.php | 3 +- 3 files changed, 152 insertions(+), 9 deletions(-) create mode 100644 classes/database/vender/postgresql/caster_PostgreSQLToPhp.php diff --git a/classes/database/dp_AbstractRow.php b/classes/database/dp_AbstractRow.php index 7bedc0d..3e645b5 100644 --- a/classes/database/dp_AbstractRow.php +++ b/classes/database/dp_AbstractRow.php @@ -2,13 +2,13 @@ abstract class dp_AbstractRow { - private $data; + private $rowData; protected $caster; abstract protected function initCaster (); public function __construct ($rowData) { - $this->data = $rowData; + $this->rowData = $rowData; } public function cast ($format, $column) { @@ -16,17 +16,16 @@ public function cast ($format, $column) { if (!array_key_exists($column, $this->rowData)) throw new db_RowColumnNotFoundException('Column not found: '.$column); - $data = $this->date[$column]; + $data = $this->rowData[$column]; - $caster = $this->initCaster(); + $this->initCaster(); - return $caster->cast('%'.$format, $data); + return $this->caster->cast('%'.$format, $data); } - public function get ($format, $column) { - - return $this->data[$column]; + public function getRaw ($column) { + return $data[$column]; } diff --git a/classes/database/vender/postgresql/caster_PostgreSQLToPhp.php b/classes/database/vender/postgresql/caster_PostgreSQLToPhp.php new file mode 100644 index 0000000..c920658 --- /dev/null +++ b/classes/database/vender/postgresql/caster_PostgreSQLToPhp.php @@ -0,0 +1,143 @@ + 'sqlArrayOrNull', + 'a' => 'sqlArray', + 'b' => 'boolean', + 'd' => 'date', + 'i' => 'integer', + 's' => 'text', + 'S' => 'textOrNull', + 't' => 'timestampWithTimezone' + ); + + /* CONSTRUCTOR & DESTRUCTOR */ + /* GET METHODS */ + /* SET METHODS */ + /* MAGIC METHODS */ + /* METHODS */ + + /** + * Casts a string in a PostgreSQL format + * NOTE: Annoyance due to PHP scope issue + * @param string $string The string to cast + * @param mixed $args The args to be parsed into the string + * @param mixed $_ Repeated last arg as needed + * @return string The casted string + */ + static function cast($string, $args = null, $_ = null) { + $parser = new self(); + + /* 'func_get_args' cannot be called as function arg pre PHP5 */ + $func_args = func_get_args(); + return (string)$parser->castString($func_args); + } + + + /** + * Casts a variable into a PostgreSQL text + * @param string $in String to be casted + * @return string Casted string + */ + static function text($in) { + if(!is_string($in)) throw new caster_StrictTypeException('Expected String, received: '.$in.' ('.gettype($in).')'); + return sf("%s", $in); + } + + /** + * Casts a variable into a PostgreSQL text thats accepts NULL values + * @param string $in String to be casted or null + * @return string Casted string + */ + static function textOrNull($in) { + if (!is_string($in) && !is_null($in)) throw new caster_StrictTypeException('Expected String or Null, received: '.$in.' ('.gettype($in).')'); + + if (is_string($in) && strlen($in)) return self::text($in); + elseif (is_null($in)) return null; + } + + /** + * Casts a variable into a PostgreSQL array + * @param array $in Array to be casted + * @return string Casted string + */ + static function sqlArray($in) { + + if (!is_array($in)) throw new caster_StrictTypeException('Expected Array, received: '.$in.' ('.gettype($in).')'); + + if (is_null($in)) return array(); + + $in = str_replace(array("{","}"),"", $in); + $arr = explode(",",$in); + + // int + if ($type == "integer") { + $newArr = array(); + foreach ($arr as $val) + $newArr[] = intval($val); + $arr = $newArr; + } + return $arr; + } + + static function sqlArrayOrNull($in) { + if (!is_array($in) && !is_null($in)) throw new caster_StrictTypeException('Expected Array or Null, received: '.$in.' ('.gettype($in).')'); + + if (is_array($in) && count($in)) return self::text($in); + elseif (is_null($in)) return null; + } + + /** + * Casts a variable into a PostgreSQL boolean + * @param bool $in Bool to be casted + * @return string Casted string + */ + static function boolean($in) { + if (!is_bool($in)) throw new caster_StrictTypeException('Expected Boolean, received: '.$in.' ('.gettype($in).')'); + return $in; + } + + /** + * Casts a variable into a PostgreSQL integer + * @param int $in Int to be casted + * @return string Casted string + */ + static function integer($in) { + if (!is_int($in)) throw new caster_StrictTypeException('Expected Integer, received: '.$in.' ('.gettype($in).')'); + return $in; + } + + static function date($in) { + throw new caster_StrictTypeException('TODO: date'); + } + static function timestampWithTimezone($in) { + throw new caster_StrictTypeException('TODO: timestamp'); + } + + /* DEPRECATED METHODS */ +} +?> \ No newline at end of file diff --git a/classes/database/vender/postgresql/db_PostgreSQLRow.php b/classes/database/vender/postgresql/db_PostgreSQLRow.php index 343ead3..48f261f 100644 --- a/classes/database/vender/postgresql/db_PostgreSQLRow.php +++ b/classes/database/vender/postgresql/db_PostgreSQLRow.php @@ -3,7 +3,8 @@ class db_PostgreSQLRow extends dp_AbstractRow { protected function initCaster() { - $this->caster = new caster_PostgreSQLToPhp(); + if (is_null($this->caster)) + $this->caster = new caster_PostgreSQLToPhp(); } } From 0b4e102f07ad162420c74f6af1b4c8df88da8989 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 20 Sep 2011 13:41:27 +0100 Subject: [PATCH 029/164] Added postgresql support for exists() count() instertOrUpdateOne() transactions and more --- classes/database/db_AbstractDatabase.php | 113 ++++++++++++++++-- .../db_UnexpectedResultException.php | 22 ++++ .../vender/postgresql/caster_PostgreSQL.php | 12 +- .../postgresql/caster_PostgreSQLToPhp.php | 4 +- .../vender/postgresql/db_PostgreSQL.php | 2 +- 5 files changed, 140 insertions(+), 13 deletions(-) create mode 100644 classes/database/exceptions/db_UnexpectedResultException.php diff --git a/classes/database/db_AbstractDatabase.php b/classes/database/db_AbstractDatabase.php index d775a6d..51775c8 100644 --- a/classes/database/db_AbstractDatabase.php +++ b/classes/database/db_AbstractDatabase.php @@ -63,6 +63,10 @@ abstract class db_AbstractDatabase /* implements db_InterfaceDatabase */ { */ protected $caster; + + protected $transaction; + + /* CONSTRUCTOR & DESTRUCTOR */ /** @@ -220,6 +224,9 @@ public function queryReal($sql) { } $data = $return->fetchAll(PDO::FETCH_ASSOC); + + $this->affectedRows = count($data); + if(!is_array($data)) throw new PDOException('Failed to return data array'); @@ -261,7 +268,7 @@ public function selectOne($args) { $result = call_user_func_array(array(&$this, 'select'), $args); if(count($result) > 1) - throw new db_QueryFailedException('selectOne returned more than one result'); + throw new db_UnexpectedResultException('selectOne returned more than one result'); return array_key_exists(0, $result) ? $result[0] : null; } @@ -277,16 +284,16 @@ public function fetch ($cols, $table, $where = null, $offset = null, $limit = nu $query = call_user_func_array (array ($this, 'parseFetchQuery'), $args); /* perform query */ - $this->query('%l', $query); + $result = $this->query('%l', $query); - return true; + return $result; } public function fetchOne($cols, $table, $where = null) { $args = func_get_args(); $result = call_user_func_array(array(&$this, 'fetch'), $args); if(count($result) > 1) - throw new db_QueryFailedException('fetchOne returned more than one result'); + throw new db_UnexpectedResultException('fetchOne returned more than one result'); return array_key_exists(0, $result) ? $result[0] : null; } public function parseFetchQuery($cols, $table, $where = null, $offset = null, $limit = null) { @@ -345,6 +352,24 @@ public function insert($table, $column, $value = null, $_ = null) { return true; } + public function insertOrUpdateOne ($table, $where, $values) { + + if (is_null($this->caster)) + throw new db_Exception('Caster not loaded.'); + + $args = func_get_args(); + $sets = $this->caster->castArraySets($args); + $table = $sets [0]; + $where = $sets [1]; + + $exists = $this->exists ($table, '%l', $where); + + call_user_func_array (array ($this, $exists? 'updateOne' : 'insert'), $args); + + } + public function deleteAndInsert ($args) { + throw new Expcetion ('TODO'); + } public function parseInsertQuery($args) { if (is_null($this->caster)) @@ -368,7 +393,6 @@ public function parseInsertQuery($args) { 'INSERT INTO %@ (%l) VALUES(%l)', $table, implode(', ', $column), implode(', ', $data) ); } - /* * UPDATE @@ -390,10 +414,10 @@ public function updateOne($args) { $ret = call_user_func_array (array ($this, 'update'), $args); // ensure affected row count is correct - if($this->affected_rows () == 0) - throw new sql_Exception ('No rows affected in updateOne()'); - if($this->affected_rows () > 1) - throw new sql_Exception ('Multiple rows affected in updateOne()'); + if($this->getAffectedRows () == 0) + throw new db_UnexpectedResultException ('No rows affected in updateOne()'); + if($this->getAffectedRows () > 1) + throw new db_UnexpectedResultException ('Multiple rows affected in updateOne()'); return true; } @@ -414,6 +438,77 @@ public function parseUpdateQuery($args) { ); } + /* + * EXISTS + * */ + public function exists ($table, $where) { + + if (is_null($this->caster)) + throw new db_Exception('Caster not loaded.'); + + $args = func_get_args(); + $table = array_shift($args); + $where = $this->caster->castArraySets($args); + + $result = $this->query( + 'SELECT CASE WHEN EXISTS ( + SELECT * + FROM %@ + WHERE %l + ) THEN %b ELSE %b END AS exists', + $table, $where[0], true, false + ); + + $exists = $result[0]; + return $exists->cast('b', 'exists'); + } + + /* + * COUNT + * */ + public function count ($table, $where = null) { + + if (is_null($this->caster)) + throw new db_Exception('Caster not loaded.'); + + $args = func_get_args(); + $table = array_shift($args); + $where = $this->caster->castArraySets($args); + if (!count($where)) $where = array('1=1'); + + $result = $this->query( + 'SELECT COUNT(*) AS count FROM %@ WHERE %l', + $table, $where[0] + ); + + $exists = $result[0]; + return $exists->cast('i', 'count'); + } + + + + /* + * DELETE + * */ + public function delete ($args) { + throw new db_Exception ('TODO'); + } + public function deleteOne ($args) { + throw new db_Exception ('TODO'); + } + public function parseDeleteQuery ($args) { + throw new db_Exception ('TODO'); + } + + /* + * Table management + * */ + public function createTable ($args) { + throw new db_Exception ('TODO'); + } + public function dropTable ($args) { + throw new db_Exception ('TODO'); + } } ?> \ No newline at end of file diff --git a/classes/database/exceptions/db_UnexpectedResultException.php b/classes/database/exceptions/db_UnexpectedResultException.php new file mode 100644 index 0000000..cfd8a2e --- /dev/null +++ b/classes/database/exceptions/db_UnexpectedResultException.php @@ -0,0 +1,22 @@ +extendedInfo = $extendedInfo; + parent::__construct('Unexpected result from database query'); + } + + public function getInstructions($contentType) { + return $this->extendedInfo; + } +} +?> \ No newline at end of file diff --git a/classes/database/vender/postgresql/caster_PostgreSQL.php b/classes/database/vender/postgresql/caster_PostgreSQL.php index 3626e34..0be4fba 100644 --- a/classes/database/vender/postgresql/caster_PostgreSQL.php +++ b/classes/database/vender/postgresql/caster_PostgreSQL.php @@ -41,6 +41,7 @@ class caster_PostgreSQL extends caster_Abstract { 't' => 'timestampWithTimezone', 'v' => 'fullTextVector', 'x' => 'binary', + 'z' => 'interval', ); /* CONSTRUCTOR & DESTRUCTOR */ @@ -144,7 +145,7 @@ static function float($in) { * @return string Casted string */ static function boolean($in) { - return sf("%s::BOOLEAN", $in?'t':'f'); + return sf("'%s'::BOOLEAN", $in?'t':'f'); } /** @@ -191,6 +192,15 @@ static function timestampWithTimezone($in) { static function date($in) { return sf("'%s'::DATE", $in); } + + /** + * Casts a variable into a PostgreSQL date + * @param string $in String to be casted + * @return string Casted string + */ + static function interval($in) { + return sf("'%s'::INTERVAL", $in); + } /** * Performs no casting on the variable, leaving it as it is diff --git a/classes/database/vender/postgresql/caster_PostgreSQLToPhp.php b/classes/database/vender/postgresql/caster_PostgreSQLToPhp.php index c920658..d485d6f 100644 --- a/classes/database/vender/postgresql/caster_PostgreSQLToPhp.php +++ b/classes/database/vender/postgresql/caster_PostgreSQLToPhp.php @@ -127,8 +127,8 @@ static function boolean($in) { * @return string Casted string */ static function integer($in) { - if (!is_int($in)) throw new caster_StrictTypeException('Expected Integer, received: '.$in.' ('.gettype($in).')'); - return $in; + if (!is_int(intval($in))) throw new caster_StrictTypeException('Expected Integer, received: '.$in.' ('.gettype($in).')'); + return intval($in); } static function date($in) { diff --git a/classes/database/vender/postgresql/db_PostgreSQL.php b/classes/database/vender/postgresql/db_PostgreSQL.php index 86de1ca..828476c 100644 --- a/classes/database/vender/postgresql/db_PostgreSQL.php +++ b/classes/database/vender/postgresql/db_PostgreSQL.php @@ -113,7 +113,7 @@ public function transactionRollback() { */ public function transactionAutoRollback() { if($this->transaction) - $this->rollbackTransaction(); + $this->transactionRollback(); } } ?> \ No newline at end of file From d3d7d167562dff1b1ae579590e3eaa9694e4aa55 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 20 Sep 2011 14:38:26 +0100 Subject: [PATCH 030/164] renamed vender folder to adapter, renamed SQL to Sql in filenames for consistency --- .../database/{vender => adapter}/mysql/db_MySql.php | 0 .../postgresql/caster_PostgreSql.php} | 2 +- .../postgresql/caster_PostgreSqlToPhp.php} | 2 +- .../postgresql/db_PostgreSql.php} | 2 +- .../adapter/postgresql/db_PostgreSqlRow.php | 13 +++++++++++++ .../{vender => adapter}/sqlite/db_SqlLite.php | 0 classes/database/dp_AbstractRow.php | 6 ------ .../database/vender/postgresql/db_PostgreSQLRow.php | 13 ------------- 8 files changed, 16 insertions(+), 22 deletions(-) rename classes/database/{vender => adapter}/mysql/db_MySql.php (100%) rename classes/database/{vender/postgresql/caster_PostgreSQL.php => adapter/postgresql/caster_PostgreSql.php} (99%) rename classes/database/{vender/postgresql/caster_PostgreSQLToPhp.php => adapter/postgresql/caster_PostgreSqlToPhp.php} (98%) rename classes/database/{vender/postgresql/db_PostgreSQL.php => adapter/postgresql/db_PostgreSql.php} (98%) create mode 100644 classes/database/adapter/postgresql/db_PostgreSqlRow.php rename classes/database/{vender => adapter}/sqlite/db_SqlLite.php (100%) delete mode 100644 classes/database/vender/postgresql/db_PostgreSQLRow.php diff --git a/classes/database/vender/mysql/db_MySql.php b/classes/database/adapter/mysql/db_MySql.php similarity index 100% rename from classes/database/vender/mysql/db_MySql.php rename to classes/database/adapter/mysql/db_MySql.php diff --git a/classes/database/vender/postgresql/caster_PostgreSQL.php b/classes/database/adapter/postgresql/caster_PostgreSql.php similarity index 99% rename from classes/database/vender/postgresql/caster_PostgreSQL.php rename to classes/database/adapter/postgresql/caster_PostgreSql.php index 0be4fba..3336f22 100644 --- a/classes/database/vender/postgresql/caster_PostgreSQL.php +++ b/classes/database/adapter/postgresql/caster_PostgreSql.php @@ -16,7 +16,7 @@ * @subpackage Caster * @since 1.0 */ -class caster_PostgreSQL extends caster_Abstract { +class caster_PostgreSql extends caster_Abstract { /* CONSTANTS */ /* PROPERTIES */ diff --git a/classes/database/vender/postgresql/caster_PostgreSQLToPhp.php b/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php similarity index 98% rename from classes/database/vender/postgresql/caster_PostgreSQLToPhp.php rename to classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php index d485d6f..a76b4de 100644 --- a/classes/database/vender/postgresql/caster_PostgreSQLToPhp.php +++ b/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php @@ -16,7 +16,7 @@ * @subpackage Caster * @since 1.0 */ -class caster_PostgreSQLToPhp extends caster_Abstract { +class caster_PostgreSqlToPhp extends caster_Abstract { /* CONSTANTS */ /* PROPERTIES */ diff --git a/classes/database/vender/postgresql/db_PostgreSQL.php b/classes/database/adapter/postgresql/db_PostgreSql.php similarity index 98% rename from classes/database/vender/postgresql/db_PostgreSQL.php rename to classes/database/adapter/postgresql/db_PostgreSql.php index 828476c..e610ca3 100644 --- a/classes/database/vender/postgresql/db_PostgreSQL.php +++ b/classes/database/adapter/postgresql/db_PostgreSql.php @@ -51,7 +51,7 @@ protected function createRow ($rowData) { * Initalise the vender type caster */ protected function initCaster() { - $this->caster = new caster_PostgreSQL(); + $this->caster = new caster_PostgreSql(); } /** diff --git a/classes/database/adapter/postgresql/db_PostgreSqlRow.php b/classes/database/adapter/postgresql/db_PostgreSqlRow.php new file mode 100644 index 0000000..6557f54 --- /dev/null +++ b/classes/database/adapter/postgresql/db_PostgreSqlRow.php @@ -0,0 +1,13 @@ +caster)) + $this->caster = new caster_PostgreSqlToPhp(); + } + +} + +?> + diff --git a/classes/database/vender/sqlite/db_SqlLite.php b/classes/database/adapter/sqlite/db_SqlLite.php similarity index 100% rename from classes/database/vender/sqlite/db_SqlLite.php rename to classes/database/adapter/sqlite/db_SqlLite.php diff --git a/classes/database/dp_AbstractRow.php b/classes/database/dp_AbstractRow.php index 3e645b5..f118e79 100644 --- a/classes/database/dp_AbstractRow.php +++ b/classes/database/dp_AbstractRow.php @@ -10,26 +10,20 @@ abstract protected function initCaster (); public function __construct ($rowData) { $this->rowData = $rowData; } - public function cast ($format, $column) { if (!array_key_exists($column, $this->rowData)) throw new db_RowColumnNotFoundException('Column not found: '.$column); $data = $this->rowData[$column]; - $this->initCaster(); return $this->caster->cast('%'.$format, $data); - } public function getRaw ($column) { return $data[$column]; - } - - } ?> \ No newline at end of file diff --git a/classes/database/vender/postgresql/db_PostgreSQLRow.php b/classes/database/vender/postgresql/db_PostgreSQLRow.php deleted file mode 100644 index 48f261f..0000000 --- a/classes/database/vender/postgresql/db_PostgreSQLRow.php +++ /dev/null @@ -1,13 +0,0 @@ -caster)) - $this->caster = new caster_PostgreSQLToPhp(); - } - -} - -?> - From d03629b48d5d9d6ebb85d59dd5525cebc31bfc87 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 20 Sep 2011 15:34:51 +0100 Subject: [PATCH 031/164] Renamed helpers -> utility, added calendar utility subpackage which currently includes a Date and DateTime class. PostgreSql caster now returns a date object --- classes/core/Atsumi.php | 2 +- classes/core/loader/atsumi_Loader.php | 6 +-- .../postgresql/caster_PostgreSqlToPhp.php | 8 ++- classes/database/dp_AbstractRow.php | 3 ++ classes/utility/calendar/atsumi_Date.php | 49 +++++++++++++++++++ classes/utility/calendar/atsumi_DateTime.php | 21 ++++++++ .../{helpers => utility}/http/atsumi_Http.php | 2 +- .../{helpers => utility}/math/atsumi_Math.php | 2 +- 8 files changed, 86 insertions(+), 7 deletions(-) create mode 100644 classes/utility/calendar/atsumi_Date.php create mode 100644 classes/utility/calendar/atsumi_DateTime.php rename classes/{helpers => utility}/http/atsumi_Http.php (98%) rename classes/{helpers => utility}/math/atsumi_Math.php (92%) diff --git a/classes/core/Atsumi.php b/classes/core/Atsumi.php index 4f86c3f..f6e7a55 100644 --- a/classes/core/Atsumi.php +++ b/classes/core/Atsumi.php @@ -53,7 +53,7 @@ private function __construct() { $this->errorHandler = new atsumi_ErrorHandler(); // Load some helpful files - atsumi_Loader::references(atsumi_Loader::getAtsumiDir(), 'caster helpers/http'); + atsumi_Loader::references(atsumi_Loader::getAtsumiDir(), 'caster utility/http'); } /* GET METHODS */ diff --git a/classes/core/loader/atsumi_Loader.php b/classes/core/loader/atsumi_Loader.php index 66fbfc5..66f3c1f 100644 --- a/classes/core/loader/atsumi_Loader.php +++ b/classes/core/loader/atsumi_Loader.php @@ -138,9 +138,9 @@ protected function findWorkspace() { * Used to load or include a set of files as specified in the spec * * Examples: - * references('atsumi', 'helpers mvc database'); - * references('atsumi', array('helpers', 'mvc', 'database')); - * references(array('atsumi' => 'helpers mvc database')); + * references('atsumi', 'utility mvc database'); + * references('atsumi', array('utility', 'mvc', 'database')); + * references(array('atsumi' => 'utility mvc database')); * @access public * @param $spec mixed See above for examples */ diff --git a/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php b/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php index a76b4de..a8e9e30 100644 --- a/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php +++ b/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php @@ -29,6 +29,7 @@ class caster_PostgreSqlToPhp extends caster_Abstract { 'a' => 'sqlArray', 'b' => 'boolean', 'd' => 'date', + 'D' => 'dateOrNull', 'i' => 'integer', 's' => 'text', 'S' => 'textOrNull', @@ -132,8 +133,13 @@ static function integer($in) { } static function date($in) { - throw new caster_StrictTypeException('TODO: date'); + return atsumi_Date::fromYmd($in); + } + static function dateOrNull($in) { + if (is_null($in)) return null; + return atsumi_Date::fromYmd($in); } + static function timestampWithTimezone($in) { throw new caster_StrictTypeException('TODO: timestamp'); } diff --git a/classes/database/dp_AbstractRow.php b/classes/database/dp_AbstractRow.php index f118e79..f35822a 100644 --- a/classes/database/dp_AbstractRow.php +++ b/classes/database/dp_AbstractRow.php @@ -1,4 +1,7 @@ 'utility/calendar' + )); abstract class dp_AbstractRow { diff --git a/classes/utility/calendar/atsumi_Date.php b/classes/utility/calendar/atsumi_Date.php new file mode 100644 index 0000000..3119b84 --- /dev/null +++ b/classes/utility/calendar/atsumi_Date.php @@ -0,0 +1,49 @@ +year = (int) $year; + $this->month = (int) $month; + $this->day = (int) $day; + } + + public function __toString() { + return $this->getYmd(); + } + + public function getYear () { return $this->year; } + public function getMonth () { return $this->month; } + public function getDay () { return $this->day; } + + public function getTimestampDayStart () { + return mktime ($this->year, $this->month, $this->day, 0, 0, 0); + } + public function getTimestampDayEnd () { + return mktime ($this->year, $this->month, $this->day + 1, 0, 0, 0); + } + public function getYmd () { + return sprintf ('%04d-%02d-%02d', $this->year, $this->month, $this->day); + } +} +?> \ No newline at end of file diff --git a/classes/utility/calendar/atsumi_DateTime.php b/classes/utility/calendar/atsumi_DateTime.php new file mode 100644 index 0000000..3fc06aa --- /dev/null +++ b/classes/utility/calendar/atsumi_DateTime.php @@ -0,0 +1,21 @@ +timestamp = (int) $timestamp; + } + + public function __toString() { + return $this->timestamp; + } + +} +?> \ No newline at end of file diff --git a/classes/helpers/http/atsumi_Http.php b/classes/utility/http/atsumi_Http.php similarity index 98% rename from classes/helpers/http/atsumi_Http.php rename to classes/utility/http/atsumi_Http.php index 23a2231..f1aa3ec 100644 --- a/classes/helpers/http/atsumi_Http.php +++ b/classes/utility/http/atsumi_Http.php @@ -2,7 +2,7 @@ /** * - * @package Helpers + * @package Utility * @subpackage Http */ class atsumi_Http { diff --git a/classes/helpers/math/atsumi_Math.php b/classes/utility/math/atsumi_Math.php similarity index 92% rename from classes/helpers/math/atsumi_Math.php rename to classes/utility/math/atsumi_Math.php index 12c7ed7..9094896 100644 --- a/classes/helpers/math/atsumi_Math.php +++ b/classes/utility/math/atsumi_Math.php @@ -2,7 +2,7 @@ /** * - * @package Helpers + * @package Utility * @subpackage Math */ class atsumi_Math { From 835536223de74149a352dae914f2dc3ce914257a Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 20 Sep 2011 21:32:11 +0100 Subject: [PATCH 032/164] Abstract db row now supports quick referencing of casting calls. ->S_first_name is the same as calling ->cast('s', 'first_name') --- .../adapter/postgresql/db_PostgreSqlRow.php | 2 +- .../{dp_AbstractRow.php => db_AbstractRow.php} | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) rename classes/database/{dp_AbstractRow.php => db_AbstractRow.php} (67%) diff --git a/classes/database/adapter/postgresql/db_PostgreSqlRow.php b/classes/database/adapter/postgresql/db_PostgreSqlRow.php index 6557f54..223f14b 100644 --- a/classes/database/adapter/postgresql/db_PostgreSqlRow.php +++ b/classes/database/adapter/postgresql/db_PostgreSqlRow.php @@ -1,6 +1,6 @@ caster)) diff --git a/classes/database/dp_AbstractRow.php b/classes/database/db_AbstractRow.php similarity index 67% rename from classes/database/dp_AbstractRow.php rename to classes/database/db_AbstractRow.php index f35822a..3ea87ff 100644 --- a/classes/database/dp_AbstractRow.php +++ b/classes/database/db_AbstractRow.php @@ -1,11 +1,11 @@ 'utility/calendar' - )); + 'atsumi' => 'utility/calendar' +)); -abstract class dp_AbstractRow { +abstract class db_AbstractRow { - private $rowData; + private $rowData; protected $caster; abstract protected function initCaster (); @@ -27,6 +27,13 @@ public function cast ($format, $column) { public function getRaw ($column) { return $data[$column]; } + + public function __get($call) { + $pos = strpos($call, '_'); + return call_user_func_array (array($this, 'cast'), array(substr($call,0,$pos), substr($call,$pos+1))); + + } + } ?> \ No newline at end of file From f0d4cd2c49d8148a83fc95a594aa036af233bbc6 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Fri, 30 Sep 2011 17:37:23 +0100 Subject: [PATCH 033/164] Various fixes for database abstraction layer. Added a JSONP view and added various formatting methods to postgreSQL casters --- classes/caster/caster_Abstract.php | 26 +++++++++++++++++- classes/core/Atsumi.php | 12 ++++++++- classes/core/app/atsumi_AppHandler.php | 18 +++++++++++++ classes/core/app/parser/uriparser_Gyokuro.php | 2 +- .../adapter/postgresql/caster_PostgreSql.php | 10 +++++++ .../postgresql/caster_PostgreSqlToPhp.php | 17 +++++++++--- .../adapter/postgresql/db_PostgreSql.php | 6 +++++ classes/database/db_AbstractDatabase.php | 27 +++++++++++-------- classes/mvc/views/mvc_JsonpView.php | 12 +++++++++ classes/utility/calendar/atsumi_DateTime.php | 6 ++++- 10 files changed, 118 insertions(+), 18 deletions(-) create mode 100644 classes/mvc/views/mvc_JsonpView.php diff --git a/classes/caster/caster_Abstract.php b/classes/caster/caster_Abstract.php index bf19f67..cadb718 100644 --- a/classes/caster/caster_Abstract.php +++ b/classes/caster/caster_Abstract.php @@ -79,6 +79,30 @@ public function castString($string, $args = null, $_ = null) { } return implode('', $ret); } + /** + * Casts args into a given string/object by the class held caster spec + * @param string $string The string to cast + * @param mixed $args The args to be parsed into the string + * @return string The casted string + */ + public function castObject($string, $args = null, $_ = null) { + // Get args + $args = func_get_args(); + + if (count($args[0]) !== 2) throw new caster_Exception('Cast object expects two parameters'); + + $ch = substr($args[0][0], 1); + + if(array_key_exists($ch, $this->spec) && method_exists($this,$this->spec[$ch])) { + $methodName = $this->spec[$ch]; + return $this->$methodName($args[0][1]); + } elseif($ch == '%') { + return '%'; + } else { + throw new caster_Exception("Caster received unexpected format character: %".$ch); + } + + } public function castArray ($params) { $format = array_shift ($params); @@ -128,7 +152,7 @@ protected function castReal($format, $args) { } elseif($ch == '%') { $ret .= '%'; } else { - throw new caster_Exception("Invalid format string"); + throw new caster_Exception("Caster received unexpected format character: %".$ch); } $pos0 = $pos1 + 2; } diff --git a/classes/core/Atsumi.php b/classes/core/Atsumi.php index f6e7a55..06fea57 100644 --- a/classes/core/Atsumi.php +++ b/classes/core/Atsumi.php @@ -178,11 +178,21 @@ public static function app__render() { $args = func_get_args(); return self::__callStatic(__FUNCTION__, $args); } - + + public static function app__getParserData() { + $args = func_get_args(); + return self::__callStatic(__FUNCTION__, $args); + } + public static function app__getParserMetaData() { $args = func_get_args(); return self::__callStatic(__FUNCTION__, $args); } + + public static function app__getBaseUri() { + $args = func_get_args(); + return self::__callStatic(__FUNCTION__, $args); + } public static function app__createUri($controller, $method) { $args = func_get_args(); diff --git a/classes/core/app/atsumi_AppHandler.php b/classes/core/app/atsumi_AppHandler.php index e15abb3..a0e404c 100644 --- a/classes/core/app/atsumi_AppHandler.php +++ b/classes/core/app/atsumi_AppHandler.php @@ -97,6 +97,15 @@ public function __construct($settings, $errorHandler) { /* GET METHODS */ + /** + * Returns the parser return data + * @access public + * @return array|null The parser return data, or null on error + */ + public function getParserData() { + return $this->parserData; + } + /** * Returns the parser return data * @access public @@ -106,6 +115,15 @@ public function getParserMetaData() { return $this->parserMetaData; } + /** + * get the base path of atsumi relative to the domain for if atsumi is running in a subdirectory + * @access public + * @param string $basePath + */ + public function getBaseUri() { + return $this->baseUri; + } + // SET FUNCTIONS /** diff --git a/classes/core/app/parser/uriparser_Gyokuro.php b/classes/core/app/parser/uriparser_Gyokuro.php index 612cc09..1283633 100644 --- a/classes/core/app/parser/uriparser_Gyokuro.php +++ b/classes/core/app/parser/uriparser_Gyokuro.php @@ -123,7 +123,7 @@ public function createUri($specification, $controller, $method, $args = array()) $components = self::searchSpec($specification, $controller); // TODO: add args parser!! - $path = count($components) ? implode('/',$components).'/'. $method.'/':''; + $path = count($components) ? implode('/',$components).'/'. ($method ==''?'':$method.'/'):''; return $path; } diff --git a/classes/database/adapter/postgresql/caster_PostgreSql.php b/classes/database/adapter/postgresql/caster_PostgreSql.php index 3336f22..9128408 100644 --- a/classes/database/adapter/postgresql/caster_PostgreSql.php +++ b/classes/database/adapter/postgresql/caster_PostgreSql.php @@ -33,6 +33,7 @@ class caster_PostgreSql extends caster_Abstract { 'd' => 'date', 'f' => 'float', 'i' => 'integer', + 'I' => 'integerOrNull', 'l' => 'literal', 'n' => 'numeric', 'q' => 'fullTextQuery', @@ -156,6 +157,15 @@ static function boolean($in) { static function integer($in) { return sf("%s::INTEGER", $in); } + /** + * Casts a variable into a PostgreSQL integer or Null + * @param int $in Int to be casted + * @return string Casted string + */ + static function integerOrNull($in) { + if (is_null($in)) return 'NULL'; + return sf("%s::INTEGER", $in); + } /** * Casts a variable into a PostgreSQL numeric diff --git a/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php b/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php index a8e9e30..7a4bf9d 100644 --- a/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php +++ b/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php @@ -31,9 +31,11 @@ class caster_PostgreSqlToPhp extends caster_Abstract { 'd' => 'date', 'D' => 'dateOrNull', 'i' => 'integer', + 'I' => 'integerOrNull', 's' => 'text', 'S' => 'textOrNull', - 't' => 'timestampWithTimezone' + 't' => 'timestampWithTimezone', + 'T' => 'timestampWithTimezoneOrNull' ); /* CONSTRUCTOR & DESTRUCTOR */ @@ -55,7 +57,7 @@ static function cast($string, $args = null, $_ = null) { /* 'func_get_args' cannot be called as function arg pre PHP5 */ $func_args = func_get_args(); - return (string)$parser->castString($func_args); + return $parser->castObject($func_args); } @@ -131,6 +133,11 @@ static function integer($in) { if (!is_int(intval($in))) throw new caster_StrictTypeException('Expected Integer, received: '.$in.' ('.gettype($in).')'); return intval($in); } + static function integerOrNull($in) { + if (is_null($in)) return null; + if (!is_int(intval($in))) throw new caster_StrictTypeException('Expected Integer or Null, received: '.$in.' ('.gettype($in).')'); + return intval($in); + } static function date($in) { return atsumi_Date::fromYmd($in); @@ -141,7 +148,11 @@ static function dateOrNull($in) { } static function timestampWithTimezone($in) { - throw new caster_StrictTypeException('TODO: timestamp'); + return new atsumi_DateTime(strtotime($in)); + } + static function timestampWithTimezoneOrNull($in) { + if (is_null($in)) return null; + return self::timestampWithTimezone($in); } /* DEPRECATED METHODS */ diff --git a/classes/database/adapter/postgresql/db_PostgreSql.php b/classes/database/adapter/postgresql/db_PostgreSql.php index e610ca3..8459dba 100644 --- a/classes/database/adapter/postgresql/db_PostgreSql.php +++ b/classes/database/adapter/postgresql/db_PostgreSql.php @@ -115,5 +115,11 @@ public function transactionAutoRollback() { if($this->transaction) $this->transactionRollback(); } + + public function nextval ($name) { + $row = $this->selectOne ("SELECT nextval (%s) AS id", $name); + return $row->cast('i', 'id'); + } + } ?> \ No newline at end of file diff --git a/classes/database/db_AbstractDatabase.php b/classes/database/db_AbstractDatabase.php index 51775c8..34b1b92 100644 --- a/classes/database/db_AbstractDatabase.php +++ b/classes/database/db_AbstractDatabase.php @@ -278,7 +278,7 @@ public function selectOne($args) { /* FETCH * abstract select - for simple queries (no joins) */ - public function fetch ($cols, $table, $where = null, $offset = null, $limit = null) { + public function fetch ($cols, $table, $where = null, $orderBy = null, $offset = null, $limit = null) { /* parse the query */ $args = func_get_args(); $query = call_user_func_array (array ($this, 'parseFetchQuery'), $args); @@ -296,12 +296,16 @@ public function fetchOne($cols, $table, $where = null) { throw new db_UnexpectedResultException('fetchOne returned more than one result'); return array_key_exists(0, $result) ? $result[0] : null; } - public function parseFetchQuery($cols, $table, $where = null, $offset = null, $limit = null) { + public function parseFetchQuery($cols, $table, $where = null, $orderBy = null, $offset = null, $limit = null) { if (is_null($this->caster)) throw new db_Exception('Caster not loaded.'); - $args = func_get_args (); + // strip out the nulls + $argsReal = func_get_args (); + $args = array(); + foreach ($argsReal as $arg) + $args[] = !is_null($arg)?$arg:''; $cols = array_shift ($args); $table = array_shift ($args); @@ -316,19 +320,20 @@ public function parseFetchQuery($cols, $table, $where = null, $offset = null, $l $sets = $this->caster->castArraySets($args); $where = array_shift ($sets); } + if (count($args) && $args[0] !== null) { + $orderBy = array_shift ($sets); + } // return select query string return $this->caster->castString( - 'SELECT %l FROM %@%l%l%l', $cols, $table, - is_null($where)?'':$this->caster->castString(' WHERE %l', $where), + 'SELECT %l FROM %@%l%l%l%l', $cols, $table, + empty($where)?'':$this->caster->castString(' WHERE %l', $where), + empty($orderBy)?'':$this->caster->castString(' ORDER BY %l', $orderBy), !is_int($offset)?'':$this->caster->castString(' OFFSET %i', $offset), - !is_int($limit)?'':$this->caster->castString(' LIMIT %i', $limit) + !is_int($limit)?'':$this->caster->castString(' LIMIT %i', $limit) ); } - - - /** * INSERT @@ -473,8 +478,8 @@ public function count ($table, $where = null) { $args = func_get_args(); $table = array_shift($args); - $where = $this->caster->castArraySets($args); - if (!count($where)) $where = array('1=1'); + + $where = (count($args) && !is_null($args[0]))?$this->caster->castArraySets($args):array('1=1'); $result = $this->query( 'SELECT COUNT(*) AS count FROM %@ WHERE %l', diff --git a/classes/mvc/views/mvc_JsonpView.php b/classes/mvc/views/mvc_JsonpView.php new file mode 100644 index 0000000..0e71cbb --- /dev/null +++ b/classes/mvc/views/mvc_JsonpView.php @@ -0,0 +1,12 @@ +get_callback)?'callback':$this->get_callback, $this->get_json); + } + + public function setHeaders() { + header(sf('Content-Type: application/json; charset=%s', $this->getCharset())); + } +} +?> diff --git a/classes/utility/calendar/atsumi_DateTime.php b/classes/utility/calendar/atsumi_DateTime.php index 3fc06aa..859e755 100644 --- a/classes/utility/calendar/atsumi_DateTime.php +++ b/classes/utility/calendar/atsumi_DateTime.php @@ -14,8 +14,12 @@ public function __construct ($timestamp) { } public function __toString() { - return $this->timestamp; + return (String)$this->timestamp; } + + public function format($formatString) { + return date($formatString, $this->timestamp); + } } ?> \ No newline at end of file From 361826dd82542260a14c803c662e817f0399f24e Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 18 Oct 2011 12:15:01 +0100 Subject: [PATCH 034/164] A few tweaks to DBL and json views --- classes/database/adapter/postgresql/caster_PostgreSql.php | 5 +++++ classes/database/adapter/postgresql/db_PostgreSqlRow.php | 3 +-- classes/mvc/views/mvc_JsonView.php | 2 +- classes/mvc/views/mvc_JsonpView.php | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/classes/database/adapter/postgresql/caster_PostgreSql.php b/classes/database/adapter/postgresql/caster_PostgreSql.php index 9128408..0f4298f 100644 --- a/classes/database/adapter/postgresql/caster_PostgreSql.php +++ b/classes/database/adapter/postgresql/caster_PostgreSql.php @@ -43,6 +43,7 @@ class caster_PostgreSql extends caster_Abstract { 'v' => 'fullTextVector', 'x' => 'binary', 'z' => 'interval', + 'Z' => 'intervalOrNull' ); /* CONSTRUCTOR & DESTRUCTOR */ @@ -211,6 +212,10 @@ static function date($in) { static function interval($in) { return sf("'%s'::INTERVAL", $in); } + static function intervalOrNull($in) { + if (is_null($in)) return 'NULL'; + return sf("'%s'::INTERVAL", $in); + } /** * Performs no casting on the variable, leaving it as it is diff --git a/classes/database/adapter/postgresql/db_PostgreSqlRow.php b/classes/database/adapter/postgresql/db_PostgreSqlRow.php index 223f14b..bf72e56 100644 --- a/classes/database/adapter/postgresql/db_PostgreSqlRow.php +++ b/classes/database/adapter/postgresql/db_PostgreSqlRow.php @@ -9,5 +9,4 @@ protected function initCaster() { } -?> - +?> \ No newline at end of file diff --git a/classes/mvc/views/mvc_JsonView.php b/classes/mvc/views/mvc_JsonView.php index 863b88e..feed26b 100644 --- a/classes/mvc/views/mvc_JsonView.php +++ b/classes/mvc/views/mvc_JsonView.php @@ -9,4 +9,4 @@ public function setHeaders() { header(sf('Content-Type: application/json; charset=%s', $this->getCharset())); } } -?> +?> \ No newline at end of file diff --git a/classes/mvc/views/mvc_JsonpView.php b/classes/mvc/views/mvc_JsonpView.php index 0e71cbb..e2009ab 100644 --- a/classes/mvc/views/mvc_JsonpView.php +++ b/classes/mvc/views/mvc_JsonpView.php @@ -9,4 +9,4 @@ public function setHeaders() { header(sf('Content-Type: application/json; charset=%s', $this->getCharset())); } } -?> +?> \ No newline at end of file From 8d43cb7a4f6e4d158e0a554df383dc94a6cee71f Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Wed, 19 Oct 2011 20:31:55 +0100 Subject: [PATCH 035/164] Added floats to postgres->php caster --- .../adapter/postgresql/caster_PostgreSqlToPhp.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php b/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php index 7a4bf9d..1dab094 100644 --- a/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php +++ b/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php @@ -32,6 +32,8 @@ class caster_PostgreSqlToPhp extends caster_Abstract { 'D' => 'dateOrNull', 'i' => 'integer', 'I' => 'integerOrNull', + 'f' => 'float', + 'F' => 'floatOrNull', 's' => 'text', 'S' => 'textOrNull', 't' => 'timestampWithTimezone', @@ -138,6 +140,18 @@ static function integerOrNull($in) { if (!is_int(intval($in))) throw new caster_StrictTypeException('Expected Integer or Null, received: '.$in.' ('.gettype($in).')'); return intval($in); } + + static function float($in) { + if (!is_numeric($in)) throw new caster_StrictTypeException('Expected Float, received: '.$in.' ('.gettype($in).')'); + setType($in, 'float'); + return $in; + } + static function floatOrNull($in) { + if (is_null($in)) return null; + if (!is_numeric($in)) throw new caster_StrictTypeException('Expected Float or Null, received: '.$in.' ('.gettype($in).')'); + setType($in, 'float'); + return $in; + } static function date($in) { return atsumi_Date::fromYmd($in); From f73fa2af3b54909f102962709a726f5855bce465 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 22 Nov 2011 14:41:54 +0000 Subject: [PATCH 036/164] added a processRequest method to controller which appHandler now calls. This allows the application to override it if required --- classes/core/app/atsumi_AppHandler.php | 3 ++- classes/mvc/controllers/mvc_AbstractController.php | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/classes/core/app/atsumi_AppHandler.php b/classes/core/app/atsumi_AppHandler.php index e15abb3..e810355 100644 --- a/classes/core/app/atsumi_AppHandler.php +++ b/classes/core/app/atsumi_AppHandler.php @@ -237,7 +237,8 @@ public function process() { // Time and execute the controllers method atsumi_Debug::startTimer(); - call_user_func_array(array($this->controller, $this->parserData['method']), $this->parserData['args']); + + $this->controller->processRequest($this->parserData['method'], $this->parserData['args']); atsumi_Debug::record('Controller Method', 'The controllers requested method was executed', null, true); // Time and execute the post process diff --git a/classes/mvc/controllers/mvc_AbstractController.php b/classes/mvc/controllers/mvc_AbstractController.php index ad50595..e98a710 100644 --- a/classes/mvc/controllers/mvc_AbstractController.php +++ b/classes/mvc/controllers/mvc_AbstractController.php @@ -195,5 +195,9 @@ public function getData($method = null, $controller = null) { return call_user_func_array(array($controller, $methodName), $methodData['args']); } + + public function processRequest ($method, $args) { + return call_user_func_array(array($this, $method), $args); + } } ?> \ No newline at end of file From cbd6335b8ba696809ca6d9fd86476e556499b0e8 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Wed, 21 Dec 2011 14:38:08 +0000 Subject: [PATCH 037/164] added intval to integer casting --- classes/database/adapter/postgresql/caster_PostgreSql.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/classes/database/adapter/postgresql/caster_PostgreSql.php b/classes/database/adapter/postgresql/caster_PostgreSql.php index 0f4298f..16882c4 100644 --- a/classes/database/adapter/postgresql/caster_PostgreSql.php +++ b/classes/database/adapter/postgresql/caster_PostgreSql.php @@ -156,7 +156,7 @@ static function boolean($in) { * @return string Casted string */ static function integer($in) { - return sf("%s::INTEGER", $in); + return sf("%s::INTEGER", intval($in)); } /** * Casts a variable into a PostgreSQL integer or Null @@ -165,7 +165,7 @@ static function integer($in) { */ static function integerOrNull($in) { if (is_null($in)) return 'NULL'; - return sf("%s::INTEGER", $in); + return sf("%s::INTEGER", intval($in)); } /** From 34e8f902fcfeacc28f8fb8d6f7b7444c41f10497 Mon Sep 17 00:00:00 2001 From: Chris Chrisostomou Date: Fri, 30 Dec 2011 01:09:20 +0000 Subject: [PATCH 038/164] mysql adaptor & caster classes --- .../database/adapter/mysql/caster_MySql.php | 229 ++++++++++++++++++ .../adapter/mysql/caster_MySqlToPhp.php | 155 ++++++++++++ classes/database/adapter/mysql/db_MySql.php | 112 ++++++++- .../database/adapter/mysql/db_MySqlRow.php | 12 + 4 files changed, 504 insertions(+), 4 deletions(-) create mode 100644 classes/database/adapter/mysql/caster_MySql.php create mode 100644 classes/database/adapter/mysql/caster_MySqlToPhp.php create mode 100644 classes/database/adapter/mysql/db_MySqlRow.php diff --git a/classes/database/adapter/mysql/caster_MySql.php b/classes/database/adapter/mysql/caster_MySql.php new file mode 100644 index 0000000..1c7ae18 --- /dev/null +++ b/classes/database/adapter/mysql/caster_MySql.php @@ -0,0 +1,229 @@ + 'tableName', + 'b' => 'boolean', + 'c' => 'character', + 'C' => 'characterVarying', + 'd' => 'datetime', + 'D' => 'date', + 'f' => 'float', + 'i' => 'integer', + 'I' => 'integerOrNull', + 'l' => 'literal', + 'n' => 'numeric', + 's' => 'text', + 'S' => 'textOrNull', + 't' => 'timestamp', + 'T' => 'time', + 'x' => 'binary', + 'z' => 'interval', + 'Z' => 'intervalOrNull' + ); + + /** + * Casts a string in a MySql format + * NOTE: Annoyance due to PHP scope issue + * @param string $string The string to cast + * @param mixed $args The args to be parsed into the string + * @param mixed $_ Repeated last arg as needed + * @return string The casted string + */ + static function cast($string, $args = null, $_ = null) { + $parser = new self(); + + /* 'func_get_args' cannot be called as function arg pre PHP5 */ + $func_args = func_get_args(); + return (string)$parser->castString($func_args); + } + + /** + * Casts a variable into a MySql table name + * @param string $in String to be casted + * @return string Casted string + */ + static function tableName($in) { + return sf('%s', $in); + } + + /** + * Casts a variable into a MySql text + * @param string $in String to be casted + * @return string Casted string + */ + static function text($in) { + if(!is_string($in)) throw new caster_StrictTypeException('Expected String'); + return sf("CAST('%s' as CHAR)", mysql_escape_string($in)); + } + + /** + * Casts a variable into a MySql text thats accepts NULL values + * @param string $in String to be casted or null + * @return string Casted string + */ + static function textOrNull($in) { + if (!is_string($in) && !is_null($in)) throw new caster_StrictTypeException('Expected String or Null'); + + if (is_string($in) && strlen($in)) return self::text($in); + elseif (is_null($in)) return 'NULL'; + } + + /** + * Casts a variable into a MySql character + * @param string $in String to be casted + * @return string Casted string + */ + static function character($in) { + return self::text($in); + } + + /** + * Casts a variable into a MySql character varying + * @param string $in String to be casted + * @return string Casted string + */ + static function characterVarying($in) { + return self::text($in); + } + + /** + * Casts a variable into a MySql float + * @param float $in Float to be casted + * @return string Casted string + */ + static function float($in) { + return sf("CAST('%s' as DECIMAL)", $in); + } + + /** + * Casts a variable into a MySql boolean + * @param bool $in Bool to be casted + * @return string Casted string + */ + static function boolean($in) { + return sf("%s", $in ? 'true':'false'); + } + + /** + * Casts a variable into a MySql integer + * @param int $in Int to be casted + * @return string Casted string + */ + static function integer($in) { + return sf("CAST('%s' as SIGNED)", intval($in)); + } + /** + * Casts a variable into a MySql integer or Null + * @param int $in Int to be casted + * @return string Casted string + */ + static function integerOrNull($in) { + if (is_null($in)) return 'NULL'; + return sf("CAST('%s' as SIGNED)", intval($in)); + } + + /** + * Casts a variable into a MySql numeric + * @param mixed $in Mixed to be casted + * @return string Casted string + */ + static function numeric($in) { + return sf("CAST('%s' as SIGNED)", $in); + } + + /** + * Casts a variable into a MySql binary + * @param string $in String to be casted + * @return string Casted string + */ + static function binary($in) { + return sf("CAST('%s' as BINARY)", pg_escape_bytea($in)); + } + + /** + * Casts a variable into a MySql timestamp with timezone + * @param int $in Int to be casted + * @return string Casted string + */ + static function timestamp($in) { + //TODO: convert datetime to timestamp or leave to user input + //return sf("'%s'::TIMESTAMP WITH TIME ZONE", mysql_escape_string(gmdate('Y-m-d H:i:s+00', $in))); + return sf("'%s'", $in); + } + + /** + * Casts a variable into a MySql datetime + * @param string $in String to be casted + * @return string Casted string + */ + static function datetime($in) { + return sf("CAST('%s' as DATETIME)", $in); + } + + /** + * Casts a variable into a MySql date + * @param string $in String to be casted + * @return string Casted string + */ + static function date($in) { + return sf("CAST('%s' as DATE)", $in); + } + + /** + * Casts a variable into a MySql time + * @param string $in String to be casted + * @return string Casted string + */ + static function time($in) { + return sf("CAST('%s' as TIME)", $in); + } + + /** + * Casts a variable into a MySql date + * @param string $in String to be casted + * @return string Casted string + */ + static function interval($in) { + //TODO: interval? + return sf("'%s'", $in); + } + static function intervalOrNull($in) { + //TODO: interval or null? + if (is_null($in)) return 'NULL'; + return sf("'%s'", $in); + } + + /** + * Performs no casting on the variable, leaving it as it is + * @param mixed $in mixed to be casted + * @return string Unaffected string + */ + static function literal($in) { + return $in; + } + +} +?> \ No newline at end of file diff --git a/classes/database/adapter/mysql/caster_MySqlToPhp.php b/classes/database/adapter/mysql/caster_MySqlToPhp.php new file mode 100644 index 0000000..45f6a52 --- /dev/null +++ b/classes/database/adapter/mysql/caster_MySqlToPhp.php @@ -0,0 +1,155 @@ + 'boolean', + 'd' => 'datetime', + 'D' => 'datetimeOrNull', + 'e' => 'date', //TODO: decide on letter for date in mysql + 'E' => 'dateOrNull', + 'i' => 'integer', + 'I' => 'integerOrNull', + 'f' => 'float', + 'F' => 'floatOrNull', + 's' => 'text', + 'S' => 'textOrNull', + 't' => 'timestamp', + 'T' => 'timestampOrNull', + 'u' => 'time', //TODO: decide in letter for time in mysql + 'U' => 'timeOrNull' + ); + + /** + * Casts a string in a MySql format + * NOTE: Annoyance due to PHP scope issue + * @param string $string The string to cast + * @param mixed $args The args to be parsed into the string + * @param mixed $_ Repeated last arg as needed + * @return string The casted string + */ + static function cast($string, $args = null, $_ = null) { + $parser = new self(); + + /* 'func_get_args' cannot be called as function arg pre PHP5 */ + $func_args = func_get_args(); + return $parser->castObject($func_args); + } + + + /** + * Casts a variable into a MySql text + * @param string $in String to be casted + * @return string Casted string + */ + static function text($in) { + if(!is_string($in)) throw new caster_StrictTypeException('Expected String, received: '.$in.' ('.gettype($in).')'); + return sf("%s", $in); + } + + /** + * Casts a variable into a MySql boolean + * @param bool $in Bool to be casted + * @return string Casted string + */ + static function boolean($in) { + if ($in==='0') $in = false; + if ($in==='1') $in = true; + if (!is_bool($in)) throw new caster_StrictTypeException('Expected Boolean, received: '.$in.' ('.gettype($in).')'); + return $in; + } + + /** + * Casts a variable into a MySql integer + * @param int $in Int to be casted + * @return string Casted string + */ + static function integer($in) { + if (!is_int(intval($in))) throw new caster_StrictTypeException('Expected Integer, received: '.$in.' ('.gettype($in).')'); + return intval($in); + } + + static function integerOrNull($in) { + if (is_null($in)) return null; + if (!is_int(intval($in))) throw new caster_StrictTypeException('Expected Integer or Null, received: '.$in.' ('.gettype($in).')'); + return intval($in); + } + + static function float($in) { + if (!is_numeric($in)) throw new caster_StrictTypeException('Expected Float, received: '.$in.' ('.gettype($in).')'); + setType($in, 'float'); + return $in; + } + + static function floatOrNull($in) { + if (is_null($in)) return null; + if (!is_numeric($in)) throw new caster_StrictTypeException('Expected Float or Null, received: '.$in.' ('.gettype($in).')'); + setType($in, 'float'); + return $in; + } + + static function datetime($in) { + #return atsumi_Date::fromYmd($in); + return new atsumi_DateTime(strtotime($in)); + } + + static function datetimeOrNull($in) { + if (is_null($in)) return null; + return new atsumi_DateTime(strtotime($in)); + } + + static function timestamp($in) { + return new atsumi_DateTime(strtotime($in)); + } + + static function timestampOrNull($in) { + if (is_null($in)) return null; + return self::timestamp($in); + } + + static function date($in) { + return atsumi_Date::fromYmd($in); + } + + static function date($in) { + return atsumi_Date::fromYmd($in); + } + + static function date($in) { + return atsumi_Date::fromYmd($in); + } + + static function time($in) { + if (is_null($in)) return null; + //TODO: time stuff + return $in; + } + + static function timeOrNull($in) { + //TODO: time (or null) stuff + if (is_null($in)) return null; + return $in; + } +} +?> \ No newline at end of file diff --git a/classes/database/adapter/mysql/db_MySql.php b/classes/database/adapter/mysql/db_MySql.php index 865e554..3d73854 100755 --- a/classes/database/adapter/mysql/db_MySql.php +++ b/classes/database/adapter/mysql/db_MySql.php @@ -1,11 +1,115 @@ connectReal($conString, $config); + } + + protected function createRow ($rowData) { + return new db_MySqlRow($rowData); + } + + /** + * Initalise the vender type caster + */ + protected function initCaster() { + $this->caster = new caster_MySql(); + } + + /** + * Returns true if the database supports transaction + * @return boolean If the database supports transactions + */ + public function transactionSupport() { + // MySam does't support transactions and will just ignore begin, commit, rollback, etc + // Innodb will work as expected + return true; + } + + /** + * Returns true if the database is in transaction + * @return boolean If the database is in transaction + */ + public function inTransaction() { + return $this->transaction; + } + + /** + * Begins a database transaction. Will fail if there is already a transaction in progress. + * @return boolean If beginning the transaction was successful + */ + public function transactionBegin() { + if($this->transaction) + throw new db_Exception('Cannot call beginTransaction() while already in transaction'); + + $this->query('BEGIN'); + $this->transaction = true; + return true; + } + + /** + * Commits a database transaction. Will fail if there is no transaction in progress. + * @return boolean If commiting the transaction was successful + */ + public function transactionCommit() { + if(!$this->transaction) + throw new db_Exception('Cannot call commitTransaction() while not in transaction'); + + $this->transaction = false; + return $this->query('COMMIT'); + } + + /** + * Rolls back a database transaction. Will fail if there is no transaction in progress. + * @return boolean If rolling back the transaction was successful + */ + public function transactionRollback() { + if(!$this->transaction) + throw new db_Exception('Cannot call rollbackTransaction() While not in transaction'); + + $this->transaction = false; + return $this->query('ROLLBACK'); + } + + /** + * Rolls back a database transaction, if there is one in progress. This is designed for use in + * catch blocks to ensure a rollback in case of error. + */ + public function transactionAutoRollback() { + if($this->transaction) + $this->transactionRollback(); + } + + public function nextval ($name) { + //TODO: return last id + 1? + return false; + } } ?> \ No newline at end of file diff --git a/classes/database/adapter/mysql/db_MySqlRow.php b/classes/database/adapter/mysql/db_MySqlRow.php new file mode 100644 index 0000000..69b7576 --- /dev/null +++ b/classes/database/adapter/mysql/db_MySqlRow.php @@ -0,0 +1,12 @@ +caster)) + $this->caster = new caster_MySqlToPhp(); + } + +} + +?> \ No newline at end of file From 16499cb000ad911989ff247a308a52987a7968b0 Mon Sep 17 00:00:00 2001 From: Chris Chrisostomou Date: Fri, 30 Dec 2011 03:16:50 +0000 Subject: [PATCH 039/164] tabindexes + __tostring error handling (and useful errors) in form widgets --- classes/database/adapter/mysql/db_MySql.php | 2 +- .../form/elements/widget_AbstractElement.php | 33 +++++++++++-------- .../form/elements/widget_AgeRangeElement.php | 8 ++--- .../elements/widget_CheckBoxArrayElement.php | 3 +- .../form/elements/widget_CheckBoxElement.php | 3 +- .../form/elements/widget_DateElement.php | 9 +++-- .../form/elements/widget_FileElement.php | 3 +- .../form/elements/widget_NameElement.php | 12 +++---- .../form/elements/widget_RadioElement.php | 4 +-- .../form/elements/widget_SelectElement.php | 3 +- .../widget_TermsAndConditionsElement.php | 3 +- .../form/elements/widget_TextAreaElement.php | 3 +- .../elements/widget_TextConfirmElement.php | 12 +++---- .../form/elements/widget_TextElement.php | 25 +++++++------- .../widget_TextElementWithSurroundingText.php | 3 +- .../widget_TextWithSuggestionsElement.php | 8 +++-- .../form/elements/widget_UkAddressElement.php | 16 ++++----- classes/widgets/form/widget_Form.php | 4 +++ 18 files changed, 88 insertions(+), 66 deletions(-) diff --git a/classes/database/adapter/mysql/db_MySql.php b/classes/database/adapter/mysql/db_MySql.php index 3d73854..74314e1 100755 --- a/classes/database/adapter/mysql/db_MySql.php +++ b/classes/database/adapter/mysql/db_MySql.php @@ -11,7 +11,7 @@ */ /** - * MySql specific database interaction class + * MySql adaptor class * @package Database * @subpackage Vender.MySql * @author Chris Chrisostomou diff --git a/classes/widgets/form/elements/widget_AbstractElement.php b/classes/widgets/form/elements/widget_AbstractElement.php index b0ca6a8..1a0a06a 100644 --- a/classes/widgets/form/elements/widget_AbstractElement.php +++ b/classes/widgets/form/elements/widget_AbstractElement.php @@ -12,15 +12,15 @@ abstract class widget_AbstractElement { protected $validators = null; protected $errors = array(); protected $required = false; + protected $tabindex = ""; + protected $cssStyle = null; + protected $cssClass = null; /** * @deprecated */ protected $style = null; - - protected $cssStyle = null; - protected $cssClass = null; // abstract function outputSpecific(); @@ -56,15 +56,20 @@ static public function makeInputSafe($in) { public function render() { $out = $this->preRender(); - $out .= sfl('
%s%s%s
', - $this->style ? " " . $this->style : "", - $this->cssClass ? " " . $this->cssClass : "", - ($this->submitted && !$this->validates) ? " error" : "", - $this->name, - $this->cssStyle ? " style='" . $this->cssStyle . "'": "", - $this->renderErrors(), - ($this->label != '' || $this->getRequired() ? $this->renderLabel() : ''), - $this->renderElement()); + $out .= sfl('
', + $this->style ? " " . $this->style : "", + $this->cssClass ? " " . $this->cssClass : "", + ($this->submitted && !$this->validates) ? " error" : "", + $this->name, + $this->cssStyle ? " style='" . $this->cssStyle . "'": "" + ); + try { + $out .= sf('%s%s%s', $this->renderErrors(), ($this->label != '' || $this->getRequired() ? $this->renderLabel() : ''), $this->renderElement()); + } catch (Exception $e) { + $out .= $e->getMessage(); + } + $out .= '
'; + $out .= $this->postRender(); return $out; @@ -165,8 +170,8 @@ public function setCssClass($in) { $this->cssCLass = $in; } - public function setCssStyle($in) { - $this->cssStyle = $in; + public function setTabindex($in) { + $this->tabindex = (int) $in; } public function setForceDefault($in) { if(!is_bool($in)) throw new Exception("Force Default must be of type Bool"); diff --git a/classes/widgets/form/elements/widget_AgeRangeElement.php b/classes/widgets/form/elements/widget_AgeRangeElement.php index 8f8f0ee..46aa769 100644 --- a/classes/widgets/form/elements/widget_AgeRangeElement.php +++ b/classes/widgets/form/elements/widget_AgeRangeElement.php @@ -45,12 +45,12 @@ function renderElement() { } - $out = sfl("", - $this->getName(), $this->getName(), $minOptions + $out = sfl("", + $this->getName(), $this->getName(), ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $minOptions ); - $out .= sfl("%s ", - $this->separator, $this->getName(), $this->getName(), $maxOptions + $out .= sfl("%s ", + $this->separator, $this->getName(), $this->getName(), ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $maxOptions ); return $out; diff --git a/classes/widgets/form/elements/widget_CheckBoxArrayElement.php b/classes/widgets/form/elements/widget_CheckBoxArrayElement.php index 2a9712c..8bf638d 100644 --- a/classes/widgets/form/elements/widget_CheckBoxArrayElement.php +++ b/classes/widgets/form/elements/widget_CheckBoxArrayElement.php @@ -41,9 +41,10 @@ function renderElement() { } foreach($this->options as $value => $option) { - $out.=(sf("
%s
", + $out.=(sf("
%s
", $this->getName(),$value, (in_array($value, $valueArr) && $valueArr[$value]) ? "checked='checked'" : "", + ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName(),$value, $this->getName(),$value, $option, $this->delimiter diff --git a/classes/widgets/form/elements/widget_CheckBoxElement.php b/classes/widgets/form/elements/widget_CheckBoxElement.php index e554f95..38fe484 100644 --- a/classes/widgets/form/elements/widget_CheckBoxElement.php +++ b/classes/widgets/form/elements/widget_CheckBoxElement.php @@ -19,9 +19,10 @@ public function setValue($input, $files = null) { $this->value =(isset($input[$this->name]) && $input[$this->name] == 'on') ? true : false; } function renderElement() { - return(sf("", + return(sf("", $this->getName(), $this->getValue() ? "checked='checked'" : "", + ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName(), !is_null($this->onClick) && strlen($this->onClick)?sf(' onClick="%s"', $this->onClick):'' )); diff --git a/classes/widgets/form/elements/widget_DateElement.php b/classes/widgets/form/elements/widget_DateElement.php index 5bb03ff..c75ebbe 100644 --- a/classes/widgets/form/elements/widget_DateElement.php +++ b/classes/widgets/form/elements/widget_DateElement.php @@ -51,20 +51,23 @@ function renderElement() { - $out = sfl("", + $out = sfl("", $this->getName(), + ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName(), $dayOptions ); - $out .= sfl("", + $out .= sfl("", $this->getName(), + ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName(), $monthOptions ); - $out .= sfl("", + $out .= sfl("", $this->getName(), + ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName(), $yearOptions ); diff --git a/classes/widgets/form/elements/widget_FileElement.php b/classes/widgets/form/elements/widget_FileElement.php index c2ca34a..0868f2f 100644 --- a/classes/widgets/form/elements/widget_FileElement.php +++ b/classes/widgets/form/elements/widget_FileElement.php @@ -55,8 +55,9 @@ function renderElement() { $this->getName() )); else - return(sf("", + return(sf("", $this->getName(), + ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName() )); } diff --git a/classes/widgets/form/elements/widget_NameElement.php b/classes/widgets/form/elements/widget_NameElement.php index 3bc8f23..77facba 100644 --- a/classes/widgets/form/elements/widget_NameElement.php +++ b/classes/widgets/form/elements/widget_NameElement.php @@ -42,16 +42,16 @@ function renderElement() { } - $out = sfl("", - $this->getName(), $this->getName(), $titleOptions + $out = sfl("", + $this->getName(), ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName(), $titleOptions ); - $out .= sfl("", - $this->getName(), parent::makeInputSafe($name['firstName']), $this->getName() + $out .= sfl("", + $this->getName(), parent::makeInputSafe($name['firstName']), ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName() ); - $out .= sfl("", - $this->getName(), parent::makeInputSafe($name['lastName']), $this->getName() + $out .= sfl("", + $this->getName(), parent::makeInputSafe($name['lastName']), ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName() ); return $out; diff --git a/classes/widgets/form/elements/widget_RadioElement.php b/classes/widgets/form/elements/widget_RadioElement.php index b89e016..d2b93d1 100644 --- a/classes/widgets/form/elements/widget_RadioElement.php +++ b/classes/widgets/form/elements/widget_RadioElement.php @@ -11,9 +11,9 @@ function renderElement() { $elementValue = $this->getValue(); foreach($this->options as $value => $option) { if(strval($elementValue) == strval($value)) - $html .= sfl('
', $this->getName(), $value, $this->getName(),$value, $this->getName(), $value, $option); + $html .= sfl('
', $this->getName(), $value, ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName(),$value, $this->getName(), $value, $option); else - $html .= sfl('
', $this->getName(), $value, $this->getName(),$value, $this->getName(), $value, $option); + $html .= sfl('
', $this->getName(), $value, ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName(),$value, $this->getName(), $value, $option); } return sf("
%s
",$html); diff --git a/classes/widgets/form/elements/widget_SelectElement.php b/classes/widgets/form/elements/widget_SelectElement.php index 6dcda78..c0e6ae5 100644 --- a/classes/widgets/form/elements/widget_SelectElement.php +++ b/classes/widgets/form/elements/widget_SelectElement.php @@ -44,8 +44,9 @@ function renderElement() { ); } - return( sfl("", + return( sfl("", $this->getName(), + ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName(), $opionHtml diff --git a/classes/widgets/form/elements/widget_TermsAndConditionsElement.php b/classes/widgets/form/elements/widget_TermsAndConditionsElement.php index 90de7d4..8ea41ce 100644 --- a/classes/widgets/form/elements/widget_TermsAndConditionsElement.php +++ b/classes/widgets/form/elements/widget_TermsAndConditionsElement.php @@ -29,11 +29,12 @@ public function render() { } function renderElement() { - return(sf("
%s
%s
%s
", + return(sf("
%s
%s
%s
", $this->termsText, $this->renderErrors(), $this->getName(), $this->getValue() ? "checked='checked'" : "", + ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName(), $this->renderLabel() )); diff --git a/classes/widgets/form/elements/widget_TextAreaElement.php b/classes/widgets/form/elements/widget_TextAreaElement.php index a1e394c..a2ec510 100644 --- a/classes/widgets/form/elements/widget_TextAreaElement.php +++ b/classes/widgets/form/elements/widget_TextAreaElement.php @@ -18,8 +18,9 @@ public function outputSpecific() { } function renderElement() { - return( sf("", + return( sf("", $this->getName(), + ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName(), $this->disabled?" disabled='true'":"", !is_null($this->rows)?sf(' rows="%s"',$this->rows):'', diff --git a/classes/widgets/form/elements/widget_TextConfirmElement.php b/classes/widgets/form/elements/widget_TextConfirmElement.php index 17fa42a..b4fa0e3 100644 --- a/classes/widgets/form/elements/widget_TextConfirmElement.php +++ b/classes/widgets/form/elements/widget_TextConfirmElement.php @@ -18,18 +18,18 @@ function renderElement() { if(!array_key_exists(0,$value)) $value[0] = ""; if(!array_key_exists(1,$value)) $value[1] = ""; - $out = sf("
", - $this->htmlType, $this->getName(), parent::makeInputSafe($value[0]), $this->getName(), - $this->cssClassName, $this->cssClassName + $out = sf("
", + $this->htmlType, $this->getName(), parent::makeInputSafe($value[0]), ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', + $this->getName(), $this->cssClassName, $this->cssClassName ); $out .= sf("", $this->getName(), $this->confirmText, ($this->getRequired() && !$this->getValidates()) ? $this->goAsterisks() : ""); - $out .= sf("", - $this->htmlType, $this->getName(), parent::makeInputSafe($value[1]), $this->getName(), - $this->cssClassName, $this->cssClassName + $out .= sf("", + $this->htmlType, $this->getName(), parent::makeInputSafe($value[1]), ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', + $this->getName(), $this->cssClassName, $this->cssClassName ); return $out; diff --git a/classes/widgets/form/elements/widget_TextElement.php b/classes/widgets/form/elements/widget_TextElement.php index 7660588..ce1f856 100644 --- a/classes/widgets/form/elements/widget_TextElement.php +++ b/classes/widgets/form/elements/widget_TextElement.php @@ -30,18 +30,19 @@ public function __construct($args) { } function renderElement() { - return(sf('', - $this->htmlType, - $this->getName(), - parent::makeInputSafe($this->getValue()), - $this->getName(), - $this->cssClassName, - strlen($this->placeholder)?sf(' placeholder="%s"', $this->placeholder):'', - !is_null($this->onChange) && strlen($this->onChange)?sf(' onChange="%s"', $this->onChange):'', - !is_null($this->onKeydown) && strlen($this->onKeydown)?sf(' onKeydown="%s"', $this->onKeydown):'', - !is_null($this->onFocus) && strlen($this->onFocus)?sf(' onFocus="%s"', $this->onFocus):'', - !is_null($this->onBlur) && strlen($this->onBlur)?sf(' onBlur="%s"', $this->onBlur):'' - )); + return(sf('', + $this->htmlType, + $this->getName(), + parent::makeInputSafe($this->getValue()), + ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', + $this->getName(), + $this->cssClassName, + strlen($this->placeholder)?sf(' placeholder="%s"', $this->placeholder):'', + !is_null($this->onChange) && strlen($this->onChange)?sf(' onChange="%s"', $this->onChange):'', + !is_null($this->onKeydown) && strlen($this->onKeydown)?sf(' onKeydown="%s"', $this->onKeydown):'', + !is_null($this->onFocus) && strlen($this->onFocus)?sf(' onFocus="%s"', $this->onFocus):'', + !is_null($this->onBlur) && strlen($this->onBlur)?sf(' onBlur="%s"', $this->onBlur):'' + )); } } diff --git a/classes/widgets/form/elements/widget_TextElementWithSurroundingText.php b/classes/widgets/form/elements/widget_TextElementWithSurroundingText.php index 9a5f2d7..3df56f8 100644 --- a/classes/widgets/form/elements/widget_TextElementWithSurroundingText.php +++ b/classes/widgets/form/elements/widget_TextElementWithSurroundingText.php @@ -17,10 +17,11 @@ function renderElement() { if( $this->beforeText != '' ) $ret .= sf('%s ', $this->beforeText); - $ret .= sf("", + $ret .= sf("", $this->htmlType, $this->getName(), parent::makeInputSafe($this->getValue()), + ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName(), $this->inputStyle ); diff --git a/classes/widgets/form/elements/widget_TextWithSuggestionsElement.php b/classes/widgets/form/elements/widget_TextWithSuggestionsElement.php index bc4974b..5e78fa6 100644 --- a/classes/widgets/form/elements/widget_TextWithSuggestionsElement.php +++ b/classes/widgets/form/elements/widget_TextWithSuggestionsElement.php @@ -24,15 +24,17 @@ function renderElement() { foreach($this->options as $value => $option) { if(strval($elementValue) == strval($value)) { $usedSuggested = true; - $html .= sfl('
%s
', + $html .= sfl('
%s
', $this->getName(), parent::makeInputSafe($value), + ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $option ); } else - $html .= sfl('
%s
', + $html .= sfl('
%s
', $this->getName(), parent::makeInputSafe($value), + ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $option ); } @@ -40,7 +42,7 @@ function renderElement() { return sf("
%s
%s %s%s
", $html, - sfl('', $this->getName(), $usedSuggested ? "" :'checked="checked"'), + sfl('', $this->getName(), ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $usedSuggested ? "" :'checked="checked"'), $this->beforeText, $this->getName()."_custom", $usedSuggested ? "" : parent::makeInputSafe($this->getValue()), diff --git a/classes/widgets/form/elements/widget_UkAddressElement.php b/classes/widgets/form/elements/widget_UkAddressElement.php index ec53b33..686408d 100644 --- a/classes/widgets/form/elements/widget_UkAddressElement.php +++ b/classes/widgets/form/elements/widget_UkAddressElement.php @@ -23,24 +23,24 @@ function renderElement() { * The rendering of this element is SO messy due to IE compatibility - needs rewriting * */ - $out = sf("

", - $this->getName(), parent::makeInputSafe($address['address1']), $this->getName() + $out = sf("

", + $this->getName(), parent::makeInputSafe($address['address1']), ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName() ); - $out .= sf("
", - $this->getName(), parent::makeInputSafe($address['address2']), $this->getName() + $out .= sf("
", + $this->getName(), parent::makeInputSafe($address['address2']), ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName() ); $out .= sf("
", $this->getName(), "Post Code" ); - $out .= sfl("
", - $this->getName(), parent::makeInputSafe($address['postcode']), $this->getName() + $out .= sfl("
", + $this->getName(), parent::makeInputSafe($address['postcode']), ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName() ); - $out .= sfl("
", - $this->getName(), parent::makeInputSafe($address['town']), $this->getName() + $out .= sfl("
", + $this->getName(), parent::makeInputSafe($address['town']), ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName() ); return $out; diff --git a/classes/widgets/form/widget_Form.php b/classes/widgets/form/widget_Form.php index c8f9723..20637e2 100644 --- a/classes/widgets/form/widget_Form.php +++ b/classes/widgets/form/widget_Form.php @@ -101,6 +101,10 @@ public function add($elementClass, $args) { // css style if(array_key_exists('cssStyle',$args)) $element->setCssStyle($args['cssStyle']); + + // tabindexes + if(array_key_exists('tabindex',$args)) + $element->setTabindex($args['tabindex']); // css style if(array_key_exists('cssClass',$args)) From 37f1d06d08c648ad1977c12d7209063bdc7033cf Mon Sep 17 00:00:00 2001 From: Chris Chrisostomou Date: Fri, 30 Dec 2011 15:50:39 +0000 Subject: [PATCH 040/164] default tabindex to false for consistency --- classes/widgets/form/elements/widget_AbstractElement.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/widgets/form/elements/widget_AbstractElement.php b/classes/widgets/form/elements/widget_AbstractElement.php index 1a0a06a..b2906cf 100644 --- a/classes/widgets/form/elements/widget_AbstractElement.php +++ b/classes/widgets/form/elements/widget_AbstractElement.php @@ -12,7 +12,7 @@ abstract class widget_AbstractElement { protected $validators = null; protected $errors = array(); protected $required = false; - protected $tabindex = ""; + protected $tabindex = false; protected $cssStyle = null; protected $cssClass = null; From 42ffd215443e1a08056831c12f10d85942142b36 Mon Sep 17 00:00:00 2001 From: Chris Chrisostomou Date: Wed, 11 Jan 2012 20:46:20 +0000 Subject: [PATCH 041/164] Option to change type in text elements --- classes/widgets/form/elements/widget_TextElement.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/classes/widgets/form/elements/widget_TextElement.php b/classes/widgets/form/elements/widget_TextElement.php index ce1f856..aacb597 100644 --- a/classes/widgets/form/elements/widget_TextElement.php +++ b/classes/widgets/form/elements/widget_TextElement.php @@ -14,7 +14,10 @@ class widget_TextElement extends widget_AbstractElement { public function __construct($args) { if (array_key_exists('placeholder', $args) && strlen($args['placeholder'])) $this->placeholder = $args['placeholder']; - + + if (array_key_exists('type', $args) && strlen($args['type'])) + $this->htmlType = $args['type']; + if (array_key_exists('onChange', $args) && strlen($args['onChange'])) $this->onChange = $args['onChange']; @@ -44,6 +47,7 @@ function renderElement() { !is_null($this->onBlur) && strlen($this->onBlur)?sf(' onBlur="%s"', $this->onBlur):'' )); } + } ?> \ No newline at end of file From b31928439d0ee37a0fefffd9900c1257be80e924 Mon Sep 17 00:00:00 2001 From: Jonny Stirling Date: Sat, 14 Jan 2012 19:14:57 +0100 Subject: [PATCH 042/164] Fix english --- .../widgets/form/elements/widget_SelectElement.php | 14 +++++++------- classes/widgets/pagination/widget_Paginate.php | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/classes/widgets/form/elements/widget_SelectElement.php b/classes/widgets/form/elements/widget_SelectElement.php index c0e6ae5..167ddbf 100644 --- a/classes/widgets/form/elements/widget_SelectElement.php +++ b/classes/widgets/form/elements/widget_SelectElement.php @@ -10,21 +10,21 @@ public function __construct($args) { $this->blankMessage = $args['blank']; } function renderElement() { - $opionHtml = ""; + $optionHtml = ""; $elementValue = $this->getValue(); if(!$this->defaultValue && $this->blankMessage) - $opionHtml .= sfl("", $this->blankMessage); + $optionHtml .= sfl("", $this->blankMessage); elseif($this->defaultValue) { // TODO: put this in a func... foreach($this->defaultValue as $value => $option) { if(strval($elementValue) == strval($value)) - $opionHtml .= sfl("", + $optionHtml .= sfl("", $value, $option ); else - $opionHtml .= sfl("", + $optionHtml .= sfl("", $value, $option ); @@ -33,12 +33,12 @@ function renderElement() { } foreach($this->options as $value => $option) { if(strval($elementValue) == strval($value)) - $opionHtml .= sfl("", + $optionHtml .= sfl("", $value, $option ); else - $opionHtml .= sfl("", + $optionHtml .= sfl("", $value, $option ); @@ -48,7 +48,7 @@ function renderElement() { $this->getName(), ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName(), - $opionHtml + $optionHtml ) ); diff --git a/classes/widgets/pagination/widget_Paginate.php b/classes/widgets/pagination/widget_Paginate.php index f68fc8e..e846193 100644 --- a/classes/widgets/pagination/widget_Paginate.php +++ b/classes/widgets/pagination/widget_Paginate.php @@ -116,7 +116,7 @@ public function setFormatTemplate ($template = 1) { throw new Exception ('Unknown pagination template'); case self::TEMPLATE_CLASSIC: - $this->format = ''; + $this->format = ''; $this->formatNext = array('', ''); $this->formatPrevious = array('', ''); break; From 3b2e4e0669c1c4c66d877f24eef0855a0c5d6b2a Mon Sep 17 00:00:00 2001 From: Jonny Stirling Date: Sat, 14 Jan 2012 19:15:37 +0100 Subject: [PATCH 043/164] Allow on-the-fly form element removal --- classes/widgets/form/widget_Form.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/classes/widgets/form/widget_Form.php b/classes/widgets/form/widget_Form.php index 20637e2..860b357 100644 --- a/classes/widgets/form/widget_Form.php +++ b/classes/widgets/form/widget_Form.php @@ -128,6 +128,24 @@ public function add($elementClass, $args) { $this->elementMap[$element->getName()] = &$element; } + public function remove($elementName) { + if(!isset($this->elementMap[$elementName])) { + throw new Exception('Cannot remove non-existant element'); + } + for($i = 0; $i < count($this->elements); $i++) { + $done = false; + if($this->elementMap[$elementName] == $this->elements[$i]) { + unset($this->elementMap[$elementName]); + unset($this->elements[$i]); + $done = true; + } + if($done) { + $this->elements = array_values($this->elements); + break; + } + } + } + public function value($elementName) { if(!array_key_exists($elementName, $this->elementMap)) throw new Exception("Element not found: ".$elementName); From 8da01d116da8c0d942f992e53f3796d6fe8fd2e1 Mon Sep 17 00:00:00 2001 From: Jonny Stirling Date: Sat, 14 Jan 2012 19:16:02 +0100 Subject: [PATCH 044/164] Add delete option to DB API --- classes/database/db_AbstractDatabase.php | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/classes/database/db_AbstractDatabase.php b/classes/database/db_AbstractDatabase.php index 34b1b92..ffe36ef 100644 --- a/classes/database/db_AbstractDatabase.php +++ b/classes/database/db_AbstractDatabase.php @@ -496,13 +496,32 @@ public function count ($table, $where = null) { * DELETE * */ public function delete ($args) { - throw new db_Exception ('TODO'); + /* parse the query */ + $args = func_get_args(); + $query = call_user_func_array (array ($this, 'parseDeleteQuery'), $args); + + /* perform query */ + $this->query('%l', $query); + + return true; } public function deleteOne ($args) { throw new db_Exception ('TODO'); } public function parseDeleteQuery ($args) { - throw new db_Exception ('TODO'); + if (is_null($this->caster)) + throw new db_Exception('Caster not loaded.'); + + $args = func_get_args (); + $sets = $this->caster->castArraySets($args); + $deleteFrom = array_shift ($sets); + $where = array_shift ($sets); + + /* return update query string */ + return $this->caster->castString( + 'DELETE FROM %@ WHERE %l', $deleteFrom, $where + ); + } /* From 724535bd2471db08d6f6485f1e8da1f7590ff2a5 Mon Sep 17 00:00:00 2001 From: Jonny Stirling Date: Sun, 15 Jan 2012 16:17:46 +0100 Subject: [PATCH 045/164] Add empty constructor for atsumi-go support --- classes/core/app/atsumi_AbstractAppSettings.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/classes/core/app/atsumi_AbstractAppSettings.php b/classes/core/app/atsumi_AbstractAppSettings.php index 2c4b81c..80f037b 100644 --- a/classes/core/app/atsumi_AbstractAppSettings.php +++ b/classes/core/app/atsumi_AbstractAppSettings.php @@ -35,6 +35,7 @@ abstract class atsumi_AbstractAppSettings { protected $cache = array(); /* CONSTRUCTOR & DESTRUCTOR */ + public function __construct() { } /* GET METHODS */ /** @@ -134,4 +135,4 @@ protected function &initCache($name) { return $this->cache[$name]; } } -?> \ No newline at end of file +?> From 5d82ccd91e1103091441e5f32338962021188231 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Thu, 19 Jan 2012 14:04:56 +0000 Subject: [PATCH 046/164] Elaborated on the element exception handling Chris added to forms. It now only displays exception information if running in debug mode and it always fires the exception listeners. Also minor fix to atsumi_Debug. --- classes/core/debug/atsumi_Debug.php | 6 +- .../form/elements/widget_AbstractElement.php | 124 +++++++++--------- 2 files changed, 68 insertions(+), 62 deletions(-) diff --git a/classes/core/debug/atsumi_Debug.php b/classes/core/debug/atsumi_Debug.php index d877005..83c8108 100644 --- a/classes/core/debug/atsumi_Debug.php +++ b/classes/core/debug/atsumi_Debug.php @@ -172,7 +172,7 @@ protected static function getInstance() { * @return boolean */ public function _getActive() { - return $active; + return $this->active; } /* SET FUNCTIONS */ @@ -366,7 +366,7 @@ public function _addArea($name, $color) { * @return string A html5 valid string representation of the variable */ protected function format($value) { - + if(is_null($value)) return 'NULL'; @@ -671,7 +671,7 @@ public function _render() { if(!$this->active) return; - + $this->startTimer(); $display =(isset($_COOKIE['debugDisplay']) ? strtolower($_COOKIE['debugDisplay']) : 'console'); ob_start(); diff --git a/classes/widgets/form/elements/widget_AbstractElement.php b/classes/widgets/form/elements/widget_AbstractElement.php index b2906cf..1a95be6 100644 --- a/classes/widgets/form/elements/widget_AbstractElement.php +++ b/classes/widgets/form/elements/widget_AbstractElement.php @@ -1,19 +1,19 @@ outputGeneric(), + + return array_merge( $this->outputGeneric(), $this->outputSpecific()); } protected function outputGeneric() { - + return array( "name" => $this->getName(), "value" => $this->getValue(), "validation" => $this->getValidationData() - ); + ); } */ - + protected function preRender() { } protected function postRender() { } - + static public function makeInputSafe($in) { $in = str_replace("'", "'", $in); $in = str_replace('"', '"', $in); return $in; } - + public function render() { $out = $this->preRender(); $out .= sfl('
', @@ -66,15 +66,21 @@ public function render() { try { $out .= sf('%s%s%s', $this->renderErrors(), ($this->label != '' || $this->getRequired() ? $this->renderLabel() : ''), $this->renderElement()); } catch (Exception $e) { - $out .= $e->getMessage(); + + // if in debug mode display exception details + if (atsumi_Debug::getActive()) + $out .= sfl('Element Exception "%s": %s #%s', $e->getMessage(), $e->getFile(), $e->getLine()); + + // fire error listeners + Atsumi::error__listen($e); } $out .= '
'; - + $out .= $this->postRender(); return $out; - + } - + protected function renderErrors() { if(!count($this->errors) || !$this->submitted || $this->forceDefault) return; $div = "
"; @@ -85,20 +91,20 @@ protected function renderErrors() { $div .= ""; $div .= "
"; return $div; - + } - - protected function renderLabel() { + + protected function renderLabel() { return sf("", $this->getName(), $this->label, ($this->getRequired() && !$this->getValidates()) ? $this->goAsterisks() : ""); } - + protected function goAsterisks() { - return "*"; - } - + return "*"; + } + public function validate() { if(is_null($this->validators)) return true; @@ -111,12 +117,12 @@ public function validate() { foreach($this->validators as $validator) { if($validator instanceof validate_Required && $validator->isRequired()) $this->required = true; - + $elementValue =($this->forceDefault) ? null : $this->getValue(); - + if(is_null($elementValue) && !is_null($this->getDefault())) $elementValue = $this->getDefault(); - + try { $validator->validate($elementValue); } catch(Exception $e) { @@ -126,87 +132,87 @@ public function validate() { } } protected function getValidationData() { - - if($this->submitted && !$this->forceDefault) + + if($this->submitted && !$this->forceDefault) $validateArr = array( "validates" => $this->validates, "errors" => $this->errors ); else $validateArr = array( "validates" => $this->validates, "errors" => array()); - + return $validateArr; } function setSubmitted($in) { $this->submitted = $in; } - + public function setValidators($in) { $this->validators = $in; } public function setName($in) { if(!is_string($in)) throw new Exception("Name must be of type String"); - $this->name = $in; + $this->name = $in; } - + public function setLabel($in) { - $this->label = $in; + $this->label = $in; } - + public function setError(Exception $e) { - $this->errors[] = $e->getMessage(); + $this->errors[] = $e->getMessage(); } - + public function setValue($input, $files = array()) { - $this->value = isset($input[$this->name]) ? $input[$this->name] : null; + $this->value = isset($input[$this->name]) ? $input[$this->name] : null; } public function setDefault($in) { - $this->defaultValue = $in; + $this->defaultValue = $in; } public function setCssClass($in) { - $this->cssCLass = $in; + $this->cssCLass = $in; } public function setTabindex($in) { - $this->tabindex = (int) $in; + $this->tabindex = (int) $in; } public function setForceDefault($in) { if(!is_bool($in)) throw new Exception("Force Default must be of type Bool"); - $this->forceDefault = $in; + $this->forceDefault = $in; } - + public function getName() { - return $this->name; + return $this->name; } public function getValue() { - return(is_null($this->value) || $this->getForceDefault()) ? - $this->defaultValue : - $this->value; + return(is_null($this->value) || $this->getForceDefault()) ? + $this->defaultValue : + $this->value; } public function getValidates() { - return $this->validates; + return $this->validates; } public function getDefault() { - return $this->defaultValue; + return $this->defaultValue; } public function getForceDefault() { - return $this->forceDefault; + return $this->forceDefault; } public function getRequired() { - return $this->required; + return $this->required; } - + /** * @deprecated */ public function setStyle($in) { - $this->style = $in; + $this->style = $in; } - + } From b0f6669c382f8eb255d9804c86362ae8fc08de8d Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Fri, 20 Jan 2012 16:16:15 +0000 Subject: [PATCH 047/164] added more functionality for setting / getting array data out of postgres --- .../adapter/postgresql/caster_PostgreSql.php | 7 ++++- .../postgresql/caster_PostgreSqlToPhp.php | 26 +++++++++---------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/classes/database/adapter/postgresql/caster_PostgreSql.php b/classes/database/adapter/postgresql/caster_PostgreSql.php index 16882c4..4903477 100644 --- a/classes/database/adapter/postgresql/caster_PostgreSql.php +++ b/classes/database/adapter/postgresql/caster_PostgreSql.php @@ -27,6 +27,7 @@ class caster_PostgreSql extends caster_Abstract { protected $spec = array( '@' => 'tableName', 'a' => 'sqlArray', + 'A' => 'sqlArrayOrNull', 'b' => 'boolean', 'c' => 'character', 'C' => 'characterVarying', @@ -113,6 +114,10 @@ static function sqlArray($in) { return "ARRAY[".$sqlArr."]"; } + static function sqlArrayOrNull($in) { + if (is_null($in)) return 'NULL'; + return self::sqlArray($in); + } /** * Casts a variable into a PostgreSQL character @@ -203,7 +208,7 @@ static function timestampWithTimezone($in) { static function date($in) { return sf("'%s'::DATE", $in); } - + /** * Casts a variable into a PostgreSQL date * @param string $in String to be casted diff --git a/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php b/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php index 1dab094..8644382 100644 --- a/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php +++ b/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php @@ -91,28 +91,28 @@ static function textOrNull($in) { * @return string Casted string */ static function sqlArray($in) { - - if (!is_array($in)) throw new caster_StrictTypeException('Expected Array, received: '.$in.' ('.gettype($in).')'); - + + if (!is_string($in)) throw new caster_StrictTypeException('Expected String, received: '.$in.' ('.gettype($in).')'); if (is_null($in)) return array(); - + $in = str_replace(array("{","}"),"", $in); $arr = explode(",",$in); - + + // int - if ($type == "integer") { + /* if ($type == "integer") { $newArr = array(); foreach ($arr as $val) $newArr[] = intval($val); $arr = $newArr; - } + } */ return $arr; } - + static function sqlArrayOrNull($in) { - if (!is_array($in) && !is_null($in)) throw new caster_StrictTypeException('Expected Array or Null, received: '.$in.' ('.gettype($in).')'); + if (!is_string($in) && !is_null($in)) throw new caster_StrictTypeException('Expected String or Null, received: '.$in.' ('.gettype($in).')'); - if (is_array($in) && count($in)) return self::text($in); + if (is_string($in)) return self::sqlArray($in); elseif (is_null($in)) return null; } @@ -140,7 +140,7 @@ static function integerOrNull($in) { if (!is_int(intval($in))) throw new caster_StrictTypeException('Expected Integer or Null, received: '.$in.' ('.gettype($in).')'); return intval($in); } - + static function float($in) { if (!is_numeric($in)) throw new caster_StrictTypeException('Expected Float, received: '.$in.' ('.gettype($in).')'); setType($in, 'float'); @@ -155,12 +155,12 @@ static function floatOrNull($in) { static function date($in) { return atsumi_Date::fromYmd($in); - } + } static function dateOrNull($in) { if (is_null($in)) return null; return atsumi_Date::fromYmd($in); } - + static function timestampWithTimezone($in) { return new atsumi_DateTime(strtotime($in)); } From a684eb2ae5068dfa9fa49656541152b59229ecc4 Mon Sep 17 00:00:00 2001 From: Jonny Stirling Date: Sun, 22 Jan 2012 19:23:38 +0100 Subject: [PATCH 048/164] Multiples of 'date' function in mysql->php caster removed --- classes/database/adapter/mysql/caster_MySqlToPhp.php | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/classes/database/adapter/mysql/caster_MySqlToPhp.php b/classes/database/adapter/mysql/caster_MySqlToPhp.php index 45f6a52..bdc4456 100644 --- a/classes/database/adapter/mysql/caster_MySqlToPhp.php +++ b/classes/database/adapter/mysql/caster_MySqlToPhp.php @@ -132,14 +132,6 @@ static function date($in) { return atsumi_Date::fromYmd($in); } - static function date($in) { - return atsumi_Date::fromYmd($in); - } - - static function date($in) { - return atsumi_Date::fromYmd($in); - } - static function time($in) { if (is_null($in)) return null; //TODO: time stuff @@ -152,4 +144,4 @@ static function timeOrNull($in) { return $in; } } -?> \ No newline at end of file +?> From 809128007c9d85c3cf25ab43951c3cd5f4a38195 Mon Sep 17 00:00:00 2001 From: Jonny Stirling Date: Tue, 31 Jan 2012 23:46:43 +0100 Subject: [PATCH 049/164] Add recaptcha form widget and validator --- classes/validators/validate_Recaptcha.php | 157 ++++++++++++++++++ .../form/elements/widget_RecaptchaElement.php | 24 +++ 2 files changed, 181 insertions(+) create mode 100644 classes/validators/validate_Recaptcha.php create mode 100644 classes/widgets/form/elements/widget_RecaptchaElement.php diff --git a/classes/validators/validate_Recaptcha.php b/classes/validators/validate_Recaptcha.php new file mode 100644 index 0000000..ccbb3a2 --- /dev/null +++ b/classes/validators/validate_Recaptcha.php @@ -0,0 +1,157 @@ +privateKey = $privateKey; + } + + public function validate($data) { + // We don't care about the data variable, check for... + if(!isset($_POST['recaptcha_challenge_field']) || !isset($_POST['recaptcha_response_field'])) { + throw new Exception('Missing required reCAPTCHA field'); + } + + $answer = $this->recaptcha_check_answer($this->privateKey, + $_SERVER["REMOTE_ADDR"], + $_POST["recaptcha_challenge_field"], + $_POST["recaptcha_response_field"] + ); + if($answer->is_valid) { + return true; + } + throw new Exception('Invalid CAPTCHA answer'); + } + + /* + * Hacked up and class-ified functions from the recaptchalib.php written by + * Mike Crawford + * Ben Maurer + * + * 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. + */ + + /** + * The reCAPTCHA server URL's + */ + const RECAPTCHA_VERIFY_SERVER = 'www.google.com'; + + /** + * Encodes the given data into a query string format + * @param $data - array of string elements to be encoded + * @return string - encoded request + */ + function _recaptcha_qsencode ($data) { + $req = ""; + foreach ( $data as $key => $value ) + $req .= $key . '=' . urlencode( stripslashes($value) ) . '&'; + + // Cut the last '&' + $req=substr($req,0,strlen($req)-1); + return $req; + } + + + /** + * Submits an HTTP POST to a reCAPTCHA server + * @param string $host + * @param string $path + * @param array $data + * @param int port + * @return array response + */ + function _recaptcha_http_post($host, $path, $data, $port = 80) { + $req = $this->_recaptcha_qsencode ($data); + $http_request = "POST $path HTTP/1.0\r\n"; + $http_request .= "Host: $host\r\n"; + $http_request .= "Content-Type: application/x-www-form-urlencoded;\r\n"; + $http_request .= "Content-Length: " . strlen($req) . "\r\n"; + $http_request .= "User-Agent: reCAPTCHA/PHP\r\n"; + $http_request .= "\r\n"; + $http_request .= $req; + + $response = ''; + if( false == ( $fs = @fsockopen($host, $port, $errno, $errstr, 10) ) ) { + die ('Could not open socket'); + } + + fwrite($fs, $http_request); + + while ( !feof($fs) ) + $response .= fgets($fs, 1160); // One TCP-IP packet + fclose($fs); + $response = explode("\r\n\r\n", $response, 2); + + return $response; + } + + + /** + * Calls an HTTP POST function to verify if the user's guess was correct + * @param string $privkey + * @param string $remoteip + * @param string $challenge + * @param string $response + * @param array $extra_params an array of extra variables to post to the server + * @return ReCaptchaResponse + */ + function recaptcha_check_answer ($privkey, $remoteip, $challenge, $response, $extra_params = array()) { + if ($privkey == null || $privkey == '') { + die ("To use reCAPTCHA you must get an API key from https://www.google.com/recaptcha/admin/create"); + } + if ($remoteip == null || $remoteip == '') { + die ("For security reasons, you must pass the remote ip to reCAPTCHA"); + } + + //discard spam submissions + if ($challenge == null || strlen($challenge) == 0 || $response == null || strlen($response) == 0) { + $recaptcha_response = new ReCaptchaResponse(); + $recaptcha_response->is_valid = false; + $recaptcha_response->error = 'incorrect-captcha-sol'; + return $recaptcha_response; + } + $response = $this->_recaptcha_http_post (self::RECAPTCHA_VERIFY_SERVER, "/recaptcha/api/verify", + array ( + 'privatekey' => $privkey, + 'remoteip' => $remoteip, + 'challenge' => $challenge, + 'response' => $response + ) + $extra_params + ); + + $answers = explode ("\n", $response [1]); + $recaptcha_response = new ReCaptchaResponse(); + if (trim ($answers [0]) == 'true') { + $recaptcha_response->is_valid = true; + } else { + $recaptcha_response->is_valid = false; + $recaptcha_response->error = $answers [1]; + } + return $recaptcha_response; + } +} + + +/** + * A ReCaptchaResponse is returned from recaptcha_check_answer() + */ +class ReCaptchaResponse { + var $is_valid; + var $error; +} +?> diff --git a/classes/widgets/form/elements/widget_RecaptchaElement.php b/classes/widgets/form/elements/widget_RecaptchaElement.php new file mode 100644 index 0000000..4e2480c --- /dev/null +++ b/classes/widgets/form/elements/widget_RecaptchaElement.php @@ -0,0 +1,24 @@ +publicKey = $args['apiKey']; + } + function renderElement() { + $output = sf(' + + ', $this->publicKey, $this->publicKey); + return $output; + } +} +?> From 206ffdc0ec0f4797604538f23fe97a10cf64e102 Mon Sep 17 00:00:00 2001 From: Jonny Stirling Date: Wed, 1 Feb 2012 15:08:14 +0100 Subject: [PATCH 050/164] Check if CLI option exists before trying to get it --- classes/core/app/atsumi_AppHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/core/app/atsumi_AppHandler.php b/classes/core/app/atsumi_AppHandler.php index 239ac78..d540145 100644 --- a/classes/core/app/atsumi_AppHandler.php +++ b/classes/core/app/atsumi_AppHandler.php @@ -208,7 +208,7 @@ public function createUri($controller, $method) { * @param string $uri The uri to processed */ public function go($uri) { - if($this->settings->get_cli === true) { + if(isset($this->settings->get_cli) && $this->settings->get_cli === true) { $this->setBaseUri('.'); $this->setCommand($uri); $this->parseCommand(); From 069c5209c77040f445cb4c8870704a26b9033b6b Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Wed, 8 Feb 2012 11:19:58 +0000 Subject: [PATCH 051/164] readded the setCssStyle method --- classes/widgets/form/elements/widget_AbstractElement.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/classes/widgets/form/elements/widget_AbstractElement.php b/classes/widgets/form/elements/widget_AbstractElement.php index 1a95be6..a9e58a1 100644 --- a/classes/widgets/form/elements/widget_AbstractElement.php +++ b/classes/widgets/form/elements/widget_AbstractElement.php @@ -173,7 +173,11 @@ public function setDefault($in) { } public function setCssClass($in) { - $this->cssCLass = $in; + $this->cssClass = $in; + } + + public function setCssStyle($in) { + $this->cssStyle = $in; } public function setTabindex($in) { From e9514aee77e46bd0f82b13bbc9d5446de12a4eea Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 20 Mar 2012 12:16:46 +0000 Subject: [PATCH 052/164] Added image picker form element --- .../elements/widget_ImagePickerElement.php | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 classes/widgets/form/elements/widget_ImagePickerElement.php diff --git a/classes/widgets/form/elements/widget_ImagePickerElement.php b/classes/widgets/form/elements/widget_ImagePickerElement.php new file mode 100644 index 0000000..04608ca --- /dev/null +++ b/classes/widgets/form/elements/widget_ImagePickerElement.php @@ -0,0 +1,80 @@ +options = $args['options']; + if(array_key_exists('blank', $args)) + $this->blankMessage = $args['blank']; + + if(array_key_exists('width', $args)) + $this->width = $args['width']; + + if(array_key_exists('height', $args)) + $this->height = $args['height']; + } + function renderThumbnail ($value, $imageUrl, $selected) { + + // TODO: Would be nice to add option for multi select + // would convert to checkbox's rather than radio's + + return sfl('
+ + +
', + $this->width, + $this->getName(), + $value, + $imageUrl, + $this->width, + $this->height, + $this->getName(), + $value, + $this->getName(), + $value, + $selected?'checked="checked"':''); + + } + function renderElement() { + $htmlOut = ""; + $elementValue = $this->getValue(); + + foreach($this->options as $value => $imageUrl) { + + $htmlOut .= $this->renderThumbnail( + $value, + $imageUrl, + strval($elementValue) == strval($value) + ); + + } + + return( sfl("
%s
", + $this->getName(), + ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', + $this->getName(), + $htmlOut + + ) + ); + } +} + +?> \ No newline at end of file From 42f41b5efdead148e6b93cb01964361837b23f4f Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 3 Jul 2012 14:57:38 +0100 Subject: [PATCH 053/164] minor fixes. Safer postgres casting. Validate max chars supports foreign characters (multibyte characters), forms can now have custom css classes using ->addCssClass() --- .../adapter/postgresql/caster_PostgreSql.php | 2 +- .../postgresql/caster_PostgreSqlToPhp.php | 33 ++++++++++- classes/utility/http/atsumi_Http.php | 58 +++++++++---------- classes/validators/validate_MaxChars.php | 4 +- .../elements/widget_TextConfirmElement.php | 12 +++- classes/widgets/form/widget_Form.php | 22 ++++--- 6 files changed, 86 insertions(+), 45 deletions(-) diff --git a/classes/database/adapter/postgresql/caster_PostgreSql.php b/classes/database/adapter/postgresql/caster_PostgreSql.php index 4903477..ef88969 100644 --- a/classes/database/adapter/postgresql/caster_PostgreSql.php +++ b/classes/database/adapter/postgresql/caster_PostgreSql.php @@ -109,7 +109,7 @@ static function sqlArray($in) { $sqlArr = ""; foreach($in as $item) { $sqlArr .= ($sqlArr == "") ? "" : ", "; - $sqlArr .= is_int($item) ? $item : "'".$item."'"; + $sqlArr .= is_int($item) ? $item : "'".pg_escape_string($item)."'"; } return "ARRAY[".$sqlArr."]"; diff --git a/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php b/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php index 8644382..d3584d9 100644 --- a/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php +++ b/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php @@ -96,9 +96,38 @@ static function sqlArray($in) { if (is_null($in)) return array(); $in = str_replace(array("{","}"),"", $in); - $arr = explode(",",$in); + $arrayOut = array(); + $processing= $in; + while(strlen($processing) > 0) { + + if (substr($processing, 0, 1) == ',') { + $processing = substr($processing, 1); + } + + preg_match('/^\"([^"]*)\"/', $processing, $match); + if (count($match)) { + + if (strlen($processing) === strlen($match[0])) + $processing = ''; + else $processing = substr($processing, strlen($match[0])); + + $arrayOut[] = $match[1]; + continue; + } + + preg_match('/^([^,]*)/', $processing, $match); + if (count($match)) { + + if (strlen($processing) === strlen($match[0])) + $processing = ''; + else $processing = substr($processing, strlen($match[0])); + + $arrayOut[] = $match[1]; + continue; + } + } // int /* if ($type == "integer") { $newArr = array(); @@ -106,7 +135,7 @@ static function sqlArray($in) { $newArr[] = intval($val); $arr = $newArr; } */ - return $arr; + return $arrayOut; } static function sqlArrayOrNull($in) { diff --git a/classes/utility/http/atsumi_Http.php b/classes/utility/http/atsumi_Http.php index f1aa3ec..ff05f5d 100644 --- a/classes/utility/http/atsumi_Http.php +++ b/classes/utility/http/atsumi_Http.php @@ -6,17 +6,17 @@ * @subpackage Http */ class atsumi_Http { - - + + // 1xx Informational - + const INFO_CONTINUE = 100; const INFO_SWITHCING_PROTOCOLS = 101; const INFO_PROCESSING = 102; - - + + // 2xx Informational - + const SUCCESS_OK = 200; const SUCCESS_CREATED = 201; const SUCCESS_ACCEPTED = 202; @@ -25,10 +25,10 @@ class atsumi_Http { const SUCCESS_RESET_CONTENT = 205; const SUCCESS_PARTIAL_CONTENT = 206; const SUCCESS_MULTI_STATUS = 207; - - + + // 3xx Redirection - + const REDIRECT_MULTIPLE_CHOICES = 300; const REDIRECT_MOVED_PERMANENTLY = 301; const REDIRECT_FOUND = 302; @@ -37,29 +37,29 @@ class atsumi_Http { const REDIRECT_USE_PROXY = 305; const REDIRECT_SWITCH_PROXY = 306; const REDIRECT_TEMPORARY = 307; - - + + // 4xx Client Error - - const CLIENT_ERROR_BAD_REQUEST = 300; - const CLIENT_ERROR_UNAUTHORIZED = 301; - const CLIENT_ERROR_PAYMENT_REQUIRED = 302; - const CLIENT_ERROR_FORBIDDEN = 303; - const CLIENT_ERROR_NOT_FOUND = 304; - const CLIENT_ERROR_METHOD_NOT_ALLOWED = 305; - const CLIENT_ERROR_NOT_ACCEPTABLE = 306; - const CLIENT_ERROR_PROXY_AUTHENTICATION_REQUIRED = 307; - const CLIENT_ERROR_REQUEST_TIMEOUT = 300; - const CLIENT_ERROR_CONFLICT = 301; - const CLIENT_ERROR_GONE = 302; - const CLIENT_ERROR_LENGTH_REQUIRED = 303; - const CLIENT_ERROR_PRECONDITION_FAILED = 304; - const CLIENT_ERROR_REQUEST_ENTITY_TOO_LARGE = 305; - const CLIENT_ERROR_REQUEST_URI_TOO_LONG = 306; - const CLIENT_ERROR_UNSUPPORTED_MEDIA_TYPE = 307; + + const CLIENT_ERROR_BAD_REQUEST = 400; + const CLIENT_ERROR_UNAUTHORIZED = 401; + const CLIENT_ERROR_PAYMENT_REQUIRED = 402; + const CLIENT_ERROR_FORBIDDEN = 403; + const CLIENT_ERROR_NOT_FOUND = 404; + const CLIENT_ERROR_METHOD_NOT_ALLOWED = 405; + const CLIENT_ERROR_NOT_ACCEPTABLE = 406; + const CLIENT_ERROR_PROXY_AUTHENTICATION_REQUIRED = 407; + const CLIENT_ERROR_REQUEST_TIMEOUT = 400; + const CLIENT_ERROR_CONFLICT = 401; + const CLIENT_ERROR_GONE = 402; + const CLIENT_ERROR_LENGTH_REQUIRED = 403; + const CLIENT_ERROR_PRECONDITION_FAILED = 404; + const CLIENT_ERROR_REQUEST_ENTITY_TOO_LARGE = 405; + const CLIENT_ERROR_REQUEST_URI_TOO_LONG = 406; + const CLIENT_ERROR_UNSUPPORTED_MEDIA_TYPE = 407; //.. add the rest... - + } diff --git a/classes/validators/validate_MaxChars.php b/classes/validators/validate_MaxChars.php index 3d610e7..12f68cd 100644 --- a/classes/validators/validate_MaxChars.php +++ b/classes/validators/validate_MaxChars.php @@ -18,7 +18,7 @@ public function validate($data) { if(is_array($data))$data = $data[0]; $data = str_replace("\n", " ", str_replace("\r", "", $data)); - if(empty($data) || strlen($data) <= $this->maxChars) + if(empty($data) || mb_strlen($data) <= $this->maxChars) return true; else throw new Exception(sf("You must enter less than %s characters", @@ -28,4 +28,4 @@ public function validate($data) { } -?> +?> \ No newline at end of file diff --git a/classes/widgets/form/elements/widget_TextConfirmElement.php b/classes/widgets/form/elements/widget_TextConfirmElement.php index b4fa0e3..2da88e5 100644 --- a/classes/widgets/form/elements/widget_TextConfirmElement.php +++ b/classes/widgets/form/elements/widget_TextConfirmElement.php @@ -5,10 +5,15 @@ class widget_TextConfirmElement extends widget_AbstractElement { protected $htmlType = 'text'; protected $confirmText = "Please confirm"; protected $cssClassName = 'inputTextConfirm'; + protected $placeholder = ''; public function __construct($args) { if(array_key_exists("confirmText", $args)) $this->confirmText = $args['confirmText']; + + if (array_key_exists('placeholder', $args) && strlen($args['placeholder'])) + $this->placeholder = $args['placeholder']; + } function renderElement() { @@ -18,12 +23,13 @@ function renderElement() { if(!array_key_exists(0,$value)) $value[0] = ""; if(!array_key_exists(1,$value)) $value[1] = ""; - $out = sf("
", + $out = sf("
", $this->htmlType, $this->getName(), parent::makeInputSafe($value[0]), ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', - $this->getName(), $this->cssClassName, $this->cssClassName + $this->getName(), $this->cssClassName, $this->cssClassName, + strlen($this->placeholder)?sf(' placeholder="%s"', $this->placeholder):'' ); - $out .= sf("", + $out .= sf("", $this->getName(), $this->confirmText, ($this->getRequired() && !$this->getValidates()) ? $this->goAsterisks() : ""); diff --git a/classes/widgets/form/widget_Form.php b/classes/widgets/form/widget_Form.php index 860b357..dd5ebaa 100644 --- a/classes/widgets/form/widget_Form.php +++ b/classes/widgets/form/widget_Form.php @@ -17,6 +17,7 @@ class widget_Form { private $submitted = false; private $ancorJump = false; // will the form jump to the ancor? private $actionPath = ''; + private $cssClasses = array(); public function __construct($formName = null, $autoLoadFormData = true) { if(!is_null($formName)) $this->setName($formName); @@ -50,6 +51,10 @@ public function getTitle() { return $this->title; } + public function addCssClass ($className) { + $this->cssClasses[] = $className; + } + public function setFormDataFromMethod() { switch($this->method) { @@ -236,14 +241,15 @@ public function getElement($elementName) { public function getFormTop() { - $html = sf('
', - $this->name, - $this->name, - $this->name, - $this->method, - $this->ancorJump ? sf('%s#form_%s', $this->actionPath, $this->name) : $this->actionPath, - $this->encoding - ); + $html = sf('', + $this->name, + $this->name, + $this->name, + $this->method, + $this->ancorJump ? sf('%s#form_%s', $this->actionPath, $this->name) : $this->actionPath, + $this->encoding, + count($this->cssClasses)?' '.implode(' ', $this->cssClasses):'' + ); // add a hidden field to verify if this form has been posted $html .= sfl("", $this->name); From 53facae17c82e9e5490c1df872ea0502277b09b0 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Thu, 30 Aug 2012 14:43:52 +0100 Subject: [PATCH 054/164] Added atsumi_Interval class to the calendar utilities. Useful when doing interval stuff in postgresql. Updated postgresql casters (may need testing). --- .../adapter/postgresql/caster_PostgreSql.php | 11 +++ .../postgresql/caster_PostgreSqlToPhp.php | 10 +++ classes/utility/calendar/atsumi_Interval.php | 57 +++++++++++++ .../elements/widget_ImagePickerElement.php | 80 ------------------- 4 files changed, 78 insertions(+), 80 deletions(-) create mode 100644 classes/utility/calendar/atsumi_Interval.php delete mode 100644 classes/widgets/form/elements/widget_ImagePickerElement.php diff --git a/classes/database/adapter/postgresql/caster_PostgreSql.php b/classes/database/adapter/postgresql/caster_PostgreSql.php index ef88969..bc74cfc 100644 --- a/classes/database/adapter/postgresql/caster_PostgreSql.php +++ b/classes/database/adapter/postgresql/caster_PostgreSql.php @@ -33,6 +33,7 @@ class caster_PostgreSql extends caster_Abstract { 'C' => 'characterVarying', 'd' => 'date', 'f' => 'float', + 'g' => 'geometry', 'i' => 'integer', 'I' => 'integerOrNull', 'l' => 'literal', @@ -155,6 +156,16 @@ static function boolean($in) { return sf("'%s'::BOOLEAN", $in?'t':'f'); } + /** + * Casts a variable into a PostgreSQL geometry + * @param string $in String to be casted + * @return string Casted string + */ + static function geometry($in) { + if(!is_string($in)) throw new caster_StrictTypeException('Geom Expected String'); + return sf("'%s'::GEOMETRY", pg_escape_string($in)); + } + /** * Casts a variable into a PostgreSQL integer * @param int $in Int to be casted diff --git a/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php b/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php index d3584d9..78a5c38 100644 --- a/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php +++ b/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php @@ -30,6 +30,8 @@ class caster_PostgreSqlToPhp extends caster_Abstract { 'b' => 'boolean', 'd' => 'date', 'D' => 'dateOrNull', + 'g' => 'geometry', + 'G' => 'geometryOrNull', 'i' => 'integer', 'I' => 'integerOrNull', 'f' => 'float', @@ -198,6 +200,14 @@ static function timestampWithTimezoneOrNull($in) { return self::timestampWithTimezone($in); } + static function geometry($in) { + return atsumi_Interval::fromPostgresql(strval($in)); + } + static function geometryOrNull($in) { + if (is_null($in)) return null; + return self::geometry($in); + } + /* DEPRECATED METHODS */ } ?> \ No newline at end of file diff --git a/classes/utility/calendar/atsumi_Interval.php b/classes/utility/calendar/atsumi_Interval.php new file mode 100644 index 0000000..0478923 --- /dev/null +++ b/classes/utility/calendar/atsumi_Interval.php @@ -0,0 +1,57 @@ +newInstanceArgs($intervalMatches); + return $intervalInstance; + + } + + public function __construct ($seconds, $minutes = 0, $hours = 0, $days = 0, $years = 0) { + + // TODO: check date is valid + $this->seconds = floatval( + floatval($seconds) + + (intval($minutes) * self::DURATION_MINUTE) + + (intval($hours) * self::DURATION_HOUR) + + (intval($days) * self::DURATION_DAY) + + (intval($years) * self::DURATION_YEAR) + ); + } + + public function __toString() { + return strval($this->seconds); + } + +} +?> \ No newline at end of file diff --git a/classes/widgets/form/elements/widget_ImagePickerElement.php b/classes/widgets/form/elements/widget_ImagePickerElement.php deleted file mode 100644 index 04608ca..0000000 --- a/classes/widgets/form/elements/widget_ImagePickerElement.php +++ /dev/null @@ -1,80 +0,0 @@ -options = $args['options']; - if(array_key_exists('blank', $args)) - $this->blankMessage = $args['blank']; - - if(array_key_exists('width', $args)) - $this->width = $args['width']; - - if(array_key_exists('height', $args)) - $this->height = $args['height']; - } - function renderThumbnail ($value, $imageUrl, $selected) { - - // TODO: Would be nice to add option for multi select - // would convert to checkbox's rather than radio's - - return sfl('
- - -
', - $this->width, - $this->getName(), - $value, - $imageUrl, - $this->width, - $this->height, - $this->getName(), - $value, - $this->getName(), - $value, - $selected?'checked="checked"':''); - - } - function renderElement() { - $htmlOut = ""; - $elementValue = $this->getValue(); - - foreach($this->options as $value => $imageUrl) { - - $htmlOut .= $this->renderThumbnail( - $value, - $imageUrl, - strval($elementValue) == strval($value) - ); - - } - - return( sfl("
%s
", - $this->getName(), - ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', - $this->getName(), - $htmlOut - - ) - ); - } -} - -?> \ No newline at end of file From e5436aaf01daf1e72ea854b551a96035c795c2d3 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Thu, 30 Aug 2012 15:13:24 +0100 Subject: [PATCH 055/164] added a few useful methods to interval cass, fixed error in caster --- .../postgresql/caster_PostgreSqlToPhp.php | 17 ++++++++++---- classes/utility/calendar/atsumi_Interval.php | 22 ++++++++++++++++++- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php b/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php index 78a5c38..9ac9d89 100644 --- a/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php +++ b/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php @@ -39,7 +39,9 @@ class caster_PostgreSqlToPhp extends caster_Abstract { 's' => 'text', 'S' => 'textOrNull', 't' => 'timestampWithTimezone', - 'T' => 'timestampWithTimezoneOrNull' + 'T' => 'timestampWithTimezoneOrNull', + 'z' => 'interval', + 'Z' => 'intervalOrNull' ); /* CONSTRUCTOR & DESTRUCTOR */ @@ -157,6 +159,14 @@ static function boolean($in) { return $in; } + static function interval($in) { + return atsumi_Interval::fromPostgresql(strval($in)); + } + static function intervalOrNull($in) { + if (is_null($in)) return null; + return self::interval($in); + } + /** * Casts a variable into a PostgreSQL integer * @param int $in Int to be casted @@ -201,11 +211,10 @@ static function timestampWithTimezoneOrNull($in) { } static function geometry($in) { - return atsumi_Interval::fromPostgresql(strval($in)); + return self::text($in); } static function geometryOrNull($in) { - if (is_null($in)) return null; - return self::geometry($in); + return self::textOrNull($in); } /* DEPRECATED METHODS */ diff --git a/classes/utility/calendar/atsumi_Interval.php b/classes/utility/calendar/atsumi_Interval.php index 0478923..aa50172 100644 --- a/classes/utility/calendar/atsumi_Interval.php +++ b/classes/utility/calendar/atsumi_Interval.php @@ -50,7 +50,27 @@ public function __construct ($seconds, $minutes = 0, $hours = 0, $days = 0, $yea } public function __toString() { - return strval($this->seconds); + return strval($this->inSeconds()); + } + + public function inSeconds() { + return floatval($this->seconds); + } + + public function inMinutes() { + return floatval($this->seconds / self::DURATION_HOUR); + } + + public function inHours() { + return floatval($this->seconds / self::DURATION_MINUTE); + } + + public function inDays() { + return floatval($this->seconds / self::DURATION_DAY); + } + + public function inYears() { + return floatval($this->seconds / self::DURATION_YEAR); } } From f01c395f97c98316ebfc340d744f963f682f71f6 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Thu, 30 Aug 2012 15:15:38 +0100 Subject: [PATCH 056/164] added image picker element back in... oops --- .../elements/widget_ImagePickerElement.php | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 classes/widgets/form/elements/widget_ImagePickerElement.php diff --git a/classes/widgets/form/elements/widget_ImagePickerElement.php b/classes/widgets/form/elements/widget_ImagePickerElement.php new file mode 100644 index 0000000..04608ca --- /dev/null +++ b/classes/widgets/form/elements/widget_ImagePickerElement.php @@ -0,0 +1,80 @@ +options = $args['options']; + if(array_key_exists('blank', $args)) + $this->blankMessage = $args['blank']; + + if(array_key_exists('width', $args)) + $this->width = $args['width']; + + if(array_key_exists('height', $args)) + $this->height = $args['height']; + } + function renderThumbnail ($value, $imageUrl, $selected) { + + // TODO: Would be nice to add option for multi select + // would convert to checkbox's rather than radio's + + return sfl('
+ + +
', + $this->width, + $this->getName(), + $value, + $imageUrl, + $this->width, + $this->height, + $this->getName(), + $value, + $this->getName(), + $value, + $selected?'checked="checked"':''); + + } + function renderElement() { + $htmlOut = ""; + $elementValue = $this->getValue(); + + foreach($this->options as $value => $imageUrl) { + + $htmlOut .= $this->renderThumbnail( + $value, + $imageUrl, + strval($elementValue) == strval($value) + ); + + } + + return( sfl("
%s
", + $this->getName(), + ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', + $this->getName(), + $htmlOut + + ) + ); + } +} + +?> \ No newline at end of file From f5f0fe78cb759dcd9657e0818aad04da2e4224ff Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Wed, 12 Sep 2012 11:27:05 +0100 Subject: [PATCH 057/164] Recaptcha now shows as required and you customise the error message in the recaptcha validator. --- classes/validators/validate_Recaptcha.php | 6 ++++-- classes/widgets/form/elements/widget_RecaptchaElement.php | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/classes/validators/validate_Recaptcha.php b/classes/validators/validate_Recaptcha.php index ccbb3a2..c7288f1 100644 --- a/classes/validators/validate_Recaptcha.php +++ b/classes/validators/validate_Recaptcha.php @@ -1,8 +1,10 @@ privateKey = $privateKey; + $this->errorMsg = $errorMsg; } public function validate($data) { @@ -19,7 +21,7 @@ public function validate($data) { if($answer->is_valid) { return true; } - throw new Exception('Invalid CAPTCHA answer'); + throw new Exception($this->errorMsg); } /* diff --git a/classes/widgets/form/elements/widget_RecaptchaElement.php b/classes/widgets/form/elements/widget_RecaptchaElement.php index 4e2480c..5ce62b3 100644 --- a/classes/widgets/form/elements/widget_RecaptchaElement.php +++ b/classes/widgets/form/elements/widget_RecaptchaElement.php @@ -10,6 +10,9 @@ public function __construct($args) { } $this->publicKey = $args['apiKey']; } + function getRequired () { + return true; + } function renderElement() { $output = sf(' From 11a759cbb68ba1a1c250ac96947d237777e85abc Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Thu, 4 Oct 2012 10:28:02 +0100 Subject: [PATCH 058/164] Added a validation handler that makes validating outside of form system easier --- classes/validators/validation_Handler.php | 31 +++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 classes/validators/validation_Handler.php diff --git a/classes/validators/validation_Handler.php b/classes/validators/validation_Handler.php new file mode 100644 index 0000000..91a49e1 --- /dev/null +++ b/classes/validators/validation_Handler.php @@ -0,0 +1,31 @@ +validate($value); + } catch(Exception $e) { + $errors[] = $e->getMessage(); + $validates = false; + } + } + + return array ( + "validates" => $validates, + "errors" => $errors + ); + } + + + + +} + +?> \ No newline at end of file From 9be9cdc9c0fefe33329e7ce47c2d9a7b14ce8b5e Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Wed, 17 Oct 2012 13:25:45 +0100 Subject: [PATCH 059/164] when formatting an exception it prints the exceptions details member variable if available --- classes/core/error/parser/atsumi_ErrorParser.php | 12 ++++++++++++ classes/validators/validate_Recaptcha.php | 6 ++---- .../form/elements/widget_RecaptchaElement.php | 3 --- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/classes/core/error/parser/atsumi_ErrorParser.php b/classes/core/error/parser/atsumi_ErrorParser.php index 6b42268..be9a608 100644 --- a/classes/core/error/parser/atsumi_ErrorParser.php +++ b/classes/core/error/parser/atsumi_ErrorParser.php @@ -85,11 +85,18 @@ public static function parse(Exception $e, $contentType = self::HTML, $recoverer $out .= sfl('Recoverer action: %s', $recoverer->getActionDetails()); } + if (isset($e->details) && !is_null($e->details)) { + $out .= sfl("\n".'-Additional Detail'); + $out .= sfl('%s', pretty($e->details)); + } + + if($e instanceof atsumi_AbstractException) { $out .= sfl("\n".'-How to resolve this issue'); $out .= sfl($e->getInstructions('text/plain')); } + $out .= sfl("\n".'-Stack Trace'); $out .= sfl('%s', atsumi_ErrorParser::formatTrace($e->getTrace())); $out .= sfl('###### End of Exception '."\n\n"); @@ -118,6 +125,11 @@ public static function parse(Exception $e, $contentType = self::HTML, $recoverer $out .= sfl('

Recoverer action: %s

', $recoverer->getActionDetails()); } + if (isset($e->details) && !is_null($e->details)) { + $out .= sfl('

Additional Detail

'); + $out .= sfl('
%s
', pretty($e->details)); + } + if($e instanceof atsumi_AbstractException) { $out .= sfl('

ATSUMI: How to resolve this issue

'); $out .= sfl('
%s
', $e->getInstructions('text/html')); diff --git a/classes/validators/validate_Recaptcha.php b/classes/validators/validate_Recaptcha.php index c7288f1..ccbb3a2 100644 --- a/classes/validators/validate_Recaptcha.php +++ b/classes/validators/validate_Recaptcha.php @@ -1,10 +1,8 @@ privateKey = $privateKey; - $this->errorMsg = $errorMsg; } public function validate($data) { @@ -21,7 +19,7 @@ public function validate($data) { if($answer->is_valid) { return true; } - throw new Exception($this->errorMsg); + throw new Exception('Invalid CAPTCHA answer'); } /* diff --git a/classes/widgets/form/elements/widget_RecaptchaElement.php b/classes/widgets/form/elements/widget_RecaptchaElement.php index 5ce62b3..4e2480c 100644 --- a/classes/widgets/form/elements/widget_RecaptchaElement.php +++ b/classes/widgets/form/elements/widget_RecaptchaElement.php @@ -10,9 +10,6 @@ public function __construct($args) { } $this->publicKey = $args['apiKey']; } - function getRequired () { - return true; - } function renderElement() { $output = sf(' From d6af459c2760a53beba08a727ac2b62093db1a69 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Wed, 17 Oct 2012 13:38:34 +0100 Subject: [PATCH 060/164] added pre formatted tag in exception additional details HTML output --- classes/core/error/parser/atsumi_ErrorParser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/core/error/parser/atsumi_ErrorParser.php b/classes/core/error/parser/atsumi_ErrorParser.php index be9a608..9f74604 100644 --- a/classes/core/error/parser/atsumi_ErrorParser.php +++ b/classes/core/error/parser/atsumi_ErrorParser.php @@ -127,7 +127,7 @@ public static function parse(Exception $e, $contentType = self::HTML, $recoverer if (isset($e->details) && !is_null($e->details)) { $out .= sfl('

Additional Detail

'); - $out .= sfl('
%s
', pretty($e->details)); + $out .= sfl('
%s
', pretty($e->details)); } if($e instanceof atsumi_AbstractException) { From 935d59d1d5c058ae1226f9f60a60bad4acd14d78 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Thu, 14 Mar 2013 04:12:39 +0000 Subject: [PATCH 061/164] new simple template - suggested size of 4. couple of css class changes although not happy about the way this works. Might move to utilise a php template. --- .../widgets/pagination/widget_Paginate.php | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/classes/widgets/pagination/widget_Paginate.php b/classes/widgets/pagination/widget_Paginate.php index e846193..cac70fb 100644 --- a/classes/widgets/pagination/widget_Paginate.php +++ b/classes/widgets/pagination/widget_Paginate.php @@ -26,6 +26,7 @@ class widget_Paginate { /* format presets/templates */ const TEMPLATE_CLASSIC = 1; const TEMPLATE_ARROWS = 2; + const TEMPLATE_SIMPLE = 3; /* member variables used for pagination calculations */ private $recordCount; @@ -121,10 +122,15 @@ public function setFormatTemplate ($template = 1) { $this->formatPrevious = array('', ''); break; - case self::TEMPLATE_ARROWS: - $this->format = ''; - $this->formatNext = array('»', '»'); - $this->formatPrevious = array('«', '«'); + case self::TEMPLATE_ARROWS: + $this->format = ''; + $this->formatNext = array('»', '»'); + $this->formatPrevious = array('«', '«'); + break; + + case self::TEMPLATE_SIMPLE: + $this->format = ''; + $this->formatNext = array('Next', 'Next'); break; } @@ -198,11 +204,11 @@ private function setPageCountByResultsPerPage($in) { /* returns a html page link for inclusion in the pagination output */ public function renderPageLink ($page) { - return sf("%s", + return sf("%s", ($this->currentPage == $page) ? " currentPage" : '', - ($this->currentPage == $page) ? - number_format($page) : - sprintf("%s", $this->generateUrl($page),number_format($page)) + $this->generateUrl($page), + ($this->currentPage == $page) ? " on":'', + number_format($page) ); } @@ -218,9 +224,15 @@ public function render () { $start = $this->renderPageLink(1); $end = $this->getPageCount() < 2?'':$this->renderPageLink($this->getPageCount()); + // params: Pages links $pageLength = $this->navLength; - if ($pageLength&1) { } else $pageLength++; + if ($pageLength&1 || + (array_key_exists('[START]', $options) && !array_key_exists('[END]', $options)) || + (!array_key_exists('[START]', $options) && array_key_exists('[END]', $options)) + ) { } + + else $pageLength++; if (array_key_exists('[START]', $options)) $pageLength--; if (array_key_exists('[END]', $options)) $pageLength--; @@ -241,18 +253,19 @@ public function render () { if ($pageLinkStart < 1) $pageLinkStart = 1; if (array_key_exists('[START]', $options) && $pageLinkStart == 1) $pageLinkStart = 2; + $pages = ''; if ($pageLinkStart <= $pageLinkEnd) for ($i = $pageLinkStart; $i <= $pageLinkEnd; $i++) $pages .= $this->renderPageLink($i); // params: start ellipses - if (array_key_exists('[START_ELLIPSES]', $options) && array_key_exists('[START]', $options) && + if (array_key_exists('[START_ELLIPSES]', $options) && $pageLinkStart > 2) $startEllipses = '...'; else $startEllipses = ''; // params: end ellipses - if (array_key_exists('[END_ELLIPSES]', $options) && array_key_exists('[END]', $options) && + if (array_key_exists('[END_ELLIPSES]', $options) && $pageLinkEnd < ($this->pageCount-1)) $endEllipses = '...'; else $endEllipses = ''; From a8f38577e1f70eaf4ae4de20114c4cd3d19c1c9e Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Thu, 14 Mar 2013 04:14:35 +0000 Subject: [PATCH 062/164] added an $options param to renderElement so you can disable label from rendering. Not happy with the implementation of the form/elements classes, may refactor soon... --- .../form/elements/widget_AbstractElement.php | 13 ++++++++++--- .../widgets/form/elements/widget_HtmlElement.php | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/classes/widgets/form/elements/widget_AbstractElement.php b/classes/widgets/form/elements/widget_AbstractElement.php index a9e58a1..ca596a9 100644 --- a/classes/widgets/form/elements/widget_AbstractElement.php +++ b/classes/widgets/form/elements/widget_AbstractElement.php @@ -16,7 +16,8 @@ abstract class widget_AbstractElement { protected $cssStyle = null; protected $cssClass = null; - + + /** * @deprecated */ @@ -54,7 +55,8 @@ static public function makeInputSafe($in) { return $in; } - public function render() { + public function render($options = array()) { + $out = $this->preRender(); $out .= sfl('
', $this->style ? " " . $this->style : "", @@ -64,7 +66,12 @@ public function render() { $this->cssStyle ? " style='" . $this->cssStyle . "'": "" ); try { - $out .= sf('%s%s%s', $this->renderErrors(), ($this->label != '' || $this->getRequired() ? $this->renderLabel() : ''), $this->renderElement()); + $out .= sf('%s%s
%s
', + $this->renderErrors(), + (($this->label != '' || $this->getRequired()) && + (!array_key_exists('label', $options) || $options['label'] !== false) ? + $this->renderLabel() : ''), + $this->renderElement()); } catch (Exception $e) { // if in debug mode display exception details diff --git a/classes/widgets/form/elements/widget_HtmlElement.php b/classes/widgets/form/elements/widget_HtmlElement.php index c4d2870..6d05bf3 100644 --- a/classes/widgets/form/elements/widget_HtmlElement.php +++ b/classes/widgets/form/elements/widget_HtmlElement.php @@ -12,7 +12,7 @@ public function __construct($args) { function renderElement() { } - public function render() { + public function render($options = array()) { $out = $this->preRender(); $out .= sf("%s", $this->html); $out .= $this->postRender(); From ead9ac3fcac467d19cb7d5681473038127d534bd Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Thu, 14 Mar 2013 04:15:31 +0000 Subject: [PATCH 063/164] Added new php template handler, this view handler makes maintaniing UI on large projects much easier. --- .../mvc_PhpTemplateViewHandler.php | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 classes/mvc/view_handlers/mvc_PhpTemplateViewHandler.php diff --git a/classes/mvc/view_handlers/mvc_PhpTemplateViewHandler.php b/classes/mvc/view_handlers/mvc_PhpTemplateViewHandler.php new file mode 100644 index 0000000..63508ab --- /dev/null +++ b/classes/mvc/view_handlers/mvc_PhpTemplateViewHandler.php @@ -0,0 +1,100 @@ + file path + private $templateFileMap = array(); + + // optional main template, template that includes $pageContent + private $mainTemplate = false; + + // view/page data + private $viewData = array(); + + + public function __construct($mainTemplate = false, $templateFileMap = array()) { + + $this->templateFileMap = $templateFileMap; + $this->mainTemplate = $mainTemplate; + + } + + + /* + * Static: Processes a specific template file + */ + static public function processTemplateFile ($templateFile, $data) { + if (empty($templateFile)) return; + + extract($data, EXTR_SKIP); + ob_start(); + try { + @include($templateFile); + } catch (Exception $e) { + throw new mvc_ViewNotFoundException ( + "Can't find referenced template: ".$pageTemplate, + $pageTemplate + ); + } + return ob_get_clean(); + } + + /* + * static: Prints a specific template file + */ + static public function renderTemplateFile ($templateFile, $data) { + print self::processTemplate($templateFile, $data); + } + + + /* + * Processes a template-ref/template-file + * optionally includes view data + */ + public function processTemplate ($template, $data = array(), $incViewData = false) { + + if (array_key_exists($template, $this->templateFileMap)) + $template = $this->templateFileMap[$template]; + + if ($incViewData) + $data = array_merge($data, $this->viewData); + + return self::processTemplateFile($template, $data); + } + + + /* + * prints a template-ref/template-file + * optionally includes view data + */ + public function renderTemplate ($template, $data = array(), $incViewData = false) { + print $this->processTemplate($template, $data, $incViewData); + } + + + + public function render($viewTemplate, $viewData) { + + $this->viewData = $viewData; + + // process the view tempalte + $pageContent = $this->processTemplate( + $viewTemplate, + array(), + true + ); + + // if there is a main template - process & render it + if ($this->mainTemplate) + $this->renderTemplate( + $this->mainTemplate, + array("pageContent"=>$pageContent), + true + ); + + // if not - render view template + else print $pageContent; + } +} + +?> \ No newline at end of file From 438745fbeb9812998a80c28bc3d9a44545668905 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 26 Mar 2013 16:21:24 +0000 Subject: [PATCH 064/164] minor changes --- classes/widgets/form/widget_Form.php | 11 +++++++---- classes/widgets/pagination/widget_Paginate.php | 10 +++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/classes/widgets/form/widget_Form.php b/classes/widgets/form/widget_Form.php index dd5ebaa..97954e9 100644 --- a/classes/widgets/form/widget_Form.php +++ b/classes/widgets/form/widget_Form.php @@ -235,8 +235,11 @@ public function render() { return $html; } - public function getElement($elementName) { - return $this->elementMap[$elementName]->render(); + public function hasElement($elementName) { + return array_key_exists($elementName, $this->elementMap); + } + public function getElement($elementName, $options = array()) { + return $this->elementMap[$elementName]->render($options); } public function getFormTop() { @@ -263,8 +266,8 @@ public function getFormTop() { public function getFormBottom() { // add the submit to the bottom of the form for now(will convert to element in next version) - $html = sfl('
'); - $html .= sfl(' ', $this->name, $this->getSubmit()); + $html = sfl('
'); + $html .= sfl(' ', $this->name, $this->getSubmit()); $html .= sfl('
'); $html .= sfl(''); diff --git a/classes/widgets/pagination/widget_Paginate.php b/classes/widgets/pagination/widget_Paginate.php index cac70fb..f1e7c4e 100644 --- a/classes/widgets/pagination/widget_Paginate.php +++ b/classes/widgets/pagination/widget_Paginate.php @@ -33,7 +33,7 @@ class widget_Paginate { private $pageCount; private $resultsPerPage = 10; private $currentPage = 0; - private $navLength = 7; + private $navLength = 4; private $url; /* formatting options */ @@ -109,8 +109,8 @@ public function getResultsPerPage() { } - /* set the format template (default is classic) */ - public function setFormatTemplate ($template = 1) { + /* set the format template (default is simple) */ + public function setFormatTemplate ($template = 3) { switch ($template) { default: @@ -130,7 +130,7 @@ public function setFormatTemplate ($template = 1) { case self::TEMPLATE_SIMPLE: $this->format = ''; - $this->formatNext = array('Next', 'Next'); + $this->formatNext = array('Next', ''); break; } @@ -270,7 +270,7 @@ public function render () { else $endEllipses = ''; // params: next link - if ($this->currentPage == $this->pageCount) $next = $this->formatNext[1]; + if ($this->currentPage >= $this->pageCount) $next = $this->formatNext[1]; else $next = str_replace('[HREF]', $this->generateUrl($this->currentPage+1), $this->formatNext[0]); // params: previous link From 314f315c94c818deb6d6258b189de09641a0443b Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Wed, 27 Mar 2013 16:53:42 +0000 Subject: [PATCH 065/164] can pass options array to getFormBottom() so can add styles to rows/submit button --- classes/widgets/form/widget_Form.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/classes/widgets/form/widget_Form.php b/classes/widgets/form/widget_Form.php index 97954e9..228eafc 100644 --- a/classes/widgets/form/widget_Form.php +++ b/classes/widgets/form/widget_Form.php @@ -263,11 +263,13 @@ public function getFormTop() { return $html; } - public function getFormBottom() { + public function getFormBottom($options = array()) { // add the submit to the bottom of the form for now(will convert to element in next version) - $html = sfl('
'); - $html .= sfl(' ', $this->name, $this->getSubmit()); + $html = sfl('
', array_key_exists('rowClasses',$options)?' '.$options['rowClasses']:''); + $html .= sfl(' ', + array_key_exists('buttonClasses',$options)?' '.$options['buttonClasses']:'', + $this->name, $this->getSubmit()); $html .= sfl('
'); $html .= sfl(''); From e7a80d5cfe2031a37909cef204a3dccb0f940d95 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Sun, 31 Mar 2013 18:53:02 +0100 Subject: [PATCH 066/164] added Atsumi::error__recover($e); --- classes/core/Atsumi.php | 7 ++++++- classes/core/error/atsumi_ErrorHandler.php | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/classes/core/Atsumi.php b/classes/core/Atsumi.php index 06fea57..20b7336 100644 --- a/classes/core/Atsumi.php +++ b/classes/core/Atsumi.php @@ -218,12 +218,17 @@ public static function error__setFloodControl(cache_HandlerInterface $cacheManag $args = func_get_args(); return self::__callStatic(__FUNCTION__, $args); } - + public static function error__listen(Exception $e) { $args = func_get_args(); return self::__callStatic(__FUNCTION__, $args); } + public static function error__recover(Exception $e) { + $args = func_get_args(); + return self::__callStatic(__FUNCTION__, $args); + } + } diff --git a/classes/core/error/atsumi_ErrorHandler.php b/classes/core/error/atsumi_ErrorHandler.php index a73b25e..bfdbf43 100644 --- a/classes/core/error/atsumi_ErrorHandler.php +++ b/classes/core/error/atsumi_ErrorHandler.php @@ -230,5 +230,8 @@ protected function fireEvent($eventType, atsumi_EventArgs $args = null) { public function listen ($e) { $this->handleException($e, false); } + public function recover ($e) { + $this->recoverer->recover($e); + } } ?> \ No newline at end of file From 4b1257dcf9d7369f7cf52c7627fe8784fef2a1e9 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Sun, 31 Mar 2013 18:53:46 +0100 Subject: [PATCH 067/164] Added $supressErrors param to toggle errors --- .../mvc_PhpTemplateViewHandler.php | 37 ++++++++++++++----- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/classes/mvc/view_handlers/mvc_PhpTemplateViewHandler.php b/classes/mvc/view_handlers/mvc_PhpTemplateViewHandler.php index 63508ab..47fa8e7 100644 --- a/classes/mvc/view_handlers/mvc_PhpTemplateViewHandler.php +++ b/classes/mvc/view_handlers/mvc_PhpTemplateViewHandler.php @@ -10,31 +10,48 @@ class mvc_PhpTemplateViewHandler implements mvc_ViewHandlerInterface { // view/page data private $viewData = array(); + + private $surpressErrors = true; - public function __construct($mainTemplate = false, $templateFileMap = array()) { + public function __construct($mainTemplate = false, $templateFileMap = array(), $surpressErrors = true) { $this->templateFileMap = $templateFileMap; $this->mainTemplate = $mainTemplate; - + $this->surpressErrors = $surpressErrors; } /* * Static: Processes a specific template file */ - static public function processTemplateFile ($templateFile, $data) { + static public function processTemplateFile ($templateFile, $data, $supressErrors = false) { if (empty($templateFile)) return; extract($data, EXTR_SKIP); ob_start(); try { - @include($templateFile); + + if ($supressErrors) + @include($templateFile); + else + include($templateFile); + } catch (Exception $e) { + + Atsumi::error__listen($e); + + if (!$supressErrors) + throw $e; + else + Atsumi::error__recover($e); + + + /* throw new mvc_ViewNotFoundException ( - "Can't find referenced template: ".$pageTemplate, - $pageTemplate - ); + "Can't find referenced template: ".$templateFile, + $templateFile + ); */ } return ob_get_clean(); } @@ -42,8 +59,8 @@ static public function processTemplateFile ($templateFile, $data) { /* * static: Prints a specific template file */ - static public function renderTemplateFile ($templateFile, $data) { - print self::processTemplate($templateFile, $data); + static public function renderTemplateFile ($templateFile, $data, $supressErrors = false) { + print self::processTemplate($templateFile, $data, $supressErrors); } @@ -59,7 +76,7 @@ public function processTemplate ($template, $data = array(), $incViewData = fals if ($incViewData) $data = array_merge($data, $this->viewData); - return self::processTemplateFile($template, $data); + return self::processTemplateFile($template, $data, $this->surpressErrors); } From e724413efdf029d8178c31087879d08142b7d86e Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Sun, 31 Mar 2013 18:54:21 +0100 Subject: [PATCH 068/164] added getPage() method to pagination --- classes/widgets/pagination/widget_Paginate.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/classes/widgets/pagination/widget_Paginate.php b/classes/widgets/pagination/widget_Paginate.php index f1e7c4e..e523a23 100644 --- a/classes/widgets/pagination/widget_Paginate.php +++ b/classes/widgets/pagination/widget_Paginate.php @@ -76,6 +76,9 @@ public function generateUrl($page) { public function getPageCount() { return $this->pageCount; } + public function getPage() { + return $this->currentPage; + } /* returns total page count */ public function getTotalResults() { From 33a690791b3e178232319a6c876e11e9f9afd542 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 15 Apr 2013 16:21:09 +0100 Subject: [PATCH 069/164] added a bit of extra functionality to PHP view handlder, can override the maintemplate if required when calling render() or process() on the view handler object --- .../mvc_PhpTemplateViewHandler.php | 37 +++++++++++++++---- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/classes/mvc/view_handlers/mvc_PhpTemplateViewHandler.php b/classes/mvc/view_handlers/mvc_PhpTemplateViewHandler.php index 47fa8e7..f42a3e2 100644 --- a/classes/mvc/view_handlers/mvc_PhpTemplateViewHandler.php +++ b/classes/mvc/view_handlers/mvc_PhpTemplateViewHandler.php @@ -13,7 +13,7 @@ class mvc_PhpTemplateViewHandler implements mvc_ViewHandlerInterface { private $surpressErrors = true; - + public function __construct($mainTemplate = false, $templateFileMap = array(), $surpressErrors = true) { $this->templateFileMap = $templateFileMap; @@ -21,7 +21,6 @@ public function __construct($mainTemplate = false, $templateFileMap = array(), $ $this->surpressErrors = $surpressErrors; } - /* * Static: Processes a specific template file */ @@ -89,10 +88,16 @@ public function renderTemplate ($template, $data = array(), $incViewData = false } - - public function render($viewTemplate, $viewData) { + /* + * returns processed template + * optionally uses main template + */ + public function process ($viewTemplate, $viewData, $mainTemplateOverride = null) { $this->viewData = $viewData; + $mainTemplate = is_null($mainTemplateOverride)? + $this->mainTemplate: + $mainTemplateOverride; // process the view tempalte $pageContent = $this->processTemplate( @@ -102,15 +107,31 @@ public function render($viewTemplate, $viewData) { ); // if there is a main template - process & render it - if ($this->mainTemplate) - $this->renderTemplate( - $this->mainTemplate, + if ($mainTemplate) + return $this->processTemplate( + $mainTemplate, array("pageContent"=>$pageContent), true ); // if not - render view template - else print $pageContent; + else return $pageContent; + } + + /* + * prints a processed + * optionally uses main template + */ + public function render ($viewTemplate, $viewData, $mainTemplateOverride = null) { + print $this->process($viewTemplate, $viewData, $mainTemplateOverride); + } + + + /* + * set the main template + */ + public function setMainTemplate ($in) { + $this->mainTemplate = $in; } } From 3fa64424d6875e1a14781a6f8eb96ff235abfc74 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 15 Apr 2013 16:21:50 +0100 Subject: [PATCH 070/164] fix for DBAL, bug resolved by jonny. --- classes/database/db_AbstractDatabase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/database/db_AbstractDatabase.php b/classes/database/db_AbstractDatabase.php index ffe36ef..53e4387 100644 --- a/classes/database/db_AbstractDatabase.php +++ b/classes/database/db_AbstractDatabase.php @@ -389,7 +389,7 @@ public function parseInsertQuery($args) { $data = array(); foreach($rows as $row) { - $rowParts = explode('=', $row); + $rowParts = preg_split('/=/', $row, 2); $column[] = trim($rowParts[0]); $data[] = trim($rowParts[1]); } From 65475ee434a211863bc714de1044950dd298da60 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 16 Apr 2013 11:00:22 +0100 Subject: [PATCH 071/164] fix pass by reference changes for php 5.4 --- classes/core/error/atsumi_ErrorHandler.php | 4 ++-- classes/database/db_AbstractDatabase.php | 8 ++++---- classes/mvc/models/mvc_AbstractEntity.php | 2 +- classes/widgets/form/widget_Form.php | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/classes/core/error/atsumi_ErrorHandler.php b/classes/core/error/atsumi_ErrorHandler.php index bfdbf43..8712bb5 100644 --- a/classes/core/error/atsumi_ErrorHandler.php +++ b/classes/core/error/atsumi_ErrorHandler.php @@ -187,10 +187,10 @@ public function handleException(Exception $e, $recover = true) { try { // fires the exception_fc event if not blocked by flood control if(!$this->blockedByFloodControl(get_class($e), $e->getLine(), $e->getFile())) - $this->fireEvent(self::EVENT_EXCEPTION_FC, new atsumi_ErrorEventArgs($e, &$this->recoverer)); + $this->fireEvent(self::EVENT_EXCEPTION_FC, new atsumi_ErrorEventArgs($e, $this->recoverer)); // fire the exception event regardless of flood control - $this->fireEvent(self::EVENT_EXCEPTION, new atsumi_ErrorEventArgs($e, &$this->recoverer)); + $this->fireEvent(self::EVENT_EXCEPTION, new atsumi_ErrorEventArgs($e, $this->recoverer)); if ($recover) $this->recoverer->recover($e); diff --git a/classes/database/db_AbstractDatabase.php b/classes/database/db_AbstractDatabase.php index 53e4387..f7d2df7 100644 --- a/classes/database/db_AbstractDatabase.php +++ b/classes/database/db_AbstractDatabase.php @@ -195,7 +195,7 @@ public function formatResult ($rows) { */ public function query($query, $args = null, $_ = null) { $args = func_get_args(); - return $this->formatResult($this->queryReal(call_user_func_array(array(&$this, 'cast'), $args))); + return $this->formatResult($this->queryReal(call_user_func_array(array($this, 'cast'), $args))); } /** @@ -261,11 +261,11 @@ public function queryReal($sql) { * */ public function select($args) { $args = func_get_args(); - return call_user_func_array(array(&$this, 'query'), $args); + return call_user_func_array(array($this, 'query'), $args); } public function selectOne($args) { $args = func_get_args(); - $result = call_user_func_array(array(&$this, 'select'), $args); + $result = call_user_func_array(array($this, 'select'), $args); if(count($result) > 1) throw new db_UnexpectedResultException('selectOne returned more than one result'); @@ -290,7 +290,7 @@ public function fetch ($cols, $table, $where = null, $orderBy = null, $offset = } public function fetchOne($cols, $table, $where = null) { $args = func_get_args(); - $result = call_user_func_array(array(&$this, 'fetch'), $args); + $result = call_user_func_array(array($this, 'fetch'), $args); if(count($result) > 1) throw new db_UnexpectedResultException('fetchOne returned more than one result'); diff --git a/classes/mvc/models/mvc_AbstractEntity.php b/classes/mvc/models/mvc_AbstractEntity.php index 2055742..68cd049 100644 --- a/classes/mvc/models/mvc_AbstractEntity.php +++ b/classes/mvc/models/mvc_AbstractEntity.php @@ -207,7 +207,7 @@ public function remove() { * prevents changes made being writen to the database */ public function clearChanges() { - foreach($this->vars as &$value) { + foreach($this->vars as $value) { $value['changed'] = false; } } diff --git a/classes/widgets/form/widget_Form.php b/classes/widgets/form/widget_Form.php index 228eafc..63d7ece 100644 --- a/classes/widgets/form/widget_Form.php +++ b/classes/widgets/form/widget_Form.php @@ -130,7 +130,7 @@ public function add($elementClass, $args) { // save the new element to the form... $this->elements[] = $element; - $this->elementMap[$element->getName()] = &$element; + $this->elementMap[$element->getName()] = $element; } public function remove($elementName) { From 0731801073e0a795fefeb07770eb546eb0cc1254 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 16 Apr 2013 11:32:33 +0100 Subject: [PATCH 072/164] restored essential reference functionality --- classes/mvc/models/mvc_AbstractEntity.php | 2 +- classes/widgets/form/widget_Form.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/classes/mvc/models/mvc_AbstractEntity.php b/classes/mvc/models/mvc_AbstractEntity.php index 68cd049..2055742 100644 --- a/classes/mvc/models/mvc_AbstractEntity.php +++ b/classes/mvc/models/mvc_AbstractEntity.php @@ -207,7 +207,7 @@ public function remove() { * prevents changes made being writen to the database */ public function clearChanges() { - foreach($this->vars as $value) { + foreach($this->vars as &$value) { $value['changed'] = false; } } diff --git a/classes/widgets/form/widget_Form.php b/classes/widgets/form/widget_Form.php index 63d7ece..228eafc 100644 --- a/classes/widgets/form/widget_Form.php +++ b/classes/widgets/form/widget_Form.php @@ -130,7 +130,7 @@ public function add($elementClass, $args) { // save the new element to the form... $this->elements[] = $element; - $this->elementMap[$element->getName()] = $element; + $this->elementMap[$element->getName()] = &$element; } public function remove($elementName) { From 0e8088b37437c60f9488a2ca8ad02cc02f662cec Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Sun, 21 Apr 2013 16:58:03 +0100 Subject: [PATCH 073/164] App Handler returns NotFound exception if cant parse URI --- classes/core/app/atsumi_AppHandler.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/classes/core/app/atsumi_AppHandler.php b/classes/core/app/atsumi_AppHandler.php index d540145..e9cbf90 100644 --- a/classes/core/app/atsumi_AppHandler.php +++ b/classes/core/app/atsumi_AppHandler.php @@ -281,11 +281,12 @@ public function parseCommand() { * @access public */ public function process() { + // Could possibly be a fragment of the spec if(!is_string($this->parserData['controller'])) - throw new Exception('Path parsing error, please report to developement team'); + throw new app_PageNotFoundException('Path parsing error, please report to developement team'); if(!class_exists($this->parserData['controller'])) - throw new Exception('Could not find required controller: '.$this->parserData['controller']); + throw new app_PageNotFoundException('Could not find required controller: '.$this->parserData['controller']); $classname = $this->parserData['controller']; $this->controller = new $classname($this->settings, $this->errorHandler); From 2b3f7c78a1c1d631932414a370704d12301c8005 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Sat, 27 Apr 2013 12:18:52 +0100 Subject: [PATCH 074/164] set default encoding to UTF8 --- classes/validators/validate_MaxChars.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/classes/validators/validate_MaxChars.php b/classes/validators/validate_MaxChars.php index 12f68cd..3a380ae 100644 --- a/classes/validators/validate_MaxChars.php +++ b/classes/validators/validate_MaxChars.php @@ -9,16 +9,20 @@ class validate_MaxChars extends validate_AbstractValidator { private $maxChars = 0; + private $encoding = 'UTF-8'; - public function __construct($max) { + public function __construct($max, $encoding = null) { $this->maxChars = $max; + + if (!is_null($encoding)) + $this->encoding = $encoding; } public function validate($data) { if(is_array($data))$data = $data[0]; $data = str_replace("\n", " ", str_replace("\r", "", $data)); - if(empty($data) || mb_strlen($data) <= $this->maxChars) + if(empty($data) || mb_strlen($data, $encoding) <= $this->maxChars) return true; else throw new Exception(sf("You must enter less than %s characters", From 7fdfd07bf2a3f58ed56f6c16c1a214f227efe17a Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Sat, 27 Apr 2013 12:19:39 +0100 Subject: [PATCH 075/164] added support for cloudfront & proxy IPs --- classes/validators/validate_Recaptcha.php | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/classes/validators/validate_Recaptcha.php b/classes/validators/validate_Recaptcha.php index ccbb3a2..8374b37 100644 --- a/classes/validators/validate_Recaptcha.php +++ b/classes/validators/validate_Recaptcha.php @@ -4,7 +4,23 @@ class validate_Recaptcha extends validate_AbstractValidator { public function __construct($privateKey) { $this->privateKey = $privateKey; } - + public function getUserIp () { + + // cloudflare + if (array_key_exists('HTTP_CF_CONNECTING_IP', $_SERVER)) + return $_SERVER['HTTP_CF_CONNECTING_IP']; + + // proxy + elseif (array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)) + return $_SERVER['HTTP_X_FORWARDED_FOR']; + + // direct IP + elseif (array_key_exists('REMOTE_ADDR', $_SERVER)) + return $_SERVER['REMOTE_ADDR']; + + else return false; + + } public function validate($data) { // We don't care about the data variable, check for... if(!isset($_POST['recaptcha_challenge_field']) || !isset($_POST['recaptcha_response_field'])) { @@ -12,7 +28,7 @@ public function validate($data) { } $answer = $this->recaptcha_check_answer($this->privateKey, - $_SERVER["REMOTE_ADDR"], + $this->getUserIp(), $_POST["recaptcha_challenge_field"], $_POST["recaptcha_response_field"] ); From e5806e859985bd48697aedd3eaba77a0ab44e7cf Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Sun, 28 Apr 2013 16:57:53 +0100 Subject: [PATCH 076/164] fixed character encoding issue --- classes/validators/validate_MaxChars.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/validators/validate_MaxChars.php b/classes/validators/validate_MaxChars.php index 3a380ae..7ab1627 100644 --- a/classes/validators/validate_MaxChars.php +++ b/classes/validators/validate_MaxChars.php @@ -22,7 +22,7 @@ public function validate($data) { if(is_array($data))$data = $data[0]; $data = str_replace("\n", " ", str_replace("\r", "", $data)); - if(empty($data) || mb_strlen($data, $encoding) <= $this->maxChars) + if(empty($data) || mb_strlen($data, $this->encoding) <= $this->maxChars) return true; else throw new Exception(sf("You must enter less than %s characters", From 37a7149cbc35bc723aaa5f7d1f86206d152ca27f Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Wed, 1 May 2013 13:52:44 +0100 Subject: [PATCH 077/164] sitemap handler now trys to create the sitemap directories if they dont already exist --- classes/sitemap/exceptions/sitemap_Exception.php | 1 + classes/sitemap/exceptions/sitemap_WriteException.php | 3 +++ classes/sitemap/sitemap_Handler.php | 4 ++++ 3 files changed, 8 insertions(+) create mode 100644 classes/sitemap/exceptions/sitemap_Exception.php create mode 100644 classes/sitemap/exceptions/sitemap_WriteException.php diff --git a/classes/sitemap/exceptions/sitemap_Exception.php b/classes/sitemap/exceptions/sitemap_Exception.php new file mode 100644 index 0000000..c70c55b --- /dev/null +++ b/classes/sitemap/exceptions/sitemap_Exception.php @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/classes/sitemap/exceptions/sitemap_WriteException.php b/classes/sitemap/exceptions/sitemap_WriteException.php new file mode 100644 index 0000000..b249cf3 --- /dev/null +++ b/classes/sitemap/exceptions/sitemap_WriteException.php @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/classes/sitemap/sitemap_Handler.php b/classes/sitemap/sitemap_Handler.php index 5bba858..d28510c 100644 --- a/classes/sitemap/sitemap_Handler.php +++ b/classes/sitemap/sitemap_Handler.php @@ -63,6 +63,10 @@ static public function getUrlCount(&$db, $host, $tablename = 'sitemap') { /* writes sitemap files to disk */ static public function writeXml(&$db, $host, $xmlFilePath, $xmlUrlRoot, $maxUrlsPerSitemap = null, $compress = true, $tablename = 'sitemap') { + if (!is_dir($xmlFilePath) && !mkdir($xmlFilePath, 0777, true)) { + throw new sitemap_WriteException("Failed to create neccessary folders to save XML site map: ".$xmlFilePath); + } + /* clean up old sitemap files */ foreach(glob($xmlFilePath."sitemap*.xml*") as $filename) unlink($filename); From 67a64359a9df5ab42c7e5e0a6829571abea1ce2e Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Wed, 1 May 2013 13:53:58 +0100 Subject: [PATCH 078/164] now can optionally send tempalte strings - quite dangerous as uses eval but its there for fringe cases --- .../mvc_PhpTemplateViewHandler.php | 82 +++++++++++++++++-- 1 file changed, 76 insertions(+), 6 deletions(-) diff --git a/classes/mvc/view_handlers/mvc_PhpTemplateViewHandler.php b/classes/mvc/view_handlers/mvc_PhpTemplateViewHandler.php index f42a3e2..cf354b0 100644 --- a/classes/mvc/view_handlers/mvc_PhpTemplateViewHandler.php +++ b/classes/mvc/view_handlers/mvc_PhpTemplateViewHandler.php @@ -13,6 +13,9 @@ class mvc_PhpTemplateViewHandler implements mvc_ViewHandlerInterface { private $surpressErrors = true; + const TEMPLATE_TYPE_FILE = 1; + const TEMPLATE_TYPE_STRING = 2; + public function __construct($mainTemplate = false, $templateFileMap = array(), $surpressErrors = true) { @@ -20,7 +23,8 @@ public function __construct($mainTemplate = false, $templateFileMap = array(), $ $this->mainTemplate = $mainTemplate; $this->surpressErrors = $surpressErrors; } - + + /* * Static: Processes a specific template file */ @@ -54,7 +58,31 @@ static public function processTemplateFile ($templateFile, $data, $supressErrors } return ob_get_clean(); } + + /* + * Static: Processes a specific template string + * WARNING: You could create a PHP vulnerability by using this + */ + static public function processTemplateString ($templateString, $data, $supressErrors = false) { + if (empty($templateString) || $templateString == '' || !$templateString) return; + + extract($data, EXTR_SKIP); + ob_start(); + try { + + eval("?>".$templateString.'templateFileMap)) - $template = $this->templateFileMap[$template]; + $templateFile = false; + $templateString = false; - if ($incViewData) + if (is_array($template) && isset($template['TYPE']) && isset($template['TEMPLATE'])) { + switch ($template['TYPE']) { + + case self::TEMPLATE_TYPE_FILE: + $templateFile = $template['TEMPLATE']; + break; + + case self::TEMPLATE_TYPE_STRING: + $templateString = $template['TEMPLATE']; + break; + + } + + } elseif (is_string($template)) $templateFile = $template; + + if ($incViewData) $data = array_merge($data, $this->viewData); + + if ($templateFile) { + + if (array_key_exists($templateFile, $this->templateFileMap)) + $templateFile = $this->templateFileMap[$templateFile]; + + + return self::processTemplateFile($templateFile, $data, $this->surpressErrors); + + } else if ($templateString) { - return self::processTemplateFile($template, $data, $this->surpressErrors); + return self::processTemplateString($templateString, $data, $this->surpressErrors); + + } else { + throw new Exception ('Unknown template type'); + } } @@ -91,6 +155,7 @@ public function renderTemplate ($template, $data = array(), $incViewData = false /* * returns processed template * optionally uses main template + * $viewTemplate can be type string (file path) or array */ public function process ($viewTemplate, $viewData, $mainTemplateOverride = null) { @@ -105,7 +170,6 @@ public function process ($viewTemplate, $viewData, $mainTemplateOverride = null array(), true ); - // if there is a main template - process & render it if ($mainTemplate) return $this->processTemplate( @@ -133,6 +197,12 @@ public function render ($viewTemplate, $viewData, $mainTemplateOverride = null) public function setMainTemplate ($in) { $this->mainTemplate = $in; } + + + + + + } ?> \ No newline at end of file From 399fa25fc9b3069e762ea6cc21bb7ab84b373898 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 13 May 2013 17:34:31 +0100 Subject: [PATCH 079/164] added numeric type --- .../adapter/postgresql/caster_PostgreSql.php | 10 ++++++++++ .../postgresql/caster_PostgreSqlToPhp.php | 19 ++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/classes/database/adapter/postgresql/caster_PostgreSql.php b/classes/database/adapter/postgresql/caster_PostgreSql.php index bc74cfc..cf00833 100644 --- a/classes/database/adapter/postgresql/caster_PostgreSql.php +++ b/classes/database/adapter/postgresql/caster_PostgreSql.php @@ -38,6 +38,7 @@ class caster_PostgreSql extends caster_Abstract { 'I' => 'integerOrNull', 'l' => 'literal', 'n' => 'numeric', + 'N' => 'numericOrNull', 'q' => 'fullTextQuery', 's' => 'text', 'S' => 'textOrNull', @@ -192,6 +193,15 @@ static function integerOrNull($in) { static function numeric($in) { return sf("%s::NUMERIC", $in); } + /** + * Casts a variable into a PostgreSQL numeric or Null + * @param int $in Mixed to be casted + * @return string Casted string + */ + static function numericOrNull($in) { + if (is_null($in)) return 'NULL'; + return sf("%s::NUMERIC", $in); + } /** * Casts a variable into a PostgreSQL binary diff --git a/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php b/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php index 9ac9d89..a3aa075 100644 --- a/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php +++ b/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php @@ -34,6 +34,8 @@ class caster_PostgreSqlToPhp extends caster_Abstract { 'G' => 'geometryOrNull', 'i' => 'integer', 'I' => 'integerOrNull', + 'n' => 'numeric', + 'N' => 'numericOrNull', 'f' => 'float', 'F' => 'floatOrNull', 's' => 'text', @@ -155,6 +157,9 @@ static function sqlArrayOrNull($in) { * @return string Casted string */ static function boolean($in) { + + if (is_string($in) && $in == 'f') $in = false; + if (is_string($in) && $in == 't') $in = true; if (!is_bool($in)) throw new caster_StrictTypeException('Expected Boolean, received: '.$in.' ('.gettype($in).')'); return $in; } @@ -173,7 +178,8 @@ static function intervalOrNull($in) { * @return string Casted string */ static function integer($in) { - if (!is_int(intval($in))) throw new caster_StrictTypeException('Expected Integer, received: '.$in.' ('.gettype($in).')'); + if (is_string($in)) $in = intval($in); + if (!is_int($in)) throw new caster_StrictTypeException('Expected Integer, received: '.$in.' ('.gettype($in).')'); return intval($in); } static function integerOrNull($in) { @@ -182,6 +188,17 @@ static function integerOrNull($in) { return intval($in); } + static function numeric($in) { + if (!is_numeric($in)) throw new caster_StrictTypeException('Expected Numeric, received: '.$in.' ('.gettype($in).')'); + return $in; + } + static function numericOrNull($in) { + if (is_null($in)) return null; + if (!is_numeric($in)) throw new caster_StrictTypeException('Expected Numeric, received: '.$in.' ('.gettype($in).')'); + return $in; + } + + static function float($in) { if (!is_numeric($in)) throw new caster_StrictTypeException('Expected Float, received: '.$in.' ('.gettype($in).')'); setType($in, 'float'); From e41a0e6d053bac7189073f542cea40916ec7c060 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 13 May 2013 17:35:38 +0100 Subject: [PATCH 080/164] added ->valueOrNull_$elementName method- it returns null if no value was entered to the element --- classes/widgets/form/widget_Form.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/classes/widgets/form/widget_Form.php b/classes/widgets/form/widget_Form.php index 228eafc..2db8962 100644 --- a/classes/widgets/form/widget_Form.php +++ b/classes/widgets/form/widget_Form.php @@ -27,7 +27,9 @@ public function __construct($formName = null, $autoLoadFormData = true) { public function __get($name) { $matches = null; if(preg_match('/^value_(.+)$/', $name, $matches)) { - return call_user_func(array($this, 'value'), $matches [1]); + return call_user_func(array($this, 'value'), $matches [1]); + } elseif(preg_match('/^valueOrNull_(.+)$/', $name, $matches)) { + return call_user_func(array($this, 'valueOrNull'), $matches [1]); } elseif (preg_match('/^element_(.+)$/', $name, $matches)) { return call_user_func(array($this, 'element'), $matches [1]); } else throw new Exception('Undefined method:' . $name); @@ -154,10 +156,16 @@ public function remove($elementName) { public function value($elementName) { if(!array_key_exists($elementName, $this->elementMap)) throw new Exception("Element not found: ".$elementName); - + return $this->elementMap[$elementName]->getValue(); } + public function valueOrNull($elementName) { + $value = $this->value($elementName); + + return trim($value)==''?null:$value; + } + /* Returns an element */ public function element ($elementName) { From 72d1eb7a718f2c69ea54b68aa2baaaa68340b0f6 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 13 May 2013 17:36:59 +0100 Subject: [PATCH 081/164] added an abstract model for quick database models read/write - needs more work... --- classes/mvc/models/mvc_AbstractModel.php | 110 +++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 classes/mvc/models/mvc_AbstractModel.php diff --git a/classes/mvc/models/mvc_AbstractModel.php b/classes/mvc/models/mvc_AbstractModel.php new file mode 100644 index 0000000..69dad8f --- /dev/null +++ b/classes/mvc/models/mvc_AbstractModel.php @@ -0,0 +1,110 @@ +structure as $k => $properties) { + $this->set( + $k, + isset($properties['default'])? + $properties['default']:null + ); + } + } + + static public function write ($db, $o) { + + + $method = !is_null($o->get('id', false))?'update':'insert'; + + $args = array(); + + if ($method == 'update') { + $args[] = sf('%s = %s', 'id', $o->get('id')); + } + +// $vars = $this->getChanges(); + foreach($o->data as $key => $value) { + if (!$o->structure[$key]['write']) continue; + $args[] = sf('%s = %%%s', $key, $o->structure[$key]['type']); + $args[] = $value; + } + + array_unshift($args, static::DB_TABLE_NAME); + + $result = call_user_func_array(array($db, $method), $args); + } + + /* generic */ + static protected function load ($db, $where = null, $orderBy = null, $offset = null, $limit = null) { + + if (is_null($where)) $where = ''; + else $where = ' where '.$where; + + if (is_null($orderBy)) $orderBy = ''; + else $orderBy = ' order by '.$orderBy; + + if (is_null($offset)) $offset = ''; + else $offset = ' offset '.$offset; + + if (is_null($limit)) $limit = ''; + else $limit = ' limit '.$limit; + + $rows = $db->select('%l %l %l %l %l', + static::DB_SELECT_QUERY, $where, $orderBy, $offset, $limit); + + $arrOut = array(); + + if (!$rows) return $arrOut; + + foreach ($rows as $row) { + $obj = new static(); + $obj->populateFromSqlRow($row); + $arrOut[] = $obj; + } + + return $arrOut; + + } + + /* generic */ + function populateFromSqlRow ($r) { + $rowData = $r->getData(); + foreach ($rowData as $k => $v) + $this->data[$k] = caster_PostgreSqlToPhp::cast(sf('%%%s', $this->structure[$k]['type']), $v); + } + + + /* generic */ + function has ($k) { + return array_key_exists($k, $this->data); + } + + /* generic */ + function set ($k, $v) { + $this->data[$k] = $v; + } + + /* generic */ + function get($key, $strict = true) { + if (!array_key_exists($key, $this->data)) + throw new Exception ('Unknown model key: '. $key); + + return $this->data[$key]; + /* + try { + $value = caster_PostgreSqlToPhp::cast(sf('%%%s', $this->structure[$key]['type']), $this->data[$key]); + return $value; + } catch (Exception $e) { + if ($strict) throw $e; + return null; + } + */ + } +} +?> \ No newline at end of file From fdfd69eef15e719db0746a42b130ca23b5666454 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Wed, 5 Jun 2013 11:02:30 +0100 Subject: [PATCH 082/164] leading 0 int like strings are left as ints --- classes/core/app/parser/uriparser_Gyokuro.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/classes/core/app/parser/uriparser_Gyokuro.php b/classes/core/app/parser/uriparser_Gyokuro.php index 1283633..6e609ac 100644 --- a/classes/core/app/parser/uriparser_Gyokuro.php +++ b/classes/core/app/parser/uriparser_Gyokuro.php @@ -29,7 +29,8 @@ static function processArg ($arg) { // Numeric handling. 1e2 is not handled as numeric as can cause issues // with slices of hashes being unintentionally parsed a numeric - if (is_numeric($arg) && !strpos(strtolower($arg), 'e')) { + // does't process as numeric if leads with a 0 + if (is_numeric($arg) && !strpos(strtolower($arg), 'e') && !(strlen($arg) > 1 && substr($arg, 0,1) === '0')) { // check if is float if (strpos($arg, '.')) { From 29cc9318015d3723882b81849dd9df7efce14fa7 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 11 Jun 2013 16:35:15 +0100 Subject: [PATCH 083/164] added specific exception for file type validator --- classes/validators/exceptions/ValidationException.php | 3 --- .../exceptions/ValidationIncorrectFileTypeException.php | 5 +++++ classes/validators/validate_FileType.php | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 classes/validators/exceptions/ValidationIncorrectFileTypeException.php diff --git a/classes/validators/exceptions/ValidationException.php b/classes/validators/exceptions/ValidationException.php index 4786941..262eafa 100644 --- a/classes/validators/exceptions/ValidationException.php +++ b/classes/validators/exceptions/ValidationException.php @@ -2,7 +2,4 @@ class ValidationException extends Exception { - public function __construct($message = null, $code = null) { - parent::__construct($message, $code); - } } \ No newline at end of file diff --git a/classes/validators/exceptions/ValidationIncorrectFileTypeException.php b/classes/validators/exceptions/ValidationIncorrectFileTypeException.php new file mode 100644 index 0000000..3a7fa62 --- /dev/null +++ b/classes/validators/exceptions/ValidationIncorrectFileTypeException.php @@ -0,0 +1,5 @@ +getExtensionFromMime($data['type']); } - throw new Exception(sf('File should be a valid %s%s', $this->extensionText, count($incorrectExtensionArr)?sf('(not a %s)', implode('/', $incorrectExtensionArr)):'')); + throw new ValidationIncorrectFileTypeException(sf('File should be a valid %s%s', $this->extensionText, count($incorrectExtensionArr)?sf('(not a %s)', implode('/', $incorrectExtensionArr)):'')); } From 91b400121987ede1a56003c290ffe7b676db08a0 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 11 Jun 2013 17:02:45 +0100 Subject: [PATCH 084/164] minor fix to error message fomr file validator --- classes/validators/validate_FileType.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/validators/validate_FileType.php b/classes/validators/validate_FileType.php index 4763bc0..5f253b0 100644 --- a/classes/validators/validate_FileType.php +++ b/classes/validators/validate_FileType.php @@ -108,7 +108,7 @@ public function validate($data) { $incorrectExtensionArr = $this->getExtensionFromMime($data['type']); } - throw new ValidationIncorrectFileTypeException(sf('File should be a valid %s%s', $this->extensionText, count($incorrectExtensionArr)?sf('(not a %s)', implode('/', $incorrectExtensionArr)):'')); + throw new ValidationIncorrectFileTypeException(sf('File should be a valid %s%s', $this->extensionText, count($incorrectExtensionArr)?sf(' (not a %s)', implode('/', $incorrectExtensionArr)):'')); } From 47a7c133c925fa5e437880ba3a174f2518dbcbaa Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 15 Jul 2013 09:45:34 +0100 Subject: [PATCH 085/164] added general purpose not found exception, added a white-space trim on the required validator --- classes/core/error/exceptions/exception_NotFound.php | 3 +++ classes/validators/validate_Required.php | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 classes/core/error/exceptions/exception_NotFound.php diff --git a/classes/core/error/exceptions/exception_NotFound.php b/classes/core/error/exceptions/exception_NotFound.php new file mode 100644 index 0000000..50264e4 --- /dev/null +++ b/classes/core/error/exceptions/exception_NotFound.php @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/classes/validators/validate_Required.php b/classes/validators/validate_Required.php index 4ecc0dc..0a83d85 100644 --- a/classes/validators/validate_Required.php +++ b/classes/validators/validate_Required.php @@ -18,7 +18,7 @@ public function isRequired() { return $this->required; } public function validate($data) { - if(!$this->required ||(!is_null($data) && strval($data) != "" && !is_array($data))) + if(!$this->required ||(!is_null($data) && trim(strval($data)) !== "" && !is_array($data))) return true; elseif(is_array($data)) { $empty = true; From 820953db1e7d5abd0fe383dc38d361516a542aff Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 5 Aug 2013 07:43:10 -0700 Subject: [PATCH 086/164] moved publishFlashData() before preRender() when processing a page method on controller. --- classes/core/app/atsumi_AppHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/core/app/atsumi_AppHandler.php b/classes/core/app/atsumi_AppHandler.php index e9cbf90..fedfed2 100644 --- a/classes/core/app/atsumi_AppHandler.php +++ b/classes/core/app/atsumi_AppHandler.php @@ -329,6 +329,7 @@ public function render() { // Time and execute the pre render atsumi_Debug::startTimer(); + $this->controller->publishFlashData(); $this->controller->preRender(); atsumi_Debug::record('Controller PreRender', 'Before rendering was processed the pre-render function was executed', null, true); @@ -349,7 +350,6 @@ public function render() { // Get the debugger and start a timer for rendering atsumi_Debug::startTimer(); - $this->controller->publishFlashData(); $viewData = $this->controller->getViewData(); atsumi_Debug::setViewData($viewData); From c6f3318dc6d227ae5cac70a2956ea0636f5eff2c Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 19 Aug 2013 03:13:39 -0700 Subject: [PATCH 087/164] fixed create uri method if controller is on the root of the specification --- classes/core/app/parser/uriparser_Gyokuro.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/classes/core/app/parser/uriparser_Gyokuro.php b/classes/core/app/parser/uriparser_Gyokuro.php index 6e609ac..22cdb20 100644 --- a/classes/core/app/parser/uriparser_Gyokuro.php +++ b/classes/core/app/parser/uriparser_Gyokuro.php @@ -122,9 +122,9 @@ public static function searchSpec($specification, $controller, $pathArr = array( */ public function createUri($specification, $controller, $method, $args = array()) { $components = self::searchSpec($specification, $controller); - // TODO: add args parser!! - $path = count($components) ? implode('/',$components).'/'. ($method ==''?'':$method.'/'):''; + $path = (count($components) ? implode('/',$components).'/':''). ($method ==''?'':$method.'/'); + return $path; } From 5a6a5cd56028472e7afd461910356ca574f28a20 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 19 Aug 2013 03:14:15 -0700 Subject: [PATCH 088/164] added optional parameter logFilePrefix allowing you to better organise log files --- classes/core/error/listeners/listener_LogToFile.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/classes/core/error/listeners/listener_LogToFile.php b/classes/core/error/listeners/listener_LogToFile.php index 1785cfd..0f48d33 100644 --- a/classes/core/error/listeners/listener_LogToFile.php +++ b/classes/core/error/listeners/listener_LogToFile.php @@ -26,6 +26,7 @@ class listener_LogToFile implements atsumi_Observer { * @var string */ private $logDir; + private $filePrefix; /* CONSTRUCTOR & DESTRUCTOR */ @@ -34,8 +35,9 @@ class listener_LogToFile implements atsumi_Observer { * @access public * @param string $logDir The directory to place the log files in */ - public function __construct($logDir) { + public function __construct($logDir, $filePrefix = '') { $this->logDir = $logDir; + $this->filePrefix = $filePrefix; } /* GET METHODS */ @@ -49,7 +51,7 @@ public function __construct($logDir) { * @param string $dataIn The data to write to the error log */ protected function writeToLog($dataIn) { - $filename = date('Y-m-d').'.log'; + $filename = $this->filePrefix.($this->filePrefix?'-':'').date('Y-m-d').'.log'; $handle = @fopen($this->logDir.$filename, 'a'); if(!$handle) throw new errorHandler_ListenerException('Cannot open log file: '.$this->logDir.$filename); From 4c45acfd46354e697224de0ebfef63c51d428ab1 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 19 Aug 2013 03:14:58 -0700 Subject: [PATCH 089/164] added whitespace pre-line styling to the description & data blocks in the debug panel log messages. --- classes/core/debug/atsumi_Debug.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/classes/core/debug/atsumi_Debug.php b/classes/core/debug/atsumi_Debug.php index 83c8108..b5f4ed0 100644 --- a/classes/core/debug/atsumi_Debug.php +++ b/classes/core/debug/atsumi_Debug.php @@ -650,8 +650,8 @@ protected function returnCss() { .debugConsole .typeUnknown {color: orange; } .logTitle {font-size: 14px; font-weight: bold;} .logTimestamp {font-size: 10px; font-weight: normal; color: #6677DD;} - .logDesc {font-size: 10px; font-style: italic; color:#777; margin-top:0.5em; } - .logData { background-color:#ddd; border:1px solid #ccc; padding:1em; margin-top:1em; } + .logDesc {font-size: 10px; font-style: italic; color:#777; margin-top:0.5em; white-space:pre-line; } + .logData { background-color:#ddd; border:1px solid #ccc; padding:1em; margin-top:1em; white-space:pre-line; } .debugDragToggle {height:5px; border-left:3px double #555; border-right:3px double #555; width:2px; margin:0 auto 0 auto; font-size:0px; display:block; } .debugFooter { background-color:#eee; border-top:2px inset #ccc; height:20px; padding:5px; text-align:right; color:#777; font-size: 12px; } .debugFooter span { color:#777; } From af6ba6cc20e6867b42be22ca144f4174ac126e43 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Wed, 21 Aug 2013 03:11:09 -0700 Subject: [PATCH 090/164] when outputting debug console, if a settings key contains the string "password" it will not reveal the value. --- classes/core/debug/atsumi_Debug.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/core/debug/atsumi_Debug.php b/classes/core/debug/atsumi_Debug.php index b5f4ed0..d50e12e 100644 --- a/classes/core/debug/atsumi_Debug.php +++ b/classes/core/debug/atsumi_Debug.php @@ -388,7 +388,7 @@ protected function format($value) { if(is_array($value)) { $ret = 'Array('; foreach($value as $key => $item) - $ret .= sf('
[%s] => %s
', $key, $this->format($item)); + $ret .= sf('
[%s] => %s
', $key, stripos($key,'password')?'*****':$this->format($item)); $ret .= ')'; return $ret; } From 3098f6719f51a2fecd0957015c22c36ae5f5dfd7 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Thu, 22 Aug 2013 03:30:06 -0700 Subject: [PATCH 091/164] Added support for intervals formatted like: dd:hh:mm:ss --- classes/utility/calendar/atsumi_Interval.php | 24 ++++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/classes/utility/calendar/atsumi_Interval.php b/classes/utility/calendar/atsumi_Interval.php index aa50172..2f70d85 100644 --- a/classes/utility/calendar/atsumi_Interval.php +++ b/classes/utility/calendar/atsumi_Interval.php @@ -23,17 +23,21 @@ static public function fromPostgresql ($pgInterval) { $intervalMatches = null; // regex to extrat itnerval compoenents - handle invald format - if (! preg_match ('/(?:(\d+)\sdays\s)?(?:(\d+):)?(?:(\d+):)?(\d+\.\d+)$/x', $pgInterval, $intervalMatches)) - throw new Exception ('Invalid interval format'); + if (preg_match ('/(?:(\d+)\sdays\s)?(?:(\d+):)?(?:(\d+):)?(\d+\.\d+)$/x', $pgInterval, $intervalMatches) || + preg_match ('/([0-9]{2}):([0-9]{2}):([0-9]{2})$/x', $pgInterval, $intervalMatches)) { + + // organise regex matches + $intervalMatches = array_reverse($intervalMatches); + array_pop($intervalMatches); + + // return instance of self + $reflect = new ReflectionClass('atsumi_Interval'); + $intervalInstance = $reflect->newInstanceArgs($intervalMatches); + return $intervalInstance; + - // organise regex matches - $intervalMatches = array_reverse($intervalMatches); - array_pop($intervalMatches); - - // return instance of self - $reflect = new ReflectionClass('atsumi_Interval'); - $intervalInstance = $reflect->newInstanceArgs($intervalMatches); - return $intervalInstance; + } else + throw new Exception ('Invalid interval format'); } From 972f2480394c3ec5e204881915e6581f47748fee Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Thu, 22 Aug 2013 03:30:36 -0700 Subject: [PATCH 092/164] fleshed out AbstractModel, still needs more work & testing --- classes/mvc/models/mvc_AbstractModel.php | 25 ++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/classes/mvc/models/mvc_AbstractModel.php b/classes/mvc/models/mvc_AbstractModel.php index 69dad8f..aaf88d7 100644 --- a/classes/mvc/models/mvc_AbstractModel.php +++ b/classes/mvc/models/mvc_AbstractModel.php @@ -2,7 +2,7 @@ abstract class mvc_AbstractModel { /* generic */ - private $data = array(); + protected $data = array(); @@ -35,9 +35,20 @@ static public function write ($db, $o) { $args[] = $value; } + if (!count($args)) + throw new Exception ('Model has nothing to write...'); + array_unshift($args, static::DB_TABLE_NAME); $result = call_user_func_array(array($db, $method), $args); + + if (is_null($o->get('id', false))) { + $id = $db->query('select lastval'); + dump($id); + exit; + $o->set('id', $id->i_lastval); + } + } /* generic */ @@ -75,8 +86,12 @@ static protected function load ($db, $where = null, $orderBy = null, $offset = n /* generic */ function populateFromSqlRow ($r) { $rowData = $r->getData(); - foreach ($rowData as $k => $v) + foreach ($rowData as $k => $v) { + if (!array_key_exists($k, $this->structure)) + throw new Exception (sf('Unexpected row column "%s", add this to the models \'structure\' member variable.', $k)); + $this->data[$k] = caster_PostgreSqlToPhp::cast(sf('%%%s', $this->structure[$k]['type']), $v); + } } @@ -90,6 +105,12 @@ function set ($k, $v) { $this->data[$k] = $v; } + function increment ($k, $v) { + if (!$this->has($k)) throw new Exception('unknown key: '.$k); + if (!is_numeric($this->data[$k])) throw new Exception('Key: '.$k.' is not numeric, can not increment'); + $this->data[$k] += $v; + } + /* generic */ function get($key, $strict = true) { if (!array_key_exists($key, $this->data)) From f0eec69fe21197d8dc157cbbb0d653f5a0a3d5e7 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Wed, 28 Aug 2013 08:15:33 -0700 Subject: [PATCH 093/164] minor fix for setting ID before returning after inserting --- classes/mvc/models/mvc_AbstractModel.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/classes/mvc/models/mvc_AbstractModel.php b/classes/mvc/models/mvc_AbstractModel.php index aaf88d7..28c2a41 100644 --- a/classes/mvc/models/mvc_AbstractModel.php +++ b/classes/mvc/models/mvc_AbstractModel.php @@ -43,9 +43,7 @@ static public function write ($db, $o) { $result = call_user_func_array(array($db, $method), $args); if (is_null($o->get('id', false))) { - $id = $db->query('select lastval'); - dump($id); - exit; + $id = $db->selectOne('select lastval()'); $o->set('id', $id->i_lastval); } From 35621155f43d84cab330645ed964f91b57df8661 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Sat, 28 Sep 2013 09:41:54 -0700 Subject: [PATCH 094/164] added method for returning the specification from the caster --- classes/caster/caster_Abstract.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/classes/caster/caster_Abstract.php b/classes/caster/caster_Abstract.php index cadb718..7a3f674 100644 --- a/classes/caster/caster_Abstract.php +++ b/classes/caster/caster_Abstract.php @@ -32,6 +32,11 @@ abstract class caster_Abstract { /* MAGIC METHODS */ /* METHODS */ + static public function getSpec() { + $caster = new static; + return $caster->spec; + } + /** * Checks the given string for the number of caster characters. * @param string $s The string to parse From 1840d64be20aa076f83b15ad8039d06a07afda5e Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Sat, 28 Sep 2013 09:42:39 -0700 Subject: [PATCH 095/164] added more useful error handling to abstract model --- classes/mvc/models/mvc_AbstractModel.php | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/classes/mvc/models/mvc_AbstractModel.php b/classes/mvc/models/mvc_AbstractModel.php index 28c2a41..bccbd40 100644 --- a/classes/mvc/models/mvc_AbstractModel.php +++ b/classes/mvc/models/mvc_AbstractModel.php @@ -84,11 +84,30 @@ static protected function load ($db, $where = null, $orderBy = null, $offset = n /* generic */ function populateFromSqlRow ($r) { $rowData = $r->getData(); + + // itterate through the db columns and populate the object foreach ($rowData as $k => $v) { + + // check we're expecting the currently column if (!array_key_exists($k, $this->structure)) throw new Exception (sf('Unexpected row column "%s", add this to the models \'structure\' member variable.', $k)); - $this->data[$k] = caster_PostgreSqlToPhp::cast(sf('%%%s', $this->structure[$k]['type']), $v); + try { + + $this->data[$k] = caster_PostgreSqlToPhp::cast(sf('%%%s', $this->structure[$k]['type']), $v); + + // if casting error throw a useful exception + } catch (Exception $e) { + $spec = caster_PostgreSqlToPhp::getSpec(); + throw new caster_Exception ( + sf("%s: '%s' could not be cast as '%s' (%%%s)", + $k, + is_null($v)?'NULL':$v, + $spec[$this->structure[$k]['type']], + $this->structure[$k]['type'] + ) + ); + } } } From 08397b591d32ced47ad5174a2692f08538993a95 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 8 Oct 2013 04:04:39 -0700 Subject: [PATCH 096/164] added a date format constant --- classes/utility/calendar/atsumi_DateTime.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/classes/utility/calendar/atsumi_DateTime.php b/classes/utility/calendar/atsumi_DateTime.php index 859e755..788368e 100644 --- a/classes/utility/calendar/atsumi_DateTime.php +++ b/classes/utility/calendar/atsumi_DateTime.php @@ -7,6 +7,8 @@ */ class atsumi_DateTime { + const FORMAT_FRIENDLY = "F j, Y, g:i a"; + private $timestamp; public function __construct ($timestamp) { From 50d75adfc4730d56cee819f0268f21e4f1617407 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Fri, 18 Oct 2013 05:40:33 -0700 Subject: [PATCH 097/164] added %e to postgresql casters - bigint --- .../adapter/postgresql/caster_PostgreSql.php | 20 +++++++++++++++++++ .../postgresql/caster_PostgreSqlToPhp.php | 2 ++ 2 files changed, 22 insertions(+) diff --git a/classes/database/adapter/postgresql/caster_PostgreSql.php b/classes/database/adapter/postgresql/caster_PostgreSql.php index cf00833..b3b00b9 100644 --- a/classes/database/adapter/postgresql/caster_PostgreSql.php +++ b/classes/database/adapter/postgresql/caster_PostgreSql.php @@ -32,6 +32,8 @@ class caster_PostgreSql extends caster_Abstract { 'c' => 'character', 'C' => 'characterVarying', 'd' => 'date', + 'e' => 'bigInteger', + 'E' => 'bigIntegerOrNull', 'f' => 'float', 'g' => 'geometry', 'i' => 'integer', @@ -185,6 +187,24 @@ static function integerOrNull($in) { return sf("%s::INTEGER", intval($in)); } + /** + * Casts a variable into a PostgreSQL bigint + * @param int $in Int to be casted + * @return string Casted string + */ + static function bigInteger($in) { + return sf("%s::BIGINT", intval($in)); + } + /** + * Casts a variable into a PostgreSQL big int or Null + * @param int $in Int to be casted + * @return string Casted string + */ + static function bigIntegerOrNull($in) { + if (is_null($in)) return 'NULL'; + return sf("%s::BIGINT", intval($in)); + } + /** * Casts a variable into a PostgreSQL numeric * @param mixed $in Mixed to be casted diff --git a/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php b/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php index a3aa075..96fb1c3 100644 --- a/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php +++ b/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php @@ -30,6 +30,8 @@ class caster_PostgreSqlToPhp extends caster_Abstract { 'b' => 'boolean', 'd' => 'date', 'D' => 'dateOrNull', + 'e' => 'integer', + 'E' => 'integerOrNull', 'g' => 'geometry', 'G' => 'geometryOrNull', 'i' => 'integer', From 86e288c5a830e37cf0ada2d996b0a4534fe0f966 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Fri, 18 Oct 2013 05:44:27 -0700 Subject: [PATCH 098/164] atsumi_ErrorEventArgs->recoverer is NULL if not recovering, listner_addToSession now accepts param to be disabled if not recovering. --- classes/caster/caster_Abstract.php | 1 - classes/core/error/atsumi_ErrorEventArgs.php | 2 +- classes/core/error/atsumi_ErrorHandler.php | 25 ++++++++++++++++--- .../error/listeners/listener_AddToSession.php | 5 +++- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/classes/caster/caster_Abstract.php b/classes/caster/caster_Abstract.php index 7a3f674..6ac8eb0 100644 --- a/classes/caster/caster_Abstract.php +++ b/classes/caster/caster_Abstract.php @@ -93,7 +93,6 @@ public function castString($string, $args = null, $_ = null) { public function castObject($string, $args = null, $_ = null) { // Get args $args = func_get_args(); - if (count($args[0]) !== 2) throw new caster_Exception('Cast object expects two parameters'); $ch = substr($args[0][0], 1); diff --git a/classes/core/error/atsumi_ErrorEventArgs.php b/classes/core/error/atsumi_ErrorEventArgs.php index a3e94ea..40a7ff3 100644 --- a/classes/core/error/atsumi_ErrorEventArgs.php +++ b/classes/core/error/atsumi_ErrorEventArgs.php @@ -37,7 +37,7 @@ class atsumi_ErrorEventArgs extends atsumi_EventArgs { * @param Exception $e The exception that was thrown * @param string $recover The recover that was used */ - public function __construct(Exception $e, $recoverer = '') { + public function __construct(Exception $e, $recoverer = null) { $this->exception = $e; $this->recoverer = $recoverer; } diff --git a/classes/core/error/atsumi_ErrorHandler.php b/classes/core/error/atsumi_ErrorHandler.php index 8712bb5..fff1ffc 100644 --- a/classes/core/error/atsumi_ErrorHandler.php +++ b/classes/core/error/atsumi_ErrorHandler.php @@ -187,10 +187,16 @@ public function handleException(Exception $e, $recover = true) { try { // fires the exception_fc event if not blocked by flood control if(!$this->blockedByFloodControl(get_class($e), $e->getLine(), $e->getFile())) - $this->fireEvent(self::EVENT_EXCEPTION_FC, new atsumi_ErrorEventArgs($e, $this->recoverer)); + $this->fireEvent( + self::EVENT_EXCEPTION_FC, + new atsumi_ErrorEventArgs($e, $recover?$this->recoverer:null) + ); // fire the exception event regardless of flood control - $this->fireEvent(self::EVENT_EXCEPTION, new atsumi_ErrorEventArgs($e, $this->recoverer)); + $this->fireEvent( + self::EVENT_EXCEPTION, + new atsumi_ErrorEventArgs($e, $recover?$this->recoverer:null) + ); if ($recover) $this->recoverer->recover($e); @@ -219,13 +225,24 @@ protected function fireEvent($eventType, atsumi_EventArgs $args = null) { // remove failed listener $this->removeObserver($listener); // handle the exception - $this->handleException(new errorHandler_Exception(sf("Listener '%s' failed: ", get_class($listener), $listenerException->getMessage()))); + $this->handleException( + new errorHandler_Exception( + sf("Listener '%s' failed: ", + get_class($listener), + $listenerException->getMessage() + ) + ) + ); } } // record exception as listened to if ($eventType == self::EVENT_EXCEPTION_FC) - $this->recordInFloodControl(get_class($args->exception), $args->exception->getLine(), $args->exception->getFile()); + $this->recordInFloodControl( + get_class($args->exception), + $args->exception->getLine(), + $args->exception->getFile() + ); } public function listen ($e) { $this->handleException($e, false); diff --git a/classes/core/error/listeners/listener_AddToSession.php b/classes/core/error/listeners/listener_AddToSession.php index 591e417..71c90ca 100644 --- a/classes/core/error/listeners/listener_AddToSession.php +++ b/classes/core/error/listeners/listener_AddToSession.php @@ -45,6 +45,7 @@ class listener_AddToSession implements atsumi_Observer { private $namespace; private $method; + private $onlyWhenRecovering; /* CONSTRUCTOR & DESTRUCTOR */ @@ -55,12 +56,13 @@ class listener_AddToSession implements atsumi_Observer { * @param mixed $data The data to add when the error occurs * @param string $namespace The namespace to add under if atsumi session is being used [optional, default: session_Handler::DEFAULT_NAMESPACE] */ - public function __construct($key, $data, $namespace = session_Handler::DEFAULT_NAMESPACE, $method= self::METHOD_SET) { + public function __construct($key, $data, $namespace = session_Handler::DEFAULT_NAMESPACE, $method= self::METHOD_SET, $onlyWhenRecovering = false) { $this->data = $data; $this->key = $key; $this->namespace = $namespace; $this->method = $method; + $this->onlyWhenRecovering = $onlyWhenRecovering; } /* GET METHODS */ @@ -96,6 +98,7 @@ protected function mergeDataToSession($errorText) { * @param atsumi_EventArgs $args Any args related to the event */ public function notify(atsumi_Observable $sender, atsumi_EventArgs $args) { + if ($this->onlyWhenRecovering && !$args->recoverer) return; $this->mergeDataToSession(atsumi_ErrorParser::parse($args->exception, atsumi_ErrorParser::PLAINTEXT, $args->recoverer)); } } From 8c684f29b019a0213ec3d5f7e9b1e3fa0edc7b4c Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 22 Oct 2013 08:37:13 -0700 Subject: [PATCH 099/164] error handling for unknown format being passed to caster --- classes/mvc/models/mvc_AbstractModel.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/classes/mvc/models/mvc_AbstractModel.php b/classes/mvc/models/mvc_AbstractModel.php index bccbd40..a9d8a6c 100644 --- a/classes/mvc/models/mvc_AbstractModel.php +++ b/classes/mvc/models/mvc_AbstractModel.php @@ -99,6 +99,14 @@ function populateFromSqlRow ($r) { // if casting error throw a useful exception } catch (Exception $e) { $spec = caster_PostgreSqlToPhp::getSpec(); + + if (!array_key_exists($this->structure[$k]['type'], $spec)) + throw new caster_Exception ( + sf("Caster doesn't support format: %%%s", + $this->structure[$k]['type'] + ) + ); + throw new caster_Exception ( sf("%s: '%s' could not be cast as '%s' (%%%s)", $k, From 7e2e8109c73c4d53a5e0196fc0af57c0b988700d Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 22 Oct 2013 08:39:02 -0700 Subject: [PATCH 100/164] added %B (bool or null) to caster --- .../database/adapter/postgresql/caster_PostgreSqlToPhp.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php b/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php index 96fb1c3..05204ed 100644 --- a/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php +++ b/classes/database/adapter/postgresql/caster_PostgreSqlToPhp.php @@ -28,6 +28,7 @@ class caster_PostgreSqlToPhp extends caster_Abstract { 'A' => 'sqlArrayOrNull', 'a' => 'sqlArray', 'b' => 'boolean', + 'B' => 'booleanOrNull', 'd' => 'date', 'D' => 'dateOrNull', 'e' => 'integer', @@ -165,6 +166,10 @@ static function boolean($in) { if (!is_bool($in)) throw new caster_StrictTypeException('Expected Boolean, received: '.$in.' ('.gettype($in).')'); return $in; } + static function booleanOrNull($in) { + if (is_null($in)) return null; + return self::boolean($in); + } static function interval($in) { return atsumi_Interval::fromPostgresql(strval($in)); From 1cfa106c7d1bf6ba639f83dc18dc957fad59bcb0 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Wed, 30 Oct 2013 08:45:14 -0700 Subject: [PATCH 101/164] added default format, added static method to format a timestamp --- classes/utility/calendar/atsumi_DateTime.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/classes/utility/calendar/atsumi_DateTime.php b/classes/utility/calendar/atsumi_DateTime.php index 788368e..bb00ac0 100644 --- a/classes/utility/calendar/atsumi_DateTime.php +++ b/classes/utility/calendar/atsumi_DateTime.php @@ -17,11 +17,16 @@ public function __construct ($timestamp) { public function __toString() { return (String)$this->timestamp; - } - - public function format($formatString) { + } + + public function format($formatString = null) { + if (is_null($formatString)) $formatString = self::FORMAT_FRIENDLY; return date($formatString, $this->timestamp); } + static public function formatFromTimestamp ($timestamp, $formatString = null) { + $d = new self($timestamp); + return $d->format($formatString); + } } ?> \ No newline at end of file From 7e997a84cbc602d57ada2fcc95e1ce07db05c416 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Fri, 15 Nov 2013 02:45:40 -0800 Subject: [PATCH 102/164] added a static post method and added a HTTP response class, only implemented the curl method. --- classes/utility/http/atsumi_Http.php | 35 ++++++++++++++++++++ classes/utility/http/atsumi_HttpResponse.php | 32 ++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 classes/utility/http/atsumi_HttpResponse.php diff --git a/classes/utility/http/atsumi_Http.php b/classes/utility/http/atsumi_Http.php index ff05f5d..0a8d9f4 100644 --- a/classes/utility/http/atsumi_Http.php +++ b/classes/utility/http/atsumi_Http.php @@ -60,6 +60,41 @@ class atsumi_Http { //.. add the rest... + const POST_METHOD_CURL = 1; + const POST_METHOD_PECL = 2; + + static public function post ($url, $fields, $method = 1) { + + switch ($method) { + + case self::POST_METHOD_CURL: + + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_POST, count($fields)); + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($fields)); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); + curl_setopt($ch, CURLOPT_NOBODY, TRUE); + $body = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + //close connection + curl_close($ch); + + + return new atsumi_HttpResponse( + $httpCode, + $body + ); + + case self::POST_METHOD_PECL: + + return http_post_fields($url, $fields); + + } + } + + } diff --git a/classes/utility/http/atsumi_HttpResponse.php b/classes/utility/http/atsumi_HttpResponse.php new file mode 100644 index 0000000..c33a5f3 --- /dev/null +++ b/classes/utility/http/atsumi_HttpResponse.php @@ -0,0 +1,32 @@ +httpCode = $httpCode; + $this->body = $body; + + } + + public function getHttpCode() { return $this->httpCode; } + public function getBody() { return $this->body; } + public function getSuccessResponse() { + return $this->httpCode >= 200 && $this->httpCode < 300; + } + + public function getSummaryString () { + + return sf('HTTP CODE: %s, SUCCESS: %s, BODY:%s', + $this->httpCode, + $this->getSuccessResponse()?'YES':'NO', + empty($this->body)?'EMPTY':sf('"%s"', $this->body) + ); + + } + + +} +?> \ No newline at end of file From d8b28cd85980a2fac5e10bc249f5a8ab9332e376 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Fri, 15 Nov 2013 02:48:14 -0800 Subject: [PATCH 103/164] content-type and encoding headers, already declared in abstract view, but they should have been here. --- classes/mvc/views/mvc_HtmlView.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/classes/mvc/views/mvc_HtmlView.php b/classes/mvc/views/mvc_HtmlView.php index aafb059..9af3d42 100644 --- a/classes/mvc/views/mvc_HtmlView.php +++ b/classes/mvc/views/mvc_HtmlView.php @@ -10,6 +10,15 @@ abstract class mvc_HtmlView extends mvc_AbstractView { abstract protected function renderBodyContent(); + + // headers + public function setHeaders() { + header(sf('Content-Type: text/html; charset=%s', $this->getCharset())); + } + public function getCharset() { + return 'utf-8'; + } + public function render() { $this->renderDoctype(); $this->renderHtml(); From ca277dd7fc6ef7980d3a1981f5cdc8416d221b63 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 25 Nov 2013 06:49:19 -0800 Subject: [PATCH 104/164] new relic exception listerner --- classes/core/error/listeners/listener_NewRelic.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 classes/core/error/listeners/listener_NewRelic.php diff --git a/classes/core/error/listeners/listener_NewRelic.php b/classes/core/error/listeners/listener_NewRelic.php new file mode 100644 index 0000000..e94e1e4 --- /dev/null +++ b/classes/core/error/listeners/listener_NewRelic.php @@ -0,0 +1,13 @@ +exception); + } + + } +} +?> \ No newline at end of file From 989ee8f7166cd397bf4cb41e911500bacdb144b2 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Thu, 9 Jan 2014 05:35:28 -0800 Subject: [PATCH 105/164] Fixed a few bugs in date/dateTime/interval objects. Added additional functionality to AbstractModel --- classes/mvc/models/mvc_AbstractModel.php | 51 ++++++++++++++++--- classes/utility/calendar/atsumi_Date.php | 22 ++++++-- classes/utility/calendar/atsumi_DateTime.php | 1 + classes/utility/calendar/atsumi_Interval.php | 50 +++++++++++++++++- .../validators/validate_AbstractValidator.php | 2 +- 5 files changed, 112 insertions(+), 14 deletions(-) diff --git a/classes/mvc/models/mvc_AbstractModel.php b/classes/mvc/models/mvc_AbstractModel.php index a9d8a6c..31296bf 100644 --- a/classes/mvc/models/mvc_AbstractModel.php +++ b/classes/mvc/models/mvc_AbstractModel.php @@ -12,7 +12,8 @@ public function __construct () { $this->set( $k, isset($properties['default'])? - $properties['default']:null + $properties['default']:null, + true ); } } @@ -44,7 +45,7 @@ static public function write ($db, $o) { if (is_null($o->get('id', false))) { $id = $db->selectOne('select lastval()'); - $o->set('id', $id->i_lastval); + $o->set('id', $id->i_lastval, true); } } @@ -126,14 +127,52 @@ function has ($k) { } /* generic */ - function set ($k, $v) { + function set ($k, $v, $force = false) { + if (!$force && !$this->structure[$k]['write']) + throw new Exception ('Column not writable'); + $this->data[$k] = $v; } function increment ($k, $v) { - if (!$this->has($k)) throw new Exception('unknown key: '.$k); - if (!is_numeric($this->data[$k])) throw new Exception('Key: '.$k.' is not numeric, can not increment'); - $this->data[$k] += $v; + if (!$this->has($k)) throw new Exception('unknown key: '.$k); + + // increment different data types + switch ($this->structure[$k]['type']) { + + // INTERVAL + case 'z': + case 'Z': + + // check if it's null + if (is_null($this->data[$k])) + $this->data[$k] = new atsumi_Interval(0); + + $this->data[$k]->add(new atsumi_Interval($v)); + break; + + // NUMERIC + case 'i': + case 'I': + case 'e': + case 'E': + case 'n': + case 'N': + case 'f': + case 'F': + + // check if it's null + if (is_null($this->data[$k])) + $this->data[$k] = 0; + + $this->data[$k] += $v; + break; + + + default: + throw new Exception("Unexpected data type to increment %".$this->structure[$k]['type']); + + } } /* generic */ diff --git a/classes/utility/calendar/atsumi_Date.php b/classes/utility/calendar/atsumi_Date.php index 3119b84..1c94a2c 100644 --- a/classes/utility/calendar/atsumi_Date.php +++ b/classes/utility/calendar/atsumi_Date.php @@ -11,12 +11,16 @@ class atsumi_Date { private $month; private $day; + static public function fromTimestamp ($ts) { + return new self(date("Y", $ts), date("n", $ts), date("j", $ts)); + } + static public function fromYmd ($ymd) { $matches = null; if (! preg_match ('/^ (\\d{4}) - (\\d{2}) - (\\d{2}) $/x', $ymd, $matches)) { throw new Exception ('Invalid date format'); } - return new self ($matches [1], $matches [2], $matches [3]); + return new self ($matches [1], $matches [2], $matches [3]); } public function __construct ($year, $month, $day) { @@ -34,13 +38,21 @@ public function __toString() { public function getYear () { return $this->year; } public function getMonth () { return $this->month; } - public function getDay () { return $this->day; } - + public function getDay () { return $this->day; } + + + public function adjust ($year = 0, $month = 0, $day = 0) { + $newDate = self::fromTimestamp(mktime (0,0,0, $this->month+$month, $this->day+$day, $this->year+$year)); + $this->year = $newDate->getYear(); + $this->month = $newDate->getMonth(); + $this->day = $newDate->getDay(); + } + public function getTimestampDayStart () { - return mktime ($this->year, $this->month, $this->day, 0, 0, 0); + return mktime (0,0,0, $this->month, $this->day, $this->year); } public function getTimestampDayEnd () { - return mktime ($this->year, $this->month, $this->day + 1, 0, 0, 0); + return mktime (0,0,0, $this->month, $this->day + 1, $this->year); } public function getYmd () { return sprintf ('%04d-%02d-%02d', $this->year, $this->month, $this->day); diff --git a/classes/utility/calendar/atsumi_DateTime.php b/classes/utility/calendar/atsumi_DateTime.php index bb00ac0..f33b63e 100644 --- a/classes/utility/calendar/atsumi_DateTime.php +++ b/classes/utility/calendar/atsumi_DateTime.php @@ -8,6 +8,7 @@ class atsumi_DateTime { const FORMAT_FRIENDLY = "F j, Y, g:i a"; + const FORMAT_COMPACT = "d/m/y H:i"; private $timestamp; diff --git a/classes/utility/calendar/atsumi_Interval.php b/classes/utility/calendar/atsumi_Interval.php index 2f70d85..8327022 100644 --- a/classes/utility/calendar/atsumi_Interval.php +++ b/classes/utility/calendar/atsumi_Interval.php @@ -53,6 +53,10 @@ public function __construct ($seconds, $minutes = 0, $hours = 0, $days = 0, $yea ); } + public function add (self $interval) { + $this->seconds += $interval->inSeconds(); + } + public function __toString() { return strval($this->inSeconds()); } @@ -62,11 +66,11 @@ public function inSeconds() { } public function inMinutes() { - return floatval($this->seconds / self::DURATION_HOUR); + return floatval($this->seconds / self::DURATION_MINUTE); } public function inHours() { - return floatval($this->seconds / self::DURATION_MINUTE); + return floatval($this->seconds / self::DURATION_HOUR); } public function inDays() { @@ -76,6 +80,48 @@ public function inDays() { public function inYears() { return floatval($this->seconds / self::DURATION_YEAR); } + public function getFormatBreakdown() { + + $remainder = $this->inSeconds(); + + $years = floor($remainder / self::DURATION_YEAR); + $remainder -= $years * self::DURATION_YEAR; + + $days = floor($remainder / self::DURATION_DAY); + $remainder -= $days * self::DURATION_DAY; + + $hours = floor($remainder / self::DURATION_HOUR); + $remainder -= $hours * self::DURATION_HOUR; + + $minutes = floor($remainder / self::DURATION_MINUTE); + $remainder -= $minutes * self::DURATION_MINUTE; + + $seconds = floor($remainder); + + return array( + 'years' => $years, + 'days' => $days, + 'hours' => $hours, + 'minutes' => $minutes, + 'seconds' => $seconds, + ); + } + + public function format($compact = false) { + + $formatComponents = $this->getFormatBreakdown(); + + return sf('%s%s%s:%s:%s', + $formatComponents['years']?sf('%sy ',$formatComponents['years']):'', + $formatComponents['days']||$formatComponents['days']?sf('%sd ',$formatComponents['days']):'', + sprintf('%02d', $formatComponents['hours']), + sprintf('%02d', $formatComponents['minutes']), + sprintf('%02d', $formatComponents['seconds']) + + ); + + } + } ?> \ No newline at end of file diff --git a/classes/validators/validate_AbstractValidator.php b/classes/validators/validate_AbstractValidator.php index d9c5b9c..6a0bae8 100644 --- a/classes/validators/validate_AbstractValidator.php +++ b/classes/validators/validate_AbstractValidator.php @@ -13,4 +13,4 @@ abstract function validate($data); -?> +?> \ No newline at end of file From 72689d003d5fde482f3b410bcae8ff8643377f68 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 13 Jan 2014 10:09:01 -0800 Subject: [PATCH 106/164] corrected wording slightly --- classes/validators/validate_MaxChars.php | 2 +- classes/validators/validate_MinChars.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/classes/validators/validate_MaxChars.php b/classes/validators/validate_MaxChars.php index 7ab1627..3bac03e 100644 --- a/classes/validators/validate_MaxChars.php +++ b/classes/validators/validate_MaxChars.php @@ -25,7 +25,7 @@ public function validate($data) { if(empty($data) || mb_strlen($data, $this->encoding) <= $this->maxChars) return true; else - throw new Exception(sf("You must enter less than %s characters", + throw new Exception(sf("You must enter %s characters or less", $this->maxChars )); } diff --git a/classes/validators/validate_MinChars.php b/classes/validators/validate_MinChars.php index 2be6a73..6c32261 100644 --- a/classes/validators/validate_MinChars.php +++ b/classes/validators/validate_MinChars.php @@ -19,7 +19,7 @@ public function validate($data) { if(empty($data) || strlen($data) >= $this->minChars) return true; - else throw new Exception(sf("You must enter more than %s characters", + else throw new Exception(sf("You must enter %s or more characters", $this->minChars )); From 6d7d18b48bd3eed9c683440e3e0e125aa2be0516 Mon Sep 17 00:00:00 2001 From: Chris Chrisostomou Date: Fri, 7 Mar 2014 14:35:48 +0000 Subject: [PATCH 107/164] getRawElement() --- classes/widgets/form/widget_Form.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/classes/widgets/form/widget_Form.php b/classes/widgets/form/widget_Form.php index 2db8962..504a916 100644 --- a/classes/widgets/form/widget_Form.php +++ b/classes/widgets/form/widget_Form.php @@ -246,10 +246,15 @@ public function render() { public function hasElement($elementName) { return array_key_exists($elementName, $this->elementMap); } + public function getElement($elementName, $options = array()) { return $this->elementMap[$elementName]->render($options); } + public function getRawElement($elementName) { + return $this->elementMap[$elementName]->renderElement(); + } + public function getFormTop() { $html = sf('
', From 89a5c0ec50b983332c9674094a9113ebb3273300 Mon Sep 17 00:00:00 2001 From: Chris Chrisostomou Date: Fri, 7 Mar 2014 15:11:08 +0000 Subject: [PATCH 108/164] getRawElement() -> getElement('xyz', array('elementOnly'=>true)) --- .../form/elements/widget_AbstractElement.php | 48 +++++++++++-------- classes/widgets/form/widget_Form.php | 4 -- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/classes/widgets/form/elements/widget_AbstractElement.php b/classes/widgets/form/elements/widget_AbstractElement.php index ca596a9..3aceb8e 100644 --- a/classes/widgets/form/elements/widget_AbstractElement.php +++ b/classes/widgets/form/elements/widget_AbstractElement.php @@ -56,34 +56,42 @@ static public function makeInputSafe($in) { } public function render($options = array()) { - - $out = $this->preRender(); - $out .= sfl('
', + + // If elementOnly is specified, only the form element itself is returned + if (isset($options['elementOnly']) && $options['elementOnly']) { + $out = $this->renderElement(); + + } else { + $out = $this->preRender(); + $out .= sfl('
', $this->style ? " " . $this->style : "", $this->cssClass ? " " . $this->cssClass : "", ($this->submitted && !$this->validates) ? " error" : "", $this->name, $this->cssStyle ? " style='" . $this->cssStyle . "'": "" ); - try { - $out .= sf('%s%s
%s
', - $this->renderErrors(), - (($this->label != '' || $this->getRequired()) && - (!array_key_exists('label', $options) || $options['label'] !== false) ? - $this->renderLabel() : ''), - $this->renderElement()); - } catch (Exception $e) { - - // if in debug mode display exception details - if (atsumi_Debug::getActive()) - $out .= sfl('Element Exception "%s": %s #%s', $e->getMessage(), $e->getFile(), $e->getLine()); - - // fire error listeners - Atsumi::error__listen($e); + + try { + $out .= sf('%s%s
%s
', + $this->renderErrors(), + (($this->label != '' || $this->getRequired()) && + (!array_key_exists('label', $options) || $options['label'] !== false) ? + $this->renderLabel() : ''), + $this->renderElement()); + } catch (Exception $e) { + + // if in debug mode display exception details + if (atsumi_Debug::getActive()) + $out .= sfl('Element Exception "%s": %s #%s', $e->getMessage(), $e->getFile(), $e->getLine()); + + // fire error listeners + Atsumi::error__listen($e); + } + $out .= '
'; + + $out .= $this->postRender(); } - $out .= '
'; - $out .= $this->postRender(); return $out; } diff --git a/classes/widgets/form/widget_Form.php b/classes/widgets/form/widget_Form.php index 504a916..e29432e 100644 --- a/classes/widgets/form/widget_Form.php +++ b/classes/widgets/form/widget_Form.php @@ -251,10 +251,6 @@ public function getElement($elementName, $options = array()) { return $this->elementMap[$elementName]->render($options); } - public function getRawElement($elementName) { - return $this->elementMap[$elementName]->renderElement(); - } - public function getFormTop() { $html = sf('', From 7e84d9f58253a86fbfef4e6498a22bfb78d055bd Mon Sep 17 00:00:00 2001 From: Chris Chrisostomou Date: Sat, 5 Apr 2014 11:40:21 +0100 Subject: [PATCH 109/164] short tags removed from atsumi debug --- classes/core/debug/atsumi_Debug.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) mode change 100644 => 100755 classes/core/debug/atsumi_Debug.php diff --git a/classes/core/debug/atsumi_Debug.php b/classes/core/debug/atsumi_Debug.php old mode 100644 new mode 100755 index 83c8108..0e78076 --- a/classes/core/debug/atsumi_Debug.php +++ b/classes/core/debug/atsumi_Debug.php @@ -705,13 +705,13 @@ public function _render() {
-consoleData as $data) : ?> +consoleData as $data) : ?>
%s
', $this->format($data['data'])) : '');?>
- +
@@ -773,7 +773,7 @@ public function _render() {
-databases as $key => $database) : $totalTime = 0; foreach($database->getQueryTimes() as $query) @@ -782,7 +782,7 @@ public function _render() {

Database

Total Query time:
format($database->getQueryTimes()));?> - +
From 160ad77f69cdfa1ba072bef70e1231af505135c3 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 28 Apr 2014 12:25:31 +0000 Subject: [PATCH 110/164] apache 2.4 updates --- classes/core/debug/atsumi_Debug.php | 8 ++++---- include/http.php | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/classes/core/debug/atsumi_Debug.php b/classes/core/debug/atsumi_Debug.php index d50e12e..5037f42 100644 --- a/classes/core/debug/atsumi_Debug.php +++ b/classes/core/debug/atsumi_Debug.php @@ -705,13 +705,13 @@ public function _render() {
-consoleData as $data) : ?> +consoleData as $data) : ?>
%s
', $this->format($data['data'])) : '');?>
- +
@@ -773,7 +773,7 @@ public function _render() {
-databases as $key => $database) : $totalTime = 0; foreach($database->getQueryTimes() as $query) @@ -782,7 +782,7 @@ public function _render() {

Database

Total Query time:
format($database->getQueryTimes()));?> - +
diff --git a/include/http.php b/include/http.php index 8b5fb81..68d8c27 100644 --- a/include/http.php +++ b/include/http.php @@ -1,4 +1,4 @@ - Date: Tue, 6 May 2014 16:43:35 +0000 Subject: [PATCH 111/164] Added a temporary storage handler, introduced a default TTL per cache handler. --- classes/cache/cache_AbstractHandler.php | 7 ++- classes/cache/cache_ApcHandler.php | 3 + classes/cache/cache_HandlerInterface.php | 1 + classes/cache/cache_MemcacheHandler.php | 2 + .../cache/cache_TemporaryStorageHandler.php | 61 +++++++++++++++++++ 5 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 classes/cache/cache_TemporaryStorageHandler.php diff --git a/classes/cache/cache_AbstractHandler.php b/classes/cache/cache_AbstractHandler.php index f322d8b..5940ab3 100644 --- a/classes/cache/cache_AbstractHandler.php +++ b/classes/cache/cache_AbstractHandler.php @@ -17,6 +17,8 @@ */ abstract class cache_AbstractHandler implements cache_HandlerInterface { + const DEFAULT_TTL = 0; + /** * Weather or not cache system should throw exceptions * @var bool @@ -60,7 +62,10 @@ final public function get($key, $default = null, $namespace = 'default') { * @param string $namespace The namespace under which the variable is stored [optional, default: 'default'] * @return boolen True on success or, False on failure */ - final public function set($key, $data, $ttl = 0, $namespace = 'default') { + final public function set($key, $data, $ttl = null, $namespace = 'default') { + + if (is_null($ttl)) $ttl = static::DEFAULT_TTL; + try { return $this->_set($key, $data, $ttl, $namespace); } catch (Exception $e) { diff --git a/classes/cache/cache_ApcHandler.php b/classes/cache/cache_ApcHandler.php index 4745705..53c3c1f 100644 --- a/classes/cache/cache_ApcHandler.php +++ b/classes/cache/cache_ApcHandler.php @@ -17,6 +17,9 @@ */ class cache_ApcHandler extends cache_AbstractHandler { /* CONSTANTS */ + + const DEFAULT_TTL = 0; + /* PROPERTIES */ /* CONSTRUCTOR & DESTRUCTOR */ diff --git a/classes/cache/cache_HandlerInterface.php b/classes/cache/cache_HandlerInterface.php index e4efc7f..acb03a8 100644 --- a/classes/cache/cache_HandlerInterface.php +++ b/classes/cache/cache_HandlerInterface.php @@ -18,6 +18,7 @@ */ interface cache_HandlerInterface { /* CONSTRUCTOR & DESTRUCTOR */ + /* GET METHODS */ /** diff --git a/classes/cache/cache_MemcacheHandler.php b/classes/cache/cache_MemcacheHandler.php index a550a0c..cadb688 100644 --- a/classes/cache/cache_MemcacheHandler.php +++ b/classes/cache/cache_MemcacheHandler.php @@ -19,6 +19,8 @@ class cache_MemcacheHandler extends cache_AbstractHandler { /* CONSTANTS */ + const DEFAULT_TTL = 0; + /** * Constant defining a minite storage duration * @var integer diff --git a/classes/cache/cache_TemporaryStorageHandler.php b/classes/cache/cache_TemporaryStorageHandler.php new file mode 100644 index 0000000..7ebd5da --- /dev/null +++ b/classes/cache/cache_TemporaryStorageHandler.php @@ -0,0 +1,61 @@ +store[$namespace][$key]) && + (is_null($this->store[$namespace][$key]['expires']) || $this->store[$namespace][$key]['expires'] > time())) + return $this->store[$namespace][$key]['value']; + + else return $default; + + } + protected function _set($key, $data, $ttl = null, $namespace = 'default') { + + if (!isset($this->store[$namespace])) { + $this->store[$namespace] = array(); + } + + $this->store[$namespace][$key] = array( + 'value' => $data, + 'expires' => is_null($ttl)?null:(time() + $ttl) + ); + + } + protected function _delete($key, $namespace = 'default') { + + if (isset($this->store[$namespace][$key])); + unset($this->store[$namespace][$key]); + + } + protected function _exists($key, $namespace = 'default'){ + + return isset($this->store[$namespace][$key]) && + (is_null($this->store[$namespace][$key]['expires']) || $this->store[$namespace][$key]['expires'] > time()); + } + + protected function _flush() { + $this->store = array(); + } + +} + +?> \ No newline at end of file From fd9778a58c3b2ed3063e088a1b6087daf5eb4e4c Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 6 May 2014 16:44:30 +0000 Subject: [PATCH 112/164] HTTP class much better, now can return headers & body content --- classes/utility/http/atsumi_Http.php | 16 ++++++++++++++-- classes/utility/http/atsumi_HttpResponse.php | 5 ++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/classes/utility/http/atsumi_Http.php b/classes/utility/http/atsumi_Http.php index 0a8d9f4..5bc92d8 100644 --- a/classes/utility/http/atsumi_Http.php +++ b/classes/utility/http/atsumi_Http.php @@ -74,16 +74,28 @@ static public function post ($url, $fields, $method = 1) { curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POST, count($fields)); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($fields)); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE); curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); - curl_setopt($ch, CURLOPT_NOBODY, TRUE); - $body = curl_exec($ch); + curl_setopt($ch, CURLOPT_HEADER, true); + curl_setopt($ch, CURLOPT_NOBODY, FALSE); + curl_setopt($ch, CURLOPT_VERBOSE, TRUE); + curl_setopt($ch, CURLOPT_MAXREDIRS, 10); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 120); + curl_setopt($ch, CURLOPT_TIMEOUT, 120); + $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + + $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); + $header = substr($response, 0, $headerSize); + $body = substr($response, $headerSize); + //close connection curl_close($ch); return new atsumi_HttpResponse( $httpCode, + $header, $body ); diff --git a/classes/utility/http/atsumi_HttpResponse.php b/classes/utility/http/atsumi_HttpResponse.php index c33a5f3..4652f48 100644 --- a/classes/utility/http/atsumi_HttpResponse.php +++ b/classes/utility/http/atsumi_HttpResponse.php @@ -2,17 +2,20 @@ class atsumi_HttpResponse { private $httpCode; + private $header; private $body; - function __construct($httpCode, $body) { + function __construct($httpCode, $header, $body) { $this->httpCode = $httpCode; + $this->header = $header; $this->body = $body; } public function getHttpCode() { return $this->httpCode; } public function getBody() { return $this->body; } + public function getHeader() { return $this->header; } public function getSuccessResponse() { return $this->httpCode >= 200 && $this->httpCode < 300; } From dd0b106b93ee56a1b86a2fa451df14a6a0e06a9a Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 6 May 2014 16:44:56 +0000 Subject: [PATCH 113/164] added a disabled state --- classes/widgets/form/elements/widget_TextElement.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/classes/widgets/form/elements/widget_TextElement.php b/classes/widgets/form/elements/widget_TextElement.php index aacb597..646e2b7 100644 --- a/classes/widgets/form/elements/widget_TextElement.php +++ b/classes/widgets/form/elements/widget_TextElement.php @@ -9,6 +9,7 @@ class widget_TextElement extends widget_AbstractElement { protected $onKeydown = null; protected $onFocus = null; protected $onBlur = null; + protected $disabled = false; protected $rows; public function __construct($args) { @@ -26,14 +27,17 @@ public function __construct($args) { if (array_key_exists('onFocus', $args) && strlen($args['onFocus'])) $this->onFocus = $args['onFocus']; - + if (array_key_exists('onBlur', $args) && strlen($args['onBlur'])) $this->onBlur = $args['onBlur']; + + if (array_key_exists('disabled', $args) && is_bool($args['disabled'])) + $this->disabled = $args['disabled']; } function renderElement() { - return(sf('', + return(sf('', $this->htmlType, $this->getName(), parent::makeInputSafe($this->getValue()), @@ -44,7 +48,8 @@ function renderElement() { !is_null($this->onChange) && strlen($this->onChange)?sf(' onChange="%s"', $this->onChange):'', !is_null($this->onKeydown) && strlen($this->onKeydown)?sf(' onKeydown="%s"', $this->onKeydown):'', !is_null($this->onFocus) && strlen($this->onFocus)?sf(' onFocus="%s"', $this->onFocus):'', - !is_null($this->onBlur) && strlen($this->onBlur)?sf(' onBlur="%s"', $this->onBlur):'' + !is_null($this->onBlur) && strlen($this->onBlur)?sf(' onBlur="%s"', $this->onBlur):'', + $this->disabled?' disabled':'' )); } From 1f433c047850a1419ab7837dd37139849bce1618 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 12 May 2014 11:21:06 +0000 Subject: [PATCH 114/164] Abstract models now dont have concept of data access, db functionality moved to abstractDaoModel --- classes/mvc/models/mvc_AbstractDaoModel.php | 165 +++++++++++-------- classes/mvc/models/mvc_AbstractModel.php | 172 ++++++++------------ 2 files changed, 167 insertions(+), 170 deletions(-) diff --git a/classes/mvc/models/mvc_AbstractDaoModel.php b/classes/mvc/models/mvc_AbstractDaoModel.php index b8690a5..f9763bf 100644 --- a/classes/mvc/models/mvc_AbstractDaoModel.php +++ b/classes/mvc/models/mvc_AbstractDaoModel.php @@ -1,77 +1,112 @@ select($this->selectQuery); - $returnArray = array(); - - foreach( $results as $row ) { - $forum = new gts_ForumAreaModel($db); - $forum->loadFromSqlRow($row); - $returnArray[] = $forum; +abstract class mvc_AbstractDaoModel extends mvc_AbstractModel { + + + static public function write ($db, $o) { + + + $method = !is_null($o->get('id', false))?'update':'insert'; + + $args = array(); + + if ($method == 'update') { + $args[] = sf('%s = %s', 'id', $o->get('id')); + } + +// $vars = $this->getChanges(); + foreach($o->data as $key => $value) { + if (!$o->structure[$key]['write']) continue; + $args[] = sf('%s = %%%s', $key, $o->structure[$key]['type']); + $args[] = $value; + } + + if (!count($args)) + throw new Exception ('Model has nothing to write...'); + + array_unshift($args, static::DB_TABLE_NAME); + + $result = call_user_func_array(array($db, $method), $args); + + if (is_null($o->get('id', false))) { + $id = $db->selectOne('select lastval()'); + $o->set('id', $id->i_lastval, true); } - return $returnArray; - } - abstract protected function getSelectQuery(); - - final public function __construct($db) { - } - static public function load($db, $id) { - if(!is_int($id)) throw new Exception("Identifier must be of type Integer"); - die(get_class()); - $file = new gts_ForumAreaModel($db); - $file->loadFromId($id); - return $file; + + /* generic */ + static protected function load ($db, $where = null, $orderBy = null, $offset = null, $limit = null) { + + if (is_null($where)) $where = ''; + else $where = ' where '.$where; + + if (is_null($orderBy)) $orderBy = ''; + else $orderBy = ' order by '.$orderBy; + + if (is_null($offset)) $offset = ''; + else $offset = ' offset '.$offset; + + if (is_null($limit)) $limit = ''; + else $limit = ' limit '.$limit; + + $rows = $db->select('%l %l %l %l %l', + static::DB_SELECT_QUERY, $where, $orderBy, $offset, $limit); + + $arrOut = array(); + + if (!$rows) return $arrOut; + + foreach ($rows as $row) { + $obj = new static(); + $obj->populateFromSqlRow($row); + $arrOut[] = $obj; + } + + return $arrOut; + + } + + /* generic */ + function populateFromSqlRow ($r) { + $rowData = $r->getData(); + + // itterate through the db columns and populate the object + foreach ($rowData as $k => $v) { + + // check we're expecting the currently column + if (!array_key_exists($k, $this->structure)) + throw new Exception (sf('Unexpected row column "%s", add this to the models \'structure\' member variable.', $k)); + + try { + + $this->data[$k] = caster_PostgreSqlToPhp::cast(sf('%%%s', $this->structure[$k]['type']), $v); + + // if casting error throw a useful exception + } catch (Exception $e) { + $spec = caster_PostgreSqlToPhp::getSpec(); + + if (!array_key_exists($this->structure[$k]['type'], $spec)) + throw new caster_Exception ( + sf("Caster doesn't support format: %%%s", + $this->structure[$k]['type'] + ) + ); + + throw new caster_Exception ( + sf("%s: '%s' could not be cast as '%s' (%%%s)", + $k, + is_null($v)?'NULL':$v, + $spec[$this->structure[$k]['type']], + $this->structure[$k]['type'] + ) + ); + } + } } - - public function loadFromId($id) { - - $row = $this->db->select_1(" - SELECT f.id, f.path, f.user_id, f.size, f.memorial_id, u.namefirst AS user_firstname, - u.namelast AS user_lastname, u.vc_urllocation AS user_url, - f.order, mime_type, uploaded, caption - FROM file f - INNER JOIN users u ON f.user_id = u.id - WHERE f.id = %i ", $id); - - $this->loadFromSqlRow($row); - - } - -} -/* - * ORM Implementation - put on ice - * -class gts_ForumAreaModel extends orm_AbstractModel { - public function __construct() { - - // TODO : This is only here as PHP won't let this be declared - // by default... - - $this->id = new orm_DataType_Integer(); - $this->name = new orm_DataType_Text(); - $this->sef_name = new orm_DataType_Text(); - $this->description = new orm_DataType_Text(); - - - - $this->map("id", new orm_DataType_Integer()); - $this->map("name", new orm_DataType_Text()); - $this->map("sef_name", new orm_DataType_Text()); - $this->map("description", new orm_DataType_Text()); - - } } - */ diff --git a/classes/mvc/models/mvc_AbstractModel.php b/classes/mvc/models/mvc_AbstractModel.php index 31296bf..8f19b84 100644 --- a/classes/mvc/models/mvc_AbstractModel.php +++ b/classes/mvc/models/mvc_AbstractModel.php @@ -1,13 +1,20 @@ structure as $k => $properties) { $this->set( $k, @@ -16,109 +23,15 @@ public function __construct () { true ); } - } - - static public function write ($db, $o) { - - - $method = !is_null($o->get('id', false))?'update':'insert'; - - $args = array(); - - if ($method == 'update') { - $args[] = sf('%s = %s', 'id', $o->get('id')); - } -// $vars = $this->getChanges(); - foreach($o->data as $key => $value) { - if (!$o->structure[$key]['write']) continue; - $args[] = sf('%s = %%%s', $key, $o->structure[$key]['type']); - $args[] = $value; - } - - if (!count($args)) - throw new Exception ('Model has nothing to write...'); - - array_unshift($args, static::DB_TABLE_NAME); - - $result = call_user_func_array(array($db, $method), $args); - - if (is_null($o->get('id', false))) { - $id = $db->selectOne('select lastval()'); - $o->set('id', $id->i_lastval, true); - } - - } - - /* generic */ - static protected function load ($db, $where = null, $orderBy = null, $offset = null, $limit = null) { - - if (is_null($where)) $where = ''; - else $where = ' where '.$where; - - if (is_null($orderBy)) $orderBy = ''; - else $orderBy = ' order by '.$orderBy; - - if (is_null($offset)) $offset = ''; - else $offset = ' offset '.$offset; - - if (is_null($limit)) $limit = ''; - else $limit = ' limit '.$limit; - - $rows = $db->select('%l %l %l %l %l', - static::DB_SELECT_QUERY, $where, $orderBy, $offset, $limit); - - $arrOut = array(); - - if (!$rows) return $arrOut; - - foreach ($rows as $row) { - $obj = new static(); - $obj->populateFromSqlRow($row); - $arrOut[] = $obj; + // set data + foreach ($data as $k => $value) { + $this->set($k, $value); } - - return $arrOut; - } - /* generic */ - function populateFromSqlRow ($r) { - $rowData = $r->getData(); - - // itterate through the db columns and populate the object - foreach ($rowData as $k => $v) { - - // check we're expecting the currently column - if (!array_key_exists($k, $this->structure)) - throw new Exception (sf('Unexpected row column "%s", add this to the models \'structure\' member variable.', $k)); - - try { - - $this->data[$k] = caster_PostgreSqlToPhp::cast(sf('%%%s', $this->structure[$k]['type']), $v); - - // if casting error throw a useful exception - } catch (Exception $e) { - $spec = caster_PostgreSqlToPhp::getSpec(); - - if (!array_key_exists($this->structure[$k]['type'], $spec)) - throw new caster_Exception ( - sf("Caster doesn't support format: %%%s", - $this->structure[$k]['type'] - ) - ); - - throw new caster_Exception ( - sf("%s: '%s' could not be cast as '%s' (%%%s)", - $k, - is_null($v)?'NULL':$v, - $spec[$this->structure[$k]['type']], - $this->structure[$k]['type'] - ) - ); - } - } - } + function preOutput($outputType) { } + /* generic */ @@ -128,7 +41,12 @@ function has ($k) { /* generic */ function set ($k, $v, $force = false) { - if (!$force && !$this->structure[$k]['write']) + if (!$force && + ( + isset($this->structure[$k]['write']) && + $this->structure[$k]['write'] == false + ) + ) throw new Exception ('Column not writable'); $this->data[$k] = $v; @@ -191,5 +109,49 @@ function get($key, $strict = true) { } */ } + + + function output ($type = self::OUTPUT_FORMAT_ASSOC) { + + $this->preOutput($type); + + switch ($type) { + + // Native object + case self::OUTPUT_FORMAT_OBJECT: + return $this; + + // Associative array + case self::OUTPUT_FORMAT_ASSOC: + $out = array(); + foreach($this->data as $key => $value) { + if (isset($this->structure[$key]['output']) && + $this->structure[$key]['output'] == false) + continue; + + if ($value instanceof mvc_AbstractModel) + $out[$key] = $value->output($type); + else + $out[$key] = $value; + } + return $out; + + // Std Class + case self::OUTPUT_FORMAT_STD_CLASS: + $out = new stdClass(); + foreach($this->data as $key => $value) { + if (isset($this->structure[$key]['output']) && + $this->structure[$key]['output'] == false) + continue; + + if ($value instanceof mvc_AbstractModel) + $out[$key] = $value->output($type); + else + $out[$key] = $value; + } + return $out; + } + + } } ?> \ No newline at end of file From c4ea8f6d4cd8f4e46351799e377878814f2160e8 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 12 May 2014 11:22:10 +0000 Subject: [PATCH 115/164] Explosed db queries and times via atsumi_Debug::getDbQueries() - this allows them to be used in places other than just the debug bar --- classes/core/debug/atsumi_Debug.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/classes/core/debug/atsumi_Debug.php b/classes/core/debug/atsumi_Debug.php index 5037f42..f8f8f8a 100644 --- a/classes/core/debug/atsumi_Debug.php +++ b/classes/core/debug/atsumi_Debug.php @@ -316,6 +316,7 @@ public function _endTimer() { return round((microtime(true) - $startTime), 3).' microseconds'; } + /** * Records an event along with optional data, timer and area the data belongs to * Note: This will do nothing if the debugger is not active @@ -872,5 +873,22 @@ public static function addArea($name, $color) { public static function render() { return self::__callStatic(__FUNCTION__, array()); } + + public static function getDbQueries() { + return self::__callStatic(__FUNCTION__, array()); + } + + public function _getDbQueries () { + + $out = array(); + foreach($this->databases as $key => $database) { + $db = array(); + foreach($database->getQueryTimes() as $query) { + $db[] = $query; + } + $out[] = $db; + } + return $out; + } } ?> \ No newline at end of file From 759bdd901213d582e4262ebdf4320aa5745493c8 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 12 May 2014 11:22:53 +0000 Subject: [PATCH 116/164] fixed bug in required validator - should test this --- classes/validators/validate_Required.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/validators/validate_Required.php b/classes/validators/validate_Required.php index 0a83d85..933396e 100644 --- a/classes/validators/validate_Required.php +++ b/classes/validators/validate_Required.php @@ -18,7 +18,7 @@ public function isRequired() { return $this->required; } public function validate($data) { - if(!$this->required ||(!is_null($data) && trim(strval($data)) !== "" && !is_array($data))) + if(!$this->required || (!is_array($data) && !is_null($data) && trim(strval($data)) !== "")) return true; elseif(is_array($data)) { $empty = true; From c57a6821105d3b2f98b8139d5cd6a462c98ccdaa Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 13 May 2014 17:04:03 +0000 Subject: [PATCH 117/164] output() now iterates through values including arrays and outputs those too --- classes/mvc/models/mvc_AbstractModel.php | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/classes/mvc/models/mvc_AbstractModel.php b/classes/mvc/models/mvc_AbstractModel.php index 8f19b84..0f643a0 100644 --- a/classes/mvc/models/mvc_AbstractModel.php +++ b/classes/mvc/models/mvc_AbstractModel.php @@ -110,6 +110,24 @@ function get($key, $strict = true) { */ } + static function outputItem ($value, $type) { + + // if abstract model then output that + if ($value instanceof mvc_AbstractModel) + return $value->output($type); + + // if array itterate through + elseif (is_array($value)) { + $arrayOut = array(); + foreach ($value as $item) + $arrayOut[] = self::outputItem($item, $type); + return $arrayOut; + + // otherwise just return value + } else + return $value; + + } function output ($type = self::OUTPUT_FORMAT_ASSOC) { @@ -129,10 +147,7 @@ function output ($type = self::OUTPUT_FORMAT_ASSOC) { $this->structure[$key]['output'] == false) continue; - if ($value instanceof mvc_AbstractModel) - $out[$key] = $value->output($type); - else - $out[$key] = $value; + $out[$key] = self::outputItem($value, $type); } return $out; From 90201864e872c2020d296ef17c0dcaa71f9cb7af Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Thu, 15 May 2014 17:21:45 +0000 Subject: [PATCH 118/164] added json caster, untested. Added an ::getCastName() to the casters to convert a format character to the method name for casting. --- classes/caster/caster_Abstract.php | 12 +++++++ classes/caster/caster_Json.php | 52 ++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 classes/caster/caster_Json.php diff --git a/classes/caster/caster_Abstract.php b/classes/caster/caster_Abstract.php index 6ac8eb0..53ef7a5 100644 --- a/classes/caster/caster_Abstract.php +++ b/classes/caster/caster_Abstract.php @@ -153,6 +153,10 @@ protected function castReal($format, $args) { if(array_key_exists($ch, $this->spec) && method_exists($this,$this->spec[$ch])) { $methodName = $this->spec[$ch]; $ret .= $this->$methodName($args[$i++]); + + } elseif(array_key_exists($ch, $this->spec) && method_exists($this,'cast_'.$this->spec[$ch])) { + $methodName = 'cast_'.$this->spec[$ch]; + $ret .= $this->$methodName($args[$i++]); } elseif($ch == '%') { $ret .= '%'; } else { @@ -196,6 +200,14 @@ function formatNewLines($str) { return $ret; } + static function getCastName($format) { + $caster = new static; + return $caster->getCastNameReal($format); + } + function getCastNameReal ($format) { + return $this->spec[$format]; + } + /* DEPRECATED METHODS */ } diff --git a/classes/caster/caster_Json.php b/classes/caster/caster_Json.php new file mode 100644 index 0000000..99f2709 --- /dev/null +++ b/classes/caster/caster_Json.php @@ -0,0 +1,52 @@ + 'string', + 'n' => 'number', + + /* these aren't real JSON types, but can validate on them */ + 'i' => 'integer', + 'f' => 'float', + + 'b' => 'boolean', + 'a' => 'array', + 'o' => 'object' + ); + + /* annoyance due to PHP scope issue */ + static function cast ($args) { + $parser = new self(); + return (string) $parser->castString(func_get_args()); + } + + static function cast_string ($in) { + return sf("'%j'",$in); + } + + static function cast_integer ($in) { + return intval($in); + } + static function cast_float ($in) { + return floatval($in); + } + static function cast_number ($in) { + return ($in); + } + static function cast_boolean ($in) { + return boolval($in); + } + + static function cast_array ($in) { + return json_encode($in); + } + + static function cast_object ($in) { + return json_encode($in); + } + +} + +?> \ No newline at end of file From c93b9678da762db3abe9ba2f311db977390f48df Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 27 May 2014 12:51:25 +0000 Subject: [PATCH 119/164] added basic PHP caster. atsumi PHP accepts a single none-array $fields value which posts as the whole post body. Also accepts an optional contentType. --- classes/caster/caster_Php.php | 58 ++++++++++++++++++++++++++++ classes/utility/http/atsumi_Http.php | 11 +++++- 2 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 classes/caster/caster_Php.php diff --git a/classes/caster/caster_Php.php b/classes/caster/caster_Php.php new file mode 100644 index 0000000..4fd0c50 --- /dev/null +++ b/classes/caster/caster_Php.php @@ -0,0 +1,58 @@ + 'string', + 'n' => 'number', + + /* these aren't real JSON types, but can validate on them */ + 'i' => 'integer', + 'f' => 'float', + + 'b' => 'boolean', + + 'a' => 'array', + 'o' => 'object' + ); + + + /* returns a value */ + static function value ($format, $value) { + + $parser = new self(); + + $methodName = 'cast_'.$parser->spec[$format]; + return $parser->$methodName($value); + + } + + static function cast_string ($in) { + return strval($in); + } + + static function cast_integer ($in) { + return intval($in); + } + static function cast_float ($in) { + return floatval($in); + } + static function cast_number ($in) { + return ($in); + } + static function cast_boolean ($in) { + return boolval($in); + } + + static function cast_array ($in) { + return $in; + } + + static function cast_object ($in) { + return $in; + } + +} + +?> \ No newline at end of file diff --git a/classes/utility/http/atsumi_Http.php b/classes/utility/http/atsumi_Http.php index 5bc92d8..8a7d598 100644 --- a/classes/utility/http/atsumi_Http.php +++ b/classes/utility/http/atsumi_Http.php @@ -63,7 +63,7 @@ class atsumi_Http { const POST_METHOD_CURL = 1; const POST_METHOD_PECL = 2; - static public function post ($url, $fields, $method = 1) { + static public function post ($url, $fields, $method = 1, $contentType = null) { switch ($method) { @@ -73,7 +73,7 @@ static public function post ($url, $fields, $method = 1) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POST, count($fields)); - curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($fields)); + curl_setopt($ch, CURLOPT_POSTFIELDS, is_array($fields)?http_build_query($fields):$fields); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE); curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); curl_setopt($ch, CURLOPT_HEADER, true); @@ -82,6 +82,13 @@ static public function post ($url, $fields, $method = 1) { curl_setopt($ch, CURLOPT_MAXREDIRS, 10); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 120); curl_setopt($ch, CURLOPT_TIMEOUT, 120); + + if ($contentType) + curl_setopt($ch, CURLOPT_HTTPHEADER, array( + 'Content-Type: '. $contentType, + 'Connection: Keep-Alive' + )); + $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); From 9da3e92c8500038b94e67c2aa3916b4e0f03a648 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Thu, 29 May 2014 06:01:27 -0700 Subject: [PATCH 120/164] throws a useful error on curl failing --- classes/utility/http/atsumi_Http.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/classes/utility/http/atsumi_Http.php b/classes/utility/http/atsumi_Http.php index 8a7d598..ad70910 100644 --- a/classes/utility/http/atsumi_Http.php +++ b/classes/utility/http/atsumi_Http.php @@ -90,8 +90,12 @@ static public function post ($url, $fields, $method = 1, $contentType = null) { )); $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if ($httpCode == 0) + throw new Exception ('CURL error: '.curl_errno($ch)); + $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); $header = substr($response, 0, $headerSize); $body = substr($response, $headerSize); From ed8f043ffdf638030366b9e10fe0462bc8f1df89 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Thu, 29 May 2014 06:03:56 -0700 Subject: [PATCH 121/164] Added a DynamicModel which is extends AbstractModel, allows to dynamically create the model at runtime. Added a few extras to Abstract Model. --- classes/mvc/models/mvc_AbstractModel.php | 12 +++++++-- classes/mvc/models/mvc_DynamicModel.php | 32 ++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 classes/mvc/models/mvc_DynamicModel.php diff --git a/classes/mvc/models/mvc_AbstractModel.php b/classes/mvc/models/mvc_AbstractModel.php index 0f643a0..e09e2bd 100644 --- a/classes/mvc/models/mvc_AbstractModel.php +++ b/classes/mvc/models/mvc_AbstractModel.php @@ -36,7 +36,7 @@ function preOutput($outputType) { } /* generic */ function has ($k) { - return array_key_exists($k, $this->data); + return array_key_exists($k, $this->data) && isset($this->data[$k]); } /* generic */ @@ -92,7 +92,11 @@ function increment ($k, $v) { } } - + + function __get ($key) { + return $this->get($key); + } + /* generic */ function get($key, $strict = true) { if (!array_key_exists($key, $this->data)) @@ -129,6 +133,10 @@ static function outputItem ($value, $type) { } + function __toString () { + return pretty($this->output()); + } + function output ($type = self::OUTPUT_FORMAT_ASSOC) { $this->preOutput($type); diff --git a/classes/mvc/models/mvc_DynamicModel.php b/classes/mvc/models/mvc_DynamicModel.php new file mode 100644 index 0000000..5eb727c --- /dev/null +++ b/classes/mvc/models/mvc_DynamicModel.php @@ -0,0 +1,32 @@ + $properties) { + $this->add($k, $properties); + } + } + + public function add ($key, $properties) { + + $this->structure[$key] = array( + 'type' => $properties['type'], + 'default' => isset($properties['default'])?$properties['default']:null + ); + + if (isset($properties['value'])) + $this->set($key, $properties['value']); + + } + + // is defined? + public function exists ($key) { + return array_key_exists($key, $this->structure); + } + + +} +?> \ No newline at end of file From 929950ecdb4ff7b6ef2a88655d409644209f27ce Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Fri, 30 May 2014 09:37:33 -0700 Subject: [PATCH 122/164] minor modifications --- classes/caster/caster_Json.php | 7 ++++++- classes/mvc/models/mvc_AbstractModel.php | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/classes/caster/caster_Json.php b/classes/caster/caster_Json.php index 99f2709..9426c4e 100644 --- a/classes/caster/caster_Json.php +++ b/classes/caster/caster_Json.php @@ -13,7 +13,9 @@ class caster_Json extends caster_Abstract { 'b' => 'boolean', 'a' => 'array', - 'o' => 'object' + 'o' => 'object', + + 't' => 'timestamp' ); /* annoyance due to PHP scope issue */ @@ -42,6 +44,9 @@ static function cast_boolean ($in) { static function cast_array ($in) { return json_encode($in); } + static function cast_timestamp ($in) { + return intval($in); + } static function cast_object ($in) { return json_encode($in); diff --git a/classes/mvc/models/mvc_AbstractModel.php b/classes/mvc/models/mvc_AbstractModel.php index e09e2bd..18e7d78 100644 --- a/classes/mvc/models/mvc_AbstractModel.php +++ b/classes/mvc/models/mvc_AbstractModel.php @@ -93,6 +93,10 @@ function increment ($k, $v) { } } + function getStructure () { + return $this->structure; + } + function __get ($key) { return $this->get($key); } From 2251ea0675369d6bc9a58f8fa285a6056b66942a Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Wed, 4 Jun 2014 06:15:50 -0700 Subject: [PATCH 123/164] added uri parser Matcha, adds localisation handling to the Gyokuro parser. Added a LocaleModel. --- classes/core/app/parser/uriparser_Matcha.php | 80 ++++++++++++++++++++ classes/mvc/models/core/mvc_LocaleModel.php | 39 ++++++++++ 2 files changed, 119 insertions(+) create mode 100644 classes/core/app/parser/uriparser_Matcha.php create mode 100644 classes/mvc/models/core/mvc_LocaleModel.php diff --git a/classes/core/app/parser/uriparser_Matcha.php b/classes/core/app/parser/uriparser_Matcha.php new file mode 100644 index 0000000..9490682 --- /dev/null +++ b/classes/core/app/parser/uriparser_Matcha.php @@ -0,0 +1,80 @@ +array('gb'), + 'fr'=>array() + ) + ) + ); + + This would accept: + + - en + - en-gb + - fr + + there is a locale array available in the meta + data that contains language & country + +*/ + +class uriparser_Matcha extends uriparser_Gyokuro { + + + private $locale = null; + private $supportedLanguages = array(); + + // you can optionally pass in the supported languages array + // this will prevent matching none supported 2 letter pages + public function __construct ($supportedLanguages = array()) { + $this->supportedLanguages = $supportedLanguages; + $this->locale = new mvc_LocaleModel(); + } + + + public function parseUri($uri, $specification) { + + + preg_match('@^/([a-z]{2})(-([a-z]{2})){0,1}/@', $uri, $localeMatch, PREG_OFFSET_CAPTURE); + + if (array_key_exists(0,$localeMatch) && ( + + // check that it's in the allowed languages + !count($this->supportedLanguages) || + in_array( + $localeMatch[1][0], + array_keys($this->supportedLanguages) + ) + + )) { + + $this->locale->set('language', $localeMatch[1][0]); + + if (array_key_exists(3,$localeMatch) && ( + + // check it's an allowed country for current language + !count($this->supportedLanguages) || + in_array( + $localeMatch[3][0], + $this->supportedLanguages[$this->locale->get('language')] + ) + )) { + $this->locale->set('country', $localeMatch[3][0]); + } + + $uri = substr($uri, strlen($localeMatch[0][0])-1); + } + + $output = parent::parseUri($uri, $specification); + $output['meta']['locale'] = $this->locale; + + return $output; + } +} +?> \ No newline at end of file diff --git a/classes/mvc/models/core/mvc_LocaleModel.php b/classes/mvc/models/core/mvc_LocaleModel.php new file mode 100644 index 0000000..5b599a2 --- /dev/null +++ b/classes/mvc/models/core/mvc_LocaleModel.php @@ -0,0 +1,39 @@ + array( + 'type' => 's', + ), + 'country' => array( + 'type' => 's', + ) + ); + + function getLocaleString () { + return sf('%s%s', + strtolower($this->get('language')), + $this->has('country')? + sf('_%s', strtoupper($this->has('country'))):'' + ); + } + + function uri ($uri) { return sf('/%s%s%s', + $this->getUriPrefix(), + substr($uri, 0, 1) !== '/'?'/':'', + $uri + ); + } + function getUriPrefix () { + return sf('%s%s', + strtolower($this->get('language')), + $this->has('country')? + sf('-%s', strtolower($this->has('country'))):'' + ); + } + +} + +?> \ No newline at end of file From 2c9a901811b9972377e377c50f025386435f35c7 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Wed, 4 Jun 2014 06:16:39 -0700 Subject: [PATCH 124/164] couple of bux fixes to abstract model. --- classes/mvc/models/mvc_AbstractModel.php | 29 ++++++++++++++++-------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/classes/mvc/models/mvc_AbstractModel.php b/classes/mvc/models/mvc_AbstractModel.php index 18e7d78..99c4076 100644 --- a/classes/mvc/models/mvc_AbstractModel.php +++ b/classes/mvc/models/mvc_AbstractModel.php @@ -103,10 +103,13 @@ function __get ($key) { /* generic */ function get($key, $strict = true) { - if (!array_key_exists($key, $this->data)) + if (!array_key_exists($key, $this->data) && !array_key_exists('default', $this->structure[$key])) throw new Exception ('Unknown model key: '. $key); - - return $this->data[$key]; + + if (!array_key_exists($key, $this->data)) + return $this->structure[$key]['default']; + + else return $this->data[$key]; /* try { $value = caster_PostgreSqlToPhp::cast(sf('%%%s', $this->structure[$key]['type']), $this->data[$key]); @@ -149,32 +152,38 @@ function output ($type = self::OUTPUT_FORMAT_ASSOC) { // Native object case self::OUTPUT_FORMAT_OBJECT: + + foreach($this->structure as $key => $properties) { + if (isset($this->structure[$key]['output']) && + $this->structure[$key]['output'] == false) + continue; + + self::outputItem($this->get($key), $type); + } + return $this; // Associative array case self::OUTPUT_FORMAT_ASSOC: $out = array(); - foreach($this->data as $key => $value) { + foreach($this->structure as $key => $properties) { if (isset($this->structure[$key]['output']) && $this->structure[$key]['output'] == false) continue; - $out[$key] = self::outputItem($value, $type); + $out[$key] = self::outputItem($this->get($key), $type); } return $out; // Std Class case self::OUTPUT_FORMAT_STD_CLASS: $out = new stdClass(); - foreach($this->data as $key => $value) { + foreach($this->structure as $key => $properties) { if (isset($this->structure[$key]['output']) && $this->structure[$key]['output'] == false) continue; - if ($value instanceof mvc_AbstractModel) - $out[$key] = $value->output($type); - else - $out[$key] = $value; + $out[$key] = self::outputItem($this->get($key), $type); } return $out; } From b2879024229a3eff4a407434c09b2768a0d6d915 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Wed, 4 Jun 2014 06:18:40 -0700 Subject: [PATCH 125/164] fixed timers in atsumi Debug, startTimer and endTimer now accepts a reference. This class needs rewriting soon, it could be improved significantly. --- classes/core/app/atsumi_AppHandler.php | 45 +++++++------- classes/core/debug/atsumi_Debug.php | 61 +++++++++++++++---- .../controllers/mvc_AbstractController.php | 25 +++++--- 3 files changed, 90 insertions(+), 41 deletions(-) diff --git a/classes/core/app/atsumi_AppHandler.php b/classes/core/app/atsumi_AppHandler.php index fedfed2..87d3ea3 100644 --- a/classes/core/app/atsumi_AppHandler.php +++ b/classes/core/app/atsumi_AppHandler.php @@ -228,7 +228,7 @@ public function go($uri) { * @access public */ public function parseUri() { - atsumi_Debug::startTimer(); + atsumi_Debug::startTimer('app:parse:uri'); if(!in_array('uriparser_Interface', class_implements($this->uriParser))) throw new Exception('URI Parser must implement uriparser_Interface'); @@ -249,11 +249,13 @@ public function parseUri() { atsumi_Debug::record('Uri Parsing', 'Uri was parsed to determine the controller, method and args.', - array_merge(array('path' => $this->uri), $this->parserData), true); + array_merge(array('path' => $this->uri), $this->parserData), + 'app:parse:uri' + ); } public function parseCommand() { - atsumi_Debug::startTimer(); + atsumi_Debug::startTimer('app:parse:command'); if(!in_array('claparser_Interface', class_implements($this->claParser))) { throw new Exception('Command Line Argument parser must implement claparser_Interface'); } @@ -271,7 +273,9 @@ public function parseCommand() { atsumi_Debug::setParserData($parseData); atsumi_Debug::record('Command Parsing', 'Command was parsed to determine the controller, method and args.', - array_merge(array('path' => $this->command), $this->parserData), true); + array_merge(array('path' => $this->command), $this->parserData), + 'app:parse:command' + ); } @@ -295,29 +299,28 @@ public function process() { throw new app_PageNotFoundException(); // Get the debugger and start a timer for processing - atsumi_Debug::startTimer(); + atsumi_Debug::startTimer('app:controller:processing'); // Add the method to the list of processed methods $this->controller->addProcessedMethod($this->parserData['method'], $this->parserData['args']); // Time and execute the pre process - atsumi_Debug::startTimer(); + atsumi_Debug::startTimer('app:controller:preProcess'); $this->controller->preProcess(); - atsumi_Debug::record('Controller PreProcess', 'Before the controllers method was called the pre-process function was executed', null, true); + atsumi_Debug::record('Controller PreProcess', 'Before the controllers method was called the pre-process function was executed', null, 'app:controller:preProcess'); // Time and execute the controllers method - atsumi_Debug::startTimer(); - + atsumi_Debug::startTimer('app:controller:method'); $this->controller->processRequest($this->parserData['method'], $this->parserData['args']); - atsumi_Debug::record('Controller Method', 'The controllers requested method was executed', null, true); + atsumi_Debug::record('Controller Method', 'The controllers requested method was executed', null, 'app:controller:method'); // Time and execute the post process - atsumi_Debug::startTimer(); + atsumi_Debug::startTimer('app:controller:postProcess'); $this->controller->postProcess(); - atsumi_Debug::record('Controller PostProcess', 'After the controllers method was called the post-process function was executed', null, true); + atsumi_Debug::record('Controller PostProcess', 'After the controllers method was called the post-process function was executed', null, 'app:controller:postProcess'); // Log the whole processing time - atsumi_Debug::record('Controller Processing Complete', 'All processing was completed successfully', null, true); + atsumi_Debug::record('Controller Processing Complete', 'All processing was completed successfully', null, 'app:controller:processing'); } /** @@ -328,10 +331,10 @@ public function process() { public function render() { // Time and execute the pre render - atsumi_Debug::startTimer(); + atsumi_Debug::startTimer('app:controller:preRender'); $this->controller->publishFlashData(); $this->controller->preRender(); - atsumi_Debug::record('Controller PreRender', 'Before rendering was processed the pre-render function was executed', null, true); + atsumi_Debug::record('Controller PreRender', 'Before rendering was processed the pre-render function was executed', null, 'app:controller:preRender'); $viewHandler = $this->controller->getViewHandler(); $view = $this->controller->getView(); @@ -348,24 +351,24 @@ public function render() { // Get the debugger and start a timer for rendering - atsumi_Debug::startTimer(); + atsumi_Debug::startTimer('app:controller:rendering'); $viewData = $this->controller->getViewData(); atsumi_Debug::setViewData($viewData); // Time and execute the view handler - atsumi_Debug::startTimer(); + atsumi_Debug::startTimer('app:controller:render'); $viewHandler->render($view, $viewData); - atsumi_Debug::record('Rendering', sf('Rendering was performed by the %s view handler', get_class($viewHandler)), null, true); + atsumi_Debug::record('Rendering', sf('Rendering was performed by the %s view handler', get_class($viewHandler)), null, 'app:controller:render'); // Time and execute the post render - atsumi_Debug::startTimer(); + atsumi_Debug::startTimer('app:controller:postRender'); $this->controller->postRender(); - atsumi_Debug::record('Controller PostRender', 'After the rendering was processed the post-render function was executed', null, true); + atsumi_Debug::record('Controller PostRender', 'After the rendering was processed the post-render function was executed', null, 'app:controller:postRender'); // Log the whole processing time - atsumi_Debug::record('Rendering Complete', 'All rendering was completed successfully', null, true); + atsumi_Debug::record('Rendering Complete', 'All rendering was completed successfully', null, 'app:controller:rendering'); } } ?> diff --git a/classes/core/debug/atsumi_Debug.php b/classes/core/debug/atsumi_Debug.php index f8f8f8a..3bd5d71 100644 --- a/classes/core/debug/atsumi_Debug.php +++ b/classes/core/debug/atsumi_Debug.php @@ -99,6 +99,7 @@ class atsumi_Debug { * @var array */ protected $timers = array(); + protected $timerMap = array(); /** * Weather the debugger is active and recording information @@ -302,8 +303,11 @@ public function _addDatabase( /*db_InterfaceDatabase */ $database) { * Adds a timer point onto a stack of timers * @access public */ - public function _startTimer() { - $this->timers[] = microtime(true); + public function _startTimer($key = null) { + if (!is_null($key)) + $this->timerMap[$key] = microtime(true); + else + $this->timers[] = microtime(true); } /** @@ -311,7 +315,15 @@ public function _startTimer() { * @access public * @return string */ - public function _endTimer() { + public function _endTimer($key = null) { + + if (!is_null($key)) { + if (!isset($this->timerMap[$key])) return '??'; + $ms = round((microtime(true) - $this->timerMap[$key]), 3); + unset($this->timerMap[$key]); + return $ms; + } + $startTime = array_pop($this->timers); return round((microtime(true) - $startTime), 3).' microseconds'; } @@ -330,12 +342,21 @@ public function _endTimer() { public function _record($title, $desc, $data = null, $timer = false, $area = self::AREA_GENERAL) { if(!$this->active) return; + $timerString = ''; + if ($timer === true) + $timerString = '(Process Time: '.self::_endTimer().')'; + else if (is_string($timer)) { + $timerString = '(Process Time: '.self::_endTimer($timer).' ms)'; + } + + + $this->consoleData[] = array( 'title' => $title, 'desc' => $desc, 'data' => $data, 'area' => $area, - 'timestamp' =>($timer ? '(Process Time: '.self::_endTimer().')' : '') + 'timestamp' =>$timerString ); } @@ -394,8 +415,18 @@ protected function format($value) { return $ret; } - if(is_object($value)) { - return sf('(Class) %s', get_class($value)); + if(is_object($value) && $value instanceof mvc_AbstractModel) { + + $objOutput = ''; + $objArray = $value->getStructure(); + foreach($objArray as $key => $item) + $objOutput .= sf('
[%s] => %s
', $key, stripos($key,'password')?'*****':$this->format($value->get($key))); + + + return sf('(Model) %s:%s', get_class($value), $objOutput); + + } elseif(is_object($value)) { + return sf('(Object) %s', get_class($value)); /* if(method_exists($value,'toString')) return $value->toString(); @@ -706,8 +737,12 @@ public function _render() {
-consoleData as $data) : ?> -
+consoleData as $data) : + $color = dechex(crc32($data['area'])); + $color = '#'.substr($color, 0, 6); + + ?> +
%s
', $this->format($data['data'])) : '');?> @@ -781,7 +816,7 @@ public function _render() { $totalTime += $query['time'] ?>

Database

-
Total Query time:
+
Total Query time: ms
format($database->getQueryTimes()));?>
@@ -852,12 +887,12 @@ public static function addDatabase($database) { self::__callStatic(__FUNCTION__, $args); } - public static function startTimer() { - self::__callStatic(__FUNCTION__, array()); + public static function startTimer($key = null) { + self::__callStatic(__FUNCTION__, func_get_args()); } - public static function endTimer() { - return self::__callStatic(__FUNCTION__, array()); + public static function endTimer($key = null) { + return self::__callStatic(__FUNCTION__, func_get_args()); } public static function record($title, $desc, $data = null, $timer = false, $area = self::AREA_GENERAL) { diff --git a/classes/mvc/controllers/mvc_AbstractController.php b/classes/mvc/controllers/mvc_AbstractController.php index e98a710..146c3ed 100644 --- a/classes/mvc/controllers/mvc_AbstractController.php +++ b/classes/mvc/controllers/mvc_AbstractController.php @@ -22,6 +22,9 @@ public function get($key) { return $this->data[$key]; } + function __get ($key) { + return $this->get($key); + } public function getViewHandler() { return $this->viewHandler; } @@ -179,16 +182,24 @@ public function getData($method = null, $controller = null) { // TODO: This only works for page_ not other methods.... - $method = is_null($method)?substr($entity['method'][count($entity['method'])-1]['name'],5):$method; + // is method is an index? + if (is_integer($method)) + $method = $method = substr($entity['method'][count($entity['method'])-1-$method]['name'],5); + + // is it null? + else if (is_null($method)) + $method = substr($entity['method'][count($entity['method'])-1]['name'],5); + $methodName = sf('data_%s',$method); $pageMethod = null; - // loop through methods to find the corect one... - foreach($entity['method'] as $methodData) - if($methodData['name'] == sf('page_%s',$method)) { - $pageMethod = $methodData; - break; - } + + // loop through methods to find the correct one... + foreach($entity['method'] as $methodData) + if($methodData['name'] == sf('page_%s',$method)) { + $pageMethod = $methodData; + break; + } // TODO: Give this it's own exceptions! if(is_null($methodData)) throw new Exception("Method not found..."); From 5002287c29b3af7ea8fbecb8e0f1a9dbf8ba9f77 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 9 Jun 2014 05:40:07 -0700 Subject: [PATCH 126/164] added a http accepts language parser to locale model --- classes/mvc/models/core/mvc_LocaleModel.php | 27 +++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/classes/mvc/models/core/mvc_LocaleModel.php b/classes/mvc/models/core/mvc_LocaleModel.php index 5b599a2..35b42b5 100644 --- a/classes/mvc/models/core/mvc_LocaleModel.php +++ b/classes/mvc/models/core/mvc_LocaleModel.php @@ -34,6 +34,33 @@ function getUriPrefix () { ); } + static function parseHttpAcceptLanguage ($httpAcceptLanguage) { + + $langs = array(); + + // break up string into pieces (languages and q factors) + preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', + $httpAcceptLanguage, + $langMatches + ); + + if (count($langMatches[1])) { + // create a list like "en" => 0.8 + $langs = array_combine($langMatches[1], $langMatches[4]); + + // set default to 1 for any without q factor + foreach ($langs as $lang => $val) { + if ($val === '') $langs[$lang] = 1; + } + + // sort list based on value + arsort($langs, SORT_NUMERIC); + $langs = array_change_key_case($langs, CASE_LOWER); + } + + return $langs; + } + } ?> \ No newline at end of file From f415cbf47acc57a5866b0d458adfb5c94e1634c6 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 9 Jun 2014 05:40:28 -0700 Subject: [PATCH 127/164] added a setArray method to abstract model --- classes/mvc/models/mvc_AbstractModel.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/classes/mvc/models/mvc_AbstractModel.php b/classes/mvc/models/mvc_AbstractModel.php index 99c4076..7e2d79e 100644 --- a/classes/mvc/models/mvc_AbstractModel.php +++ b/classes/mvc/models/mvc_AbstractModel.php @@ -52,6 +52,11 @@ function set ($k, $v, $force = false) { $this->data[$k] = $v; } + function setArray ($asoc) { + foreach ($asoc as $k => $v) + $this->set($k, $v); + } + function increment ($k, $v) { if (!$this->has($k)) throw new Exception('unknown key: '.$k); From 20ca0dcdb98a7485d79f87a58d447e3908ca01c3 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 9 Jun 2014 05:41:14 -0700 Subject: [PATCH 128/164] added a hook to send options to the render method --- classes/widgets/form/widget_Form.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/classes/widgets/form/widget_Form.php b/classes/widgets/form/widget_Form.php index 2db8962..9ad1b71 100644 --- a/classes/widgets/form/widget_Form.php +++ b/classes/widgets/form/widget_Form.php @@ -222,7 +222,7 @@ public function __toString() { return strval($this->render()); } - public function render() { + public function render($options = array()) { // totally forgotten what this force defaults is, do we need it?... // forceDefaults is if you want to force the form to show its default values on reload @@ -233,13 +233,13 @@ public function render() { $row->validate(); } - $html = $this->getFormTop(); + $html = $this->getFormTop(isset($options['top'])?$options['top']:array()); foreach($this->elements as $element) { $html .= sfl("%s", $element->render()); } - $html .= $this->getFormBottom(); + $html .= $this->getFormBottom(isset($options['bottom'])?$options['bottom']:array()); return $html; } From fa0034c776fdc6913afcc8c099f6c248f1babb1e Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 10 Jun 2014 05:46:13 -0700 Subject: [PATCH 129/164] added a few additional features to LocaleModel - fixed minor bug --- classes/mvc/models/core/mvc_LocaleModel.php | 33 +++++++++++++-------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/classes/mvc/models/core/mvc_LocaleModel.php b/classes/mvc/models/core/mvc_LocaleModel.php index 35b42b5..c95859d 100644 --- a/classes/mvc/models/core/mvc_LocaleModel.php +++ b/classes/mvc/models/core/mvc_LocaleModel.php @@ -16,24 +16,33 @@ function getLocaleString () { return sf('%s%s', strtolower($this->get('language')), $this->has('country')? - sf('_%s', strtoupper($this->has('country'))):'' + sf('-%s', strtolower($this->get('country'))):'' ); } - function uri ($uri) { return sf('/%s%s%s', - $this->getUriPrefix(), - substr($uri, 0, 1) !== '/'?'/':'', - $uri - ); - } - function getUriPrefix () { - return sf('%s%s', - strtolower($this->get('language')), - $this->has('country')? - sf('-%s', strtolower($this->has('country'))):'' + function uri ($uri) { + return sf('/%s%s%s', + $this->getLocaleString(), + substr($uri, 0, 1) !== '/'?'/':'', + $uri ); } + static public function fromLocaleString ($localeString) { + $locale = new self(); + preg_match('/([a-z]{2})(-([a-zA-Z]{2}))*/i', + $localeString, + $matches + ); + if (array_key_exists(1, $matches)) + $locale->set('language', strtolower($matches[1])); + + if (array_key_exists(3, $matches)) + $locale->set('country', strtolower($matches[3])); + + return $locale; + + } static function parseHttpAcceptLanguage ($httpAcceptLanguage) { $langs = array(); From f365fbb7cd6318076e5cd4edb0d2859c7c92d854 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 10 Jun 2014 05:46:39 -0700 Subject: [PATCH 130/164] added a $httpHeaders parameter that is set directly on the post request --- classes/utility/http/atsumi_Http.php | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/classes/utility/http/atsumi_Http.php b/classes/utility/http/atsumi_Http.php index ad70910..dc08dd8 100644 --- a/classes/utility/http/atsumi_Http.php +++ b/classes/utility/http/atsumi_Http.php @@ -63,7 +63,7 @@ class atsumi_Http { const POST_METHOD_CURL = 1; const POST_METHOD_PECL = 2; - static public function post ($url, $fields, $method = 1, $contentType = null) { + static public function post ($url, $fields, $method = 1, $httpHeaders = array()) { switch ($method) { @@ -83,11 +83,8 @@ static public function post ($url, $fields, $method = 1, $contentType = null) { curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 120); curl_setopt($ch, CURLOPT_TIMEOUT, 120); - if ($contentType) - curl_setopt($ch, CURLOPT_HTTPHEADER, array( - 'Content-Type: '. $contentType, - 'Connection: Keep-Alive' - )); + if (count($httpHeaders)) + curl_setopt($ch, CURLOPT_HTTPHEADER, $httpHeaders); $response = curl_exec($ch); From 33830359d1beecf317d100bd3f45bac6fe5d0837 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 23 Jun 2014 07:29:26 -0700 Subject: [PATCH 131/164] added total db query time --- classes/core/debug/atsumi_Debug.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/classes/core/debug/atsumi_Debug.php b/classes/core/debug/atsumi_Debug.php index 3bd5d71..a913806 100644 --- a/classes/core/debug/atsumi_Debug.php +++ b/classes/core/debug/atsumi_Debug.php @@ -812,11 +812,12 @@ public function _render() { databases as $key => $database) : $totalTime = 0; - foreach($database->getQueryTimes() as $query) - $totalTime += $query['time'] + foreach($database->getQueryTimes() as $query) { + $totalTime += $query['time']; + } ?> -

Database

-
Total Query time: ms
+

Database #

+
xgetQueryTimes())?> queries. Time: ms
format($database->getQueryTimes()));?>
From fce73dd1abb61f3d1654a4d0c68472724fdb8e12 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 8 Jul 2014 06:35:19 -0700 Subject: [PATCH 132/164] added timestamp to php caster, abstract model now looks for output() methods on objects. date time now has an output method --- classes/caster/caster_Php.php | 5 +++++ classes/mvc/models/mvc_AbstractModel.php | 4 ++++ classes/utility/calendar/atsumi_DateTime.php | 5 ++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/classes/caster/caster_Php.php b/classes/caster/caster_Php.php index 4fd0c50..bf1e0d3 100644 --- a/classes/caster/caster_Php.php +++ b/classes/caster/caster_Php.php @@ -13,6 +13,8 @@ class caster_Php extends caster_Abstract { 'b' => 'boolean', + 't' => 'timestamp', + 'a' => 'array', 'o' => 'object' ); @@ -44,6 +46,9 @@ static function cast_number ($in) { static function cast_boolean ($in) { return boolval($in); } + static function cast_timestamp ($in) { + return intval($in); + } static function cast_array ($in) { return $in; diff --git a/classes/mvc/models/mvc_AbstractModel.php b/classes/mvc/models/mvc_AbstractModel.php index 7e2d79e..121d6f3 100644 --- a/classes/mvc/models/mvc_AbstractModel.php +++ b/classes/mvc/models/mvc_AbstractModel.php @@ -41,6 +41,7 @@ function has ($k) { /* generic */ function set ($k, $v, $force = false) { + if (!$force && ( isset($this->structure[$k]['write']) && @@ -132,6 +133,9 @@ static function outputItem ($value, $type) { if ($value instanceof mvc_AbstractModel) return $value->output($type); + elseif (is_object($value) && method_exists($value, 'output')) + return $value->output($type); + // if array itterate through elseif (is_array($value)) { $arrayOut = array(); diff --git a/classes/utility/calendar/atsumi_DateTime.php b/classes/utility/calendar/atsumi_DateTime.php index f33b63e..dbceab8 100644 --- a/classes/utility/calendar/atsumi_DateTime.php +++ b/classes/utility/calendar/atsumi_DateTime.php @@ -17,7 +17,7 @@ public function __construct ($timestamp) { } public function __toString() { - return (String)$this->timestamp; + return (String) $this->timestamp; } public function format($formatString = null) { @@ -29,5 +29,8 @@ static public function formatFromTimestamp ($timestamp, $formatString = null) { return $d->format($formatString); } + public function output ($outputType) { + return (String) $this->timestamp; + } } ?> \ No newline at end of file From 7ebb1406753d78fd7817fb81bb95ec7baaca2396 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 28 Jul 2014 09:07:33 -0700 Subject: [PATCH 133/164] added minor debug to template handler --- classes/mvc/view_handlers/mvc_PhpTemplateViewHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/mvc/view_handlers/mvc_PhpTemplateViewHandler.php b/classes/mvc/view_handlers/mvc_PhpTemplateViewHandler.php index cf354b0..364b442 100644 --- a/classes/mvc/view_handlers/mvc_PhpTemplateViewHandler.php +++ b/classes/mvc/view_handlers/mvc_PhpTemplateViewHandler.php @@ -138,7 +138,7 @@ public function processTemplate ($template, $data = array(), $incViewData = fals return self::processTemplateString($templateString, $data, $this->surpressErrors); } else { - throw new Exception ('Unknown template type'); + throw new Exception ('Unknown template type: '.$template); } } From 4177fb6eccd1f9bfa4c6ca2cdd7a9ff8e65df974 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Thu, 31 Jul 2014 04:20:21 -0700 Subject: [PATCH 134/164] added more useful exception --- classes/mvc/controllers/mvc_AbstractController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/mvc/controllers/mvc_AbstractController.php b/classes/mvc/controllers/mvc_AbstractController.php index 146c3ed..07d9e03 100644 --- a/classes/mvc/controllers/mvc_AbstractController.php +++ b/classes/mvc/controllers/mvc_AbstractController.php @@ -18,7 +18,7 @@ public function has($key) { public function get($key) { if(!array_key_exists($key, $this->data)) - throw new Exception("Item does not exist in view data.."); + throw new Exception(sf("'%s' does not exist in view data.",$key)); return $this->data[$key]; } From ee12f6a2af38bf8c1386b74c54a3c7d614df59fc Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Thu, 11 Sep 2014 08:49:39 -0700 Subject: [PATCH 135/164] added generic duplicate exception --- classes/core/error/exceptions/exception_Duplicate.php | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 classes/core/error/exceptions/exception_Duplicate.php diff --git a/classes/core/error/exceptions/exception_Duplicate.php b/classes/core/error/exceptions/exception_Duplicate.php new file mode 100644 index 0000000..1d4bd0a --- /dev/null +++ b/classes/core/error/exceptions/exception_Duplicate.php @@ -0,0 +1,3 @@ + \ No newline at end of file From 96aaefd94bfb289507650b7a25d90ec3e89126eb Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 15 Sep 2014 09:19:18 -0700 Subject: [PATCH 136/164] Added Atsumi::store__add($k, $v) and Atsumi::store__get($k) - useful if data needs to be available globally (although usually not the best idea!) --- classes/core/Atsumi.php | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/classes/core/Atsumi.php b/classes/core/Atsumi.php index 20b7336..c6db07d 100644 --- a/classes/core/Atsumi.php +++ b/classes/core/Atsumi.php @@ -41,6 +41,14 @@ class Atsumi { */ private $errorHandler; + + /** + * A data store + * @access private + * @var mvc_DynamicModel + */ + private $store; + /* CONSTRUCTOR & DESTRUCTOR */ /** @@ -54,6 +62,8 @@ private function __construct() { // Load some helpful files atsumi_Loader::references(atsumi_Loader::getAtsumiDir(), 'caster utility/http'); + + } /* GET METHODS */ @@ -103,6 +113,9 @@ public static function __callStatic($name, $arguments) { if(preg_match('/^error__(.+)$/', $name, $matches)) return call_user_func_array(array($atsumi->errorHandler, $matches[1]), $arguments); + if(preg_match('/^store__(.+)$/', $name, $matches)) + return call_user_func_array(array($atsumi->store, $matches[1]), $arguments); + if(method_exists($atsumi, '_'.$name)) return call_user_func_array(array($atsumi, '_'.$name), $arguments); @@ -142,9 +155,16 @@ public function _initApp(atsumi_AbstractAppSettings $settings) { $this->errorHandler->setDisplayErrors(false); } - $this->appHandler = new atsumi_AppHandler($settings, $this->errorHandler); + + $this->appHandler = new atsumi_AppHandler($settings, $this->errorHandler); + } + public function _initStore () { + if (class_exists('mvc_DynamicModel')) + $this->store = new mvc_DynamicModel(); + } + /* * NOTE: Below are all static functions which will be removed when php 5.3.0+ becomes the * common and the PHP magic function __callStatic works correctly @@ -159,6 +179,11 @@ public static function initApp($settings, $debug = false) { return self::__callStatic(__FUNCTION__, $args); } + public static function initStore() { + $args = func_get_args(); + return self::__callStatic(__FUNCTION__, $args); + } + public static function app__setUriParser($parser) { $args = func_get_args(); return self::__callStatic(__FUNCTION__, $args); @@ -199,6 +224,15 @@ public static function app__createUri($controller, $method) { return self::__callStatic(__FUNCTION__, $args); } + public static function store__set($k, $v) { + $args = func_get_args(); + return self::__callStatic(__FUNCTION__, $args); + } + public static function store__get($k) { + $args = func_get_args(); + return self::__callStatic(__FUNCTION__, $args); + } + public static function error__setRecoverer($recoverer) { $args = func_get_args(); return self::__callStatic(__FUNCTION__, $args); From d5980dac725ac3c8ec8c22f9ffe1a6b27e9c12e3 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 22 Sep 2014 09:47:45 -0700 Subject: [PATCH 137/164] added interpolation to the mvc_LocaleModel - might not be the best place for it though. --- classes/mvc/models/core/mvc_LocaleModel.php | 29 +++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/classes/mvc/models/core/mvc_LocaleModel.php b/classes/mvc/models/core/mvc_LocaleModel.php index c95859d..9825201 100644 --- a/classes/mvc/models/core/mvc_LocaleModel.php +++ b/classes/mvc/models/core/mvc_LocaleModel.php @@ -69,6 +69,35 @@ static function parseHttpAcceptLanguage ($httpAcceptLanguage) { return $langs; } + static function interpolate ($str, $vars) { + $originalStr = $str; + while (preg_match('/\$\{(.*?)\}/sm', $str, $m)) { + list($src, $var) = $m; + + // retrieve variable to interpolate in context, throw an exception + // if not found. + + if (!array_key_exists($var, $vars)) { + + // TODO: Add a custom exception + Atsumi::error__listen( + new Exception ( + sf('Locale interpolation failed in for var: "%s" in str: "%s"', + $var, $originalStr + ) + ) + ); + $value = ''; + } else $value = $vars[$var]; + $str = str_replace($src, $value, $str); + } + + foreach ($vars as $key => $replacement) { + $str = str_replace ('${'.$key.'}', $replacement, $str); + } + return $str; + + } } From 9d70a3d5207117fc2865d9f1f8b79f6aa38ff901 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Thu, 23 Oct 2014 07:35:30 -0700 Subject: [PATCH 138/164] added a security class, currently has a numericCode($ref, $salt, $length) method, useful for verification codes --- classes/utility/security/atsumI_Security.php | 22 ++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 classes/utility/security/atsumI_Security.php diff --git a/classes/utility/security/atsumI_Security.php b/classes/utility/security/atsumI_Security.php new file mode 100644 index 0000000..607a4d3 --- /dev/null +++ b/classes/utility/security/atsumI_Security.php @@ -0,0 +1,22 @@ + \ No newline at end of file From 6018386f8a271ebf6b083094363a8cbbb2c508fa Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Wed, 5 Nov 2014 08:53:18 -0800 Subject: [PATCH 139/164] minor fix --- .../elements/widget_CheckBoxArrayElement.php | 25 +++++++++++++------ classes/widgets/form/widget_Form.php | 1 + 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/classes/widgets/form/elements/widget_CheckBoxArrayElement.php b/classes/widgets/form/elements/widget_CheckBoxArrayElement.php index 8bf638d..2509d07 100644 --- a/classes/widgets/form/elements/widget_CheckBoxArrayElement.php +++ b/classes/widgets/form/elements/widget_CheckBoxArrayElement.php @@ -22,12 +22,20 @@ public function __construct($args) { } function setValue($input, $files = array()) { - if(empty($input[$this->name])) $input[$this->name] = $this->default; + + if (!isset($input[$this->name])) + $input[$this->name] = array(); + + if(empty($input[$this->name])) + $input[$this->name] = $this->default; + // creates an array of ids holding boolean values - foreach($this->options as $option => $name) { - if(!isset($input[$this->name][$option]) || !$input[$this->name][$option]) $input[$this->name][$option] = false; - else $input[$this->name][$option] = true; - } + if (count($input[$this->name])) + foreach($this->options as $option => $name) { + if(!isset($input[$this->name][$option]) || !$input[$this->name][$option]) $input[$this->name][$option] = false; + else $input[$this->name][$option] = true; + } + $this->value = $input[$this->name]; } @@ -36,10 +44,10 @@ function renderElement() { $valueArr = $this->getValue(); //sort the options into 3 cols - if($this->sort) { + if($this->sort) asort($this->options); - - } + + foreach($this->options as $value => $option) { $out.=(sf("
%s
", $this->getName(),$value, @@ -50,6 +58,7 @@ function renderElement() { $option, $this->delimiter )); } + return "
".$out."
"; } diff --git a/classes/widgets/form/widget_Form.php b/classes/widgets/form/widget_Form.php index dde14d6..a2e24fd 100644 --- a/classes/widgets/form/widget_Form.php +++ b/classes/widgets/form/widget_Form.php @@ -126,6 +126,7 @@ public function add($elementClass, $args) { try { $element->setValue($this->userInput, $this->userFiles); } catch(Exception $e) { + Atsumi::error__listen($e); $element->setError($e); } $element->validate(); From c372d6cd6ad09c431b8e1311e3f12a30bc471dcf Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Fri, 7 Nov 2014 08:24:38 -0800 Subject: [PATCH 140/164] Email validator accepted "0" as a valid email address due to required issue. --- classes/core/loader/atsumi_Loader.php | 5 +++-- classes/validators/validate_EmailAddress.php | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/classes/core/loader/atsumi_Loader.php b/classes/core/loader/atsumi_Loader.php index 66f3c1f..01a5739 100644 --- a/classes/core/loader/atsumi_Loader.php +++ b/classes/core/loader/atsumi_Loader.php @@ -178,7 +178,8 @@ public function _references($spec) { if (empty($part)) continue; $success = false; if($this->useRequire($domain, $part)) $success = true; - if($this->useClasses($domain, $part)) $success = true; + if($this->useDir($domain, $part, 'src')) $success = true; + if($this->useDir($domain, $part, 'classes')) $success = true; if(!$success) throw new Exception(sprintf('Unknown reference in atsumi_Loader::references() : %s:%s', $domain, $part)); } @@ -192,7 +193,7 @@ public function _references($spec) { * @param string $part The subfolder to process * @return boolean If the part was found */ - protected function useClasses($domain, $part) { + protected function useDir ($domain, $part, $dir = 'src') { $path = sprintf('%s/%s/classes/%s', $this->workspace, $domain, $part); if(!is_dir($path)) return false; self::useClassDir($path); diff --git a/classes/validators/validate_EmailAddress.php b/classes/validators/validate_EmailAddress.php index da30925..4d1f20e 100644 --- a/classes/validators/validate_EmailAddress.php +++ b/classes/validators/validate_EmailAddress.php @@ -20,7 +20,8 @@ public function __construct($testDomain = true, $arrayIndex = null) { public function validate($data) { if(is_array($data)) $data = $data[$this->arrayIndex]; - if(empty($data) || $this->validEmailAddress($data)) { + if (is_null($data) || !strlen($data)) return true; + if($this->validEmailAddress($data)) { if($this->testDomain) { if($this->checkDomainExists($data)) { return true; From 1025f8471773048b0deaa6e115c2d15db8b79cee Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Fri, 14 Nov 2014 08:59:05 -0800 Subject: [PATCH 141/164] brought in Jonnys namespace loader. Added support for projects/modules in external directories (domain can include slashes). --- classes/core/Atsumi.php | 2 +- .../core/error/parser/atsumi_ErrorParser.php | 2 +- classes/core/loader/Loader.php | 413 ++++++++++++++++++ classes/core/loader/atsumi_Loader.php | 305 +------------ init.php | 4 +- 5 files changed, 422 insertions(+), 304 deletions(-) create mode 100644 classes/core/loader/Loader.php diff --git a/classes/core/Atsumi.php b/classes/core/Atsumi.php index c6db07d..f056572 100644 --- a/classes/core/Atsumi.php +++ b/classes/core/Atsumi.php @@ -61,7 +61,7 @@ private function __construct() { $this->errorHandler = new atsumi_ErrorHandler(); // Load some helpful files - atsumi_Loader::references(atsumi_Loader::getAtsumiDir(), 'caster utility/http'); + \Atsumi\Core\Loader::references(\Atsumi\Core\Loader::getAtsumiDir(), 'caster utility/http'); } diff --git a/classes/core/error/parser/atsumi_ErrorParser.php b/classes/core/error/parser/atsumi_ErrorParser.php index 9f74604..5821a6e 100644 --- a/classes/core/error/parser/atsumi_ErrorParser.php +++ b/classes/core/error/parser/atsumi_ErrorParser.php @@ -65,7 +65,7 @@ public static function parse(Exception $e, $contentType = self::HTML, $recoverer case self::PLAINTEXT: $out .= sfl('###### ATSUMI has caught an Exception : %s', date(DATE_ATOM)); $out .= sfl("\n".' >> %s <<', $e->getMessage()); - $out .= sfl("\n".'Exception type: '.get_class($e)); + $out .= sfl("\n".'Exception type: %s',get_class($e)); if($e instanceof ErrorException) $out .= sfl("\n".'Severity level: '.$e->getSeverity()); diff --git a/classes/core/loader/Loader.php b/classes/core/loader/Loader.php new file mode 100644 index 0000000..38d4b6e --- /dev/null +++ b/classes/core/loader/Loader.php @@ -0,0 +1,413 @@ +workspace = $this->findWorkspace(); + } + + // GET FUNCTIONS + + /** + * Creates and/or returns a singlton instance of the class + * Note: Protected prevents anything externally calling this function. Use static functions. + * @access protected + * @return Loader A singlton instance of the loader + */ + protected static function getInstance() { + static $sInstance; + + if(!is_object($sInstance)) + $sInstance = new self; + + return $sInstance; + } + + /** + * Gets the name of the atsumi directory, incase it has been name differently by the developer + * @access public + * @return string The atsumi directory name + */ + private function _getAtsumiDir() { + return $this->atsumiDir; + } + + /** + * Gets the workspace of atsumi and the all related projects + * @access public + * @return string A absolute path to the workspace + */ + private function _getWorkspace() { + return $this->workspace; + } + + /* MAGIC METHODS */ + + /** + * PHP Magic function, used to call methods on singleton staticly + * @access public + * @param string $name The name of the method being called + * @param array $arguments The arguments being passed to the method + * @return mixed The result of the function call + */ + public static function __callStatic($name, $arguments) { + $instance = self::getInstance(); + + if(method_exists($instance, '_'.$name)) + return call_user_func_array(array($instance, '_'.$name), $arguments); + + // USe sprintf. No guarantee of sf at this point. + throw new \Exception(sprintf('Undefined call to %s::%s', get_class($instance), $name)); + } + + /* METHODS */ + + /** + * Return an absolute path to the base directory containing atsumi and developer projects + * which is used as the base folder for loading external project files from + * @access protected + * @return string An absolute path to the project base directory + */ + protected function findWorkspace() { + $matches = null; + + $file = str_replace('\\', '/', __FILE__); + + if(!preg_match('|^(.*)/classes/core/loader/Loader.php$|', $file, $matches)) + throw new \Exception('The atsumi loader cannot find its workspace!'); + + $matches = explode('/', $matches[1]); + $this->atsumiDir = array_pop($matches); + return implode('/', $matches); + } + + + /** + * Used to load or include a set of files as specified in the spec + * + * Examples: + * references('atsumi', 'utility mvc database'); + * references('atsumi', array('utility', 'mvc', 'database')); + * references(array('atsumi' => 'utility mvc database')); + * @access public + * @param $spec mixed See above for examples + */ + public function _references($spec) { + + + $args = func_get_args(); + switch(func_num_args()) { + case 1: + if(!is_array($args[0])) + throw new \Exception('Loader spec must be an associative array'); + $spec = $args[0]; + break; + case 2: + if(is_string($args[0]) && is_string($args[1])) { + $spec = array($args[0] => $args[1]); + } else { + $this->projectNamespace = $args[0]; + $spec = $args[1]; + } + break; + default: + throw new \Exception('Loader: Invalid number of args'); + break; + } + $domains = ''; + foreach($spec as $domain => $parts) { + + if(isset($this->processedPaths[$domain.':'.$parts])) continue; + + $this->processedPaths[$domain.':'.$parts] = true; + + if(!is_array($parts)) { + $parts = explode(' ', $parts); + } + + foreach($parts as $part) { + if (empty($part)) continue; + $success = false; + if($this->useRequire($domain, $part)) $success = true; + if($this->useDir($domain, $part, 'src')) $success = true; + if($this->useDir($domain, $part, 'classes')) $success = true; + + if(!$success) throw new \loader_ClassNotFoundException( + sprintf('Unknown reference in Loader::references() : %s:%s', $domain, $part), $domain); + } + } + + } + + /** + * Tries to find a class instance within the classes directory and add it to the autoloader + * @access protected + * @param string $domain The project domain folder to look in + * @param string $part The subfolder to process + * @return boolean If the part was found + */ + protected function useDir($domain, $part, $dirName) { + $path = sprintf('%s/%s/%s/%s', $this->workspace, $domain, $dirName, $part); + if(!is_dir($path)) return false; + self::useClassDir($path, $domain); + return true; + } + + /** + * Tries to find an include a required file in the includes directory + * @access protected + * @param string $domain The project domain folder to look in + * @param string $part The name of the file to look for + * @return boolean If the part was found + */ + protected function useRequire($domain, $part) { + $path = sprintf('%s/%s/include/%s.php', $this->workspace, $domain, $part); + if(!is_file($path)) return false; + + require_once($path); + return true; + } + + /** + * Process a directory presuming all files are named relative to the class they contain + * @access protected + * @param string $path The absolute path to the directory to process + */ + protected function useClassDir($path, $domain) { + $contents = scandir($path); + foreach($contents as $fileName) { + if(substr($fileName, 0, 1) == '.') continue; + + $fullPath = $path.'/'.$fileName; + + // Recursive loading of subdirectories + if(is_dir($fullPath)) { + self::useClassDir($fullPath, $domain); + continue; + } + if(is_file($fullPath) && preg_match('/^([a-zA-Z_][a-zA-Z0-9_]*)[\.inc\|]*.php$/', $fileName, $matches)) { + $className = str_replace('.php', '', $matches[0]); + $className = str_replace('.inc', '', $className); + self::_registerClass($className, $fullPath); + self::_registerNamespace($className, $fullPath, $domain); + } + } + } + + /** + * Used to register a class to the auto loader + * @access public + * @param string $classname The name of the class + * @param string $filePath An absolute path to the file containing the class + */ + public function _registerClass($classname, $filePath) { + $classname = strtolower($classname); + + /** DEPRECATED FOR NAMESPACES + if(array_key_exists($classname, $this->classes) && $filePath !== $this->classes[$classname]) { + throw new Loader\Exceptions\DuplicateClassException( + 'Duplicated class '.$classname.' in '.$this->classes[$classname].' and '.$filePath + ); + }**/ + + $this->classes[$classname] = $filePath; + } + + /** + * Used to register a namespace to the auto loader + * @access public + * @param string $classname The name of the class + * @param string $filePath An absolute path to the file containing the class + */ + public function _registerNamespace($classname, $filePath, $domain) { + + // get rid of folders + if (strpos($domain, '/')) { + $parts = explode('/', $domain); + $domain = array_pop($parts); + $folder = implode('/',$parts); + } else { + $folder = ''; + } + + // Strip out the base dir + $namespacePath = str_replace($this->_getWorkspace(), '', $filePath); + $namespacePath = str_replace($folder, '', $namespacePath); + + // Get the dir levels as array + $dirStruct = explode('/', trim($namespacePath, '/')); + + + // Work out if we're in Atsumi + $baseNamespace = array_shift($dirStruct); + if(trim($baseNamespace, '/') == trim($this->_getAtsumiDir(), '/')) { + $baseNamespace = $this->atsumiNamespace; + } else if($this->projectNamespace) { + $baseNamespace = $this->projectNamespace; + } + + $baseNamespace = '\\' . $baseNamespace; + + + $namespace = ''; + $i = 0; + foreach($dirStruct as $dir) { + + + // Ignore the standard classes dir + if($dir == 'classes' || $dir == 'src') { + continue; + } + + // if project name is same as first dir then assume namespace + if (++$i == 1 && strtolower($baseNamespace) == '\\' . strtolower($dir)) { + $baseNamespace = '\\' . ucfirst($dir); + continue; + } + $namespace .= '\\' . ucfirst($dir); + } + + $namespace = $baseNamespace . $namespace; + + // Finally, trim the file extension + $namespace = substr($namespace, 0, strrpos($namespace, '.')); + + if(array_key_exists($classname, $this->classes) && $filePath !== $this->classes[$classname]) { + throw new \loader_DuplicateClassException( + 'Duplicated class '.$classname.' in '.$this->classes[$classname].' and '.$filePath + ); + } + + $this->namespaces[trim($namespace, '\\')] = $filePath; + } + + /** + * Used by the PHP auto load function to load a class + * @access public + * @param string $classname The name of the class to load + */ + public function _loadClass($classname) { + if(strpos($classname, '\\') !== false) { + return $this->_loadClassByNamespace($classname); + } else { + return $this->_loadClassByName($classname); + } + } + + private function _loadClassByNamespace($namespace) { + if(!array_key_exists(trim($namespace, '\\'), $this->namespaces)) { + return; + } + require_once($this->namespaces[$namespace]); + } + + private function _loadClassByName($classname) { + $classname = strtolower($classname); + + if(!array_key_exists($classname, $this->classes)) + return; + + //throw new loader_ClassNotFoundException('Atsumi failed to find the class required \''.$classname.'\'', $classname); + + require_once($this->classes[$classname]); + } + + static public function loadClass($className) { + $args = func_get_args(); + return self::__callStatic(__FUNCTION__, $args); + } + +} + +/** + * register the autoloader + */ +spl_autoload_register(array('Atsumi\Core\Loader', 'loadClass')); + +?> \ No newline at end of file diff --git a/classes/core/loader/atsumi_Loader.php b/classes/core/loader/atsumi_Loader.php index 01a5739..750b4f9 100644 --- a/classes/core/loader/atsumi_Loader.php +++ b/classes/core/loader/atsumi_Loader.php @@ -1,310 +1,15 @@ workspace = $this->findWorkspace(); - } - - // GET FUNCTIONS - - /** - * Creates and/or returns a singlton instance of the class - * Note: Protected prevents anything externally calling this function. Use static functions. - * @access protected - * @return atsumi_Loader A singlton instance of the loader - */ - protected static function getInstance() { - static $sInstance; - - if(!is_object($sInstance)) - $sInstance = new self; - - return $sInstance; - } - - /** - * Gets the name of the atsumi directory, incase it has been name differently by the developer - * @access public - * @return string The atsumi directory name - */ - public function _getAtsumiDir() { - return $this->atsumiDir; - } - - /** - * Gets the workspace of atsumi and the all related projects - * @access public - * @return string A absolute path to the workspace - */ - public function _getWorkspace() { - return $this->workspace; + static public function references ($args) { + return \Atsumi\Core\Loader::references ($args); } - - /* MAGIC METHODS */ - - /** - * PHP Magic function, used to call methods on singleton staticly - * @access public - * @param string $name The name of the method being called - * @param array $arguments The arguments being passed to the method - * @return mixed The result of the function call - */ - public static function __callStatic($name, $arguments) { - $instance = self::getInstance(); - - if(method_exists($instance, '_'.$name)) - return call_user_func_array(array($instance, '_'.$name), $arguments); - - throw new Exception('Undefined call to atsumi_Debug::'.$name); - } - - /* METHODS */ - - /** - * Return an absolute path to the base directory containing atsumi and developer projects - * which is used as the base folder for loading external project files from - * @access protected - * @return string An absolute path to the project base directory - */ - protected function findWorkspace() { - $matches = null; - - $file = str_replace('\\', '/', __FILE__); - - if(!preg_match('|^(.*)/classes/core/loader/atsumi_Loader.php$|', $file, $matches)) - throw new Exception('The atsumi loader cannot find its workspace!'); - - $matches = explode('/', $matches[1]); - $this->atsumiDir = array_pop($matches); - return implode('/', $matches); - } - - /** - * Used to load or include a set of files as specified in the spec - * - * Examples: - * references('atsumi', 'utility mvc database'); - * references('atsumi', array('utility', 'mvc', 'database')); - * references(array('atsumi' => 'utility mvc database')); - * @access public - * @param $spec mixed See above for examples - */ - public function _references($spec) { - $args = func_get_args(); - switch(func_num_args()) { - case 1: - if(!is_array($args[0])) - throw new Exception('Loader spec must be an associative array'); - $spec = $args[0]; - break; - case 2: - $spec = array($args[0] => $args[1]); - break; - default: - throw new Exception('atsumi_Loader: Invalid number of args'); - break; - } - - $domains = ''; - $collections = ''; - foreach($spec as $domain => $parts) { - $domains .= $domain.', '; - if(isset($this->processedPaths[$domain.':'.$parts])) continue; - $this->processedPaths[$domain.':'.$parts] = true; - - if(!is_array($parts)) { - $collections .= '['.$parts.']'; - $parts = explode(' ', $parts); - } else { - $collections .= '['.implode(', ', $parts).']'; - } - - foreach($parts as $part) { - if (empty($part)) continue; - $success = false; - if($this->useRequire($domain, $part)) $success = true; - if($this->useDir($domain, $part, 'src')) $success = true; - if($this->useDir($domain, $part, 'classes')) $success = true; - - if(!$success) throw new Exception(sprintf('Unknown reference in atsumi_Loader::references() : %s:%s', $domain, $part)); - } - } - } - - /** - * Tries to find a class instance within the classes directory and add it to the autoloader - * @access protected - * @param string $domain The project domain folder to look in - * @param string $part The subfolder to process - * @return boolean If the part was found - */ - protected function useDir ($domain, $part, $dir = 'src') { - $path = sprintf('%s/%s/classes/%s', $this->workspace, $domain, $part); - if(!is_dir($path)) return false; - self::useClassDir($path); - return true; - } - - /** - * Tries to find an include a required file in the includes directory - * @access protected - * @param string $domain The project domain folder to look in - * @param string $part The name of the file to look for - * @return boolean If the part was found - */ - protected function useRequire($domain, $part) { - $path = sprintf('%s/%s/include/%s.php', $this->workspace, $domain, $part); - if(!is_file($path)) return false; - - require_once($path); - return true; - } - - /** - * Process a directory presuming all files are named relative to the class they contain - * @access protected - * @param string $path The absolute path to the directory to process - */ - protected function useClassDir($path) { - $contents = scandir($path); - foreach($contents as $fileName) { - if(substr($fileName, 0, 1) == '.') continue; - - $fullPath = $path.'/'.$fileName; - - // Recursive loading of subdirectories - if(is_dir($fullPath)) { - self::useClassDir($fullPath); - continue; - } - if(is_file($fullPath) && preg_match('/^([a-zA-Z_][a-zA-Z0-9_]*)[\.inc\|]*.php$/', $fileName, $matches)) { - $className = str_replace('.php', '', $matches[0]); - $className = str_replace('.inc', '', $className); - self::_registerClass($className, $fullPath); - } - } - } - - /** - * Used to register a class to the auto loader - * @access public - * @param string $classname The name of the class - * @param string $filePath An absolute path to the file containing the class - */ - public function _registerClass($classname, $filePath) { - $classname = strtolower($classname); - - if(array_key_exists($classname, $this->classes) && $filePath !== $this->classes[$classname]) { - throw new loader_DuplicateClassException( - 'Duplicated class '.$classname.' in '.$this->classes[$classname].' and '.$filePath - ); - } - - $this->classes[$classname] = $filePath; - } - - /** - * Used by the PHP auto load function to load a class - * @access public - * @param string $classname The name of the class to load - */ - public function _loadClass($classname) { - $classname = strtolower($classname); - - if(!array_key_exists($classname, $this->classes)) - return; - - //throw new loader_ClassNotFoundException('Atsumi failed to find the class required \''.$classname.'\'', $classname); - - require_once($this->classes[$classname]); - } - - /* - * NOTE: Below are all static functions which will be removed when php 5.3.0+ becomes the - * common and the PHP magic function __callStatic works correctly - */ - public static function getAtsumiDir() { - return self::__callStatic(__FUNCTION__, array()); - } - - public static function getWorkspace() { - return self::__callStatic(__FUNCTION__, array()); - } - - public static function references($spec) { - $args = func_get_args(); - return self::__callStatic(__FUNCTION__, $args); - } - - public function registerClass($className, $filePath) { - $args = func_get_args(); - return self::__callStatic(__FUNCTION__, $args); - } - - static public function loadClass($className) { - $args = func_get_args(); - return self::__callStatic(__FUNCTION__, $args); + static public function getWorkspace () { + return \Atsumi\Core\Loader::getWorkspace (); } } -/** - * register the autoloader - */ -spl_autoload_register(array('atsumi_Loader', 'loadClass')); ?> \ No newline at end of file diff --git a/init.php b/init.php index 6518e77..75af4ce 100644 --- a/init.php +++ b/init.php @@ -11,10 +11,10 @@ */ // Manually require the core components as loader is not initalised at this point -require_once(dirname(__FILE__).'/classes/core/loader/atsumi_Loader.php'); +require_once(dirname(__FILE__).'/classes/core/loader/Loader.php'); // Load everything else -atsumi_Loader::references(atsumi_Loader::getAtsumiDir(), 'core'); +\Atsumi\Core\Loader::references(\Atsumi\Core\Loader::getAtsumiDir(), 'core'); // Start atsumi Atsumi::start(); From f590499d7fe9b9aa56fc69a999e4a4b5eccf276b Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 27 Jan 2015 17:12:03 +0000 Subject: [PATCH 142/164] various minor updates --- classes/cache/cache_MemcacheHandler.php | 9 ++++++++- classes/core/error/listeners/listener_LogToFile.php | 7 ++++--- classes/utility/http/atsumi_Http.php | 5 ++++- classes/utility/http/atsumi_HttpException.php | 1 + 4 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 classes/utility/http/atsumi_HttpException.php diff --git a/classes/cache/cache_MemcacheHandler.php b/classes/cache/cache_MemcacheHandler.php index cadb688..18a33db 100644 --- a/classes/cache/cache_MemcacheHandler.php +++ b/classes/cache/cache_MemcacheHandler.php @@ -48,10 +48,17 @@ public function __construct($host, $port, $useExceptions = false) { $this->useExceptions = $useExceptions; $this->memcache = new Memcache; + if (is_null($host)) return; + if(!$this->memcache->connect($host, $port)) throw new cache_CouldNotConnectException('Could not connect to Memcache server'); } - + + // adds a memcached server to the connection pool + public function addServer ($host, $port = 11211) { + $this->memcache->addServer($host, $port); + } + /* GET METHODS */ /** diff --git a/classes/core/error/listeners/listener_LogToFile.php b/classes/core/error/listeners/listener_LogToFile.php index 0f48d33..4447966 100644 --- a/classes/core/error/listeners/listener_LogToFile.php +++ b/classes/core/error/listeners/listener_LogToFile.php @@ -52,12 +52,13 @@ public function __construct($logDir, $filePrefix = '') { */ protected function writeToLog($dataIn) { $filename = $this->filePrefix.($this->filePrefix?'-':'').date('Y-m-d').'.log'; - $handle = @fopen($this->logDir.$filename, 'a'); + $filePath = $this->logDir.DIRECTORY_SEPARATOR.$filename; + $handle = @fopen($filePath, 'a'); if(!$handle) - throw new errorHandler_ListenerException('Cannot open log file: '.$this->logDir.$filename); + throw new errorHandler_ListenerException('Cannot open log file: '.$filePath); $write = fwrite($handle, $dataIn); - if ($write === false) throw new errorHandler_ListenerException('Cannot no write log file: '.$this->logDir.$filename); + if ($write === false) throw new errorHandler_ListenerException('Cannot no write log file: '.$filePath); fclose($handle); } diff --git a/classes/utility/http/atsumi_Http.php b/classes/utility/http/atsumi_Http.php index dc08dd8..5410fe7 100644 --- a/classes/utility/http/atsumi_Http.php +++ b/classes/utility/http/atsumi_Http.php @@ -91,7 +91,10 @@ static public function post ($url, $fields, $method = 1, $httpHeaders = array()) $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); if ($httpCode == 0) - throw new Exception ('CURL error: '.curl_errno($ch)); + throw new atsumi_HttpException ( + 'CURL error: #'.curl_errno($ch), + curl_errno($ch) + ); $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); $header = substr($response, 0, $headerSize); diff --git a/classes/utility/http/atsumi_HttpException.php b/classes/utility/http/atsumi_HttpException.php new file mode 100644 index 0000000..ba72037 --- /dev/null +++ b/classes/utility/http/atsumi_HttpException.php @@ -0,0 +1 @@ + \ No newline at end of file From da4121c4786cd3fd57e8106c7dd7153a908b25d1 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Wed, 28 Jan 2015 06:27:12 -0800 Subject: [PATCH 143/164] added ::from() and ::fromModel() methods, now can transpose one model to another --- classes/mvc/models/mvc_AbstractModel.php | 43 ++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/classes/mvc/models/mvc_AbstractModel.php b/classes/mvc/models/mvc_AbstractModel.php index 121d6f3..3392301 100644 --- a/classes/mvc/models/mvc_AbstractModel.php +++ b/classes/mvc/models/mvc_AbstractModel.php @@ -9,9 +9,52 @@ abstract class mvc_AbstractModel { protected $data = array(); + static public function from ($data) { + + switch (gettype($data)) { + case 'array': + return self::fromArray($data); + + case 'object': + if ($data instanceof self) + return self::fromModel($data); + default: + throw new Exception ('Can not create new model. Unexpected input: '.gettype($data)); + } + + } + + static public function fromArray ($data) { return new static ($data); } + static public function fromModel (self $data) { + + $o = new static(); + $o->_fromModel($data); + return $o; + } + + private function _fromModel (self $data) { + + foreach($this->structure as $key => $properties) { + switch ($properties['type']) { + case 'o': + if (isset($properties['model']) && $data->has($key)) { + $m = $properties['model']::from($data->get($key)); + + $this->set($key, $m, true); + break; + + } + default: + $this->set($key, $data->get($key), true); + + } + + } + return $this; + } /* generic */ public function __construct ($data = array()) { From f1e8c0a2b03a86f006e9a1ecd290a61c6dae93c0 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 17 Feb 2015 18:49:03 +0000 Subject: [PATCH 144/164] can now populate a request headers var reference --- classes/utility/http/atsumi_Http.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/classes/utility/http/atsumi_Http.php b/classes/utility/http/atsumi_Http.php index 5410fe7..04eb906 100644 --- a/classes/utility/http/atsumi_Http.php +++ b/classes/utility/http/atsumi_Http.php @@ -63,7 +63,7 @@ class atsumi_Http { const POST_METHOD_CURL = 1; const POST_METHOD_PECL = 2; - static public function post ($url, $fields, $method = 1, $httpHeaders = array()) { + static public function post ($url, $fields, $method = 1, $httpHeaders = array(), &$httpRequestHeaders = false) { switch ($method) { @@ -82,6 +82,7 @@ static public function post ($url, $fields, $method = 1, $httpHeaders = array()) curl_setopt($ch, CURLOPT_MAXREDIRS, 10); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 120); curl_setopt($ch, CURLOPT_TIMEOUT, 120); + curl_setopt($ch, CURLINFO_HEADER_OUT, true); if (count($httpHeaders)) curl_setopt($ch, CURLOPT_HTTPHEADER, $httpHeaders); @@ -100,6 +101,8 @@ static public function post ($url, $fields, $method = 1, $httpHeaders = array()) $header = substr($response, 0, $headerSize); $body = substr($response, $headerSize); + $httpRequestHeaders = curl_getinfo($ch, CURLINFO_HEADER_OUT ); + //close connection curl_close($ch); From c73ad7d7151771758f8713fa314003db768ad5c3 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 17 Feb 2015 18:50:18 +0000 Subject: [PATCH 145/164] minor updates --- classes/mvc/models/mvc_AbstractDaoModel.php | 16 ++++++++++++++++ classes/mvc/models/mvc_AbstractModel.php | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/classes/mvc/models/mvc_AbstractDaoModel.php b/classes/mvc/models/mvc_AbstractDaoModel.php index f9763bf..3fbc42a 100644 --- a/classes/mvc/models/mvc_AbstractDaoModel.php +++ b/classes/mvc/models/mvc_AbstractDaoModel.php @@ -34,6 +34,22 @@ static public function write ($db, $o) { } } + + /* + static public function writeInsert ($db, $o) { + + $args = array(); + foreach($o->data as $key => $value) { + if (!$o->structure[$key]['write']) continue; + $args[] = sf('%s = %%%s', $key, $o->structure[$key]['type']); + $args[] = $value; + } + + array_unshift($args, static::DB_TABLE_NAME); + + $result = call_user_func_array(array($db, 'insert'), $args); + } + */ /* generic */ static protected function load ($db, $where = null, $orderBy = null, $offset = null, $limit = null) { diff --git a/classes/mvc/models/mvc_AbstractModel.php b/classes/mvc/models/mvc_AbstractModel.php index 3392301..1a1a6c9 100644 --- a/classes/mvc/models/mvc_AbstractModel.php +++ b/classes/mvc/models/mvc_AbstractModel.php @@ -101,7 +101,7 @@ function setArray ($asoc) { $this->set($k, $v); } - function increment ($k, $v) { + function increment ($k, $v = 1) { if (!$this->has($k)) throw new Exception('unknown key: '.$k); // increment different data types From 15b4af2ffd32daced9894d581b50e8876dd5e54d Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 2 Mar 2015 11:55:30 +0000 Subject: [PATCH 146/164] added more useful exception messages - still should add custom exception classes --- classes/mvc/models/mvc_AbstractModel.php | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/classes/mvc/models/mvc_AbstractModel.php b/classes/mvc/models/mvc_AbstractModel.php index 1a1a6c9..6b96316 100644 --- a/classes/mvc/models/mvc_AbstractModel.php +++ b/classes/mvc/models/mvc_AbstractModel.php @@ -151,15 +151,24 @@ function __get ($key) { } /* generic */ - function get($key, $strict = true) { - if (!array_key_exists($key, $this->data) && !array_key_exists('default', $this->structure[$key])) - throw new Exception ('Unknown model key: '. $key); + function get($key, $strict = true) { - if (!array_key_exists($key, $this->data)) + if (array_key_exists($key, $this->data)) + return $this->data[$key]; + + else if (!array_key_exists($key, $this->data) && !array_key_exists($key, $this->structure)) + throw new Exception ($key. ' is not a known property of ' .get_called_class()); + + else if (!array_key_exists($key, $this->data) && array_key_exists('default', $this->structure[$key])) return $this->structure[$key]['default']; + + else if (!array_key_exists($key, $this->data)) + throw new Exception ('property: '.$key. ' has no default value in ' .get_called_class()); - else return $this->data[$key]; + + /* + * TODO: casting try { $value = caster_PostgreSqlToPhp::cast(sf('%%%s', $this->structure[$key]['type']), $this->data[$key]); return $value; From 4037999e7f24cde0a36df31c294072cb34d9a626 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Fri, 13 Mar 2015 18:36:21 +0000 Subject: [PATCH 147/164] more work on dynamic models --- classes/mvc/models/mvc_AbstractModel.php | 68 +++++++++++++++++++++--- classes/mvc/models/mvc_DynamicModel.php | 17 ++++++ 2 files changed, 78 insertions(+), 7 deletions(-) diff --git a/classes/mvc/models/mvc_AbstractModel.php b/classes/mvc/models/mvc_AbstractModel.php index 6b96316..c82c05e 100644 --- a/classes/mvc/models/mvc_AbstractModel.php +++ b/classes/mvc/models/mvc_AbstractModel.php @@ -58,13 +58,36 @@ private function _fromModel (self $data) { /* generic */ public function __construct ($data = array()) { + + foreach ($this->structure as $k => $properties) { + switch ($properties['type']) { + + case 'o': + + // do the objects want to be created + if (isset($properties['create'])) { + + if (isset($properties['model']) && !is_null($properties['model'])) { + $this->set($k, new $properties['model']); + + // if it's a dynamic model - create it + } else if (isset($properties['structure']) && !is_null($properties['structure'])) { + $this->set($k, new mvc_DynamicModel($properties['structure'])); + } + + } + + } + $this->set( - $k, + $k, isset($properties['default'])? $properties['default']:null, true ); + + } // set data @@ -93,11 +116,41 @@ function set ($k, $v, $force = false) { ) throw new Exception ('Column not writable'); - $this->data[$k] = $v; + + if (isset($this->structure[$k]['type']) && + $this->structure[$k]['type'] == 'o' && + isset($this->data[$k]) && $this->data[$k] instanceof mvc_AbstractModel + ) { + if (is_array($v)) { + $this->data[$k]->setArray($v); + } else if ($v instanceof mvc_AbstractModel) { + $this->data[$k] = $v; + } else if (is_null($v)) { + // $this->data[$k] = $v; + } else { + // at this point we don't have valud data for an object... + // dump(array('dont know what to do', $k, $v)); + } + } else + $this->data[$k] = $v; } - function setArray ($asoc) { - foreach ($asoc as $k => $v) + + // setup the properties that are objects + // TODO: this shouldn't be required - __construct should this automatically... + function setupObjectsFromStructure ($structure) { + foreach ($structure as $k => $v) + if ($this->structure[$k]['type'] == 'o' && isset($v['structure'])) { + $o = new mvc_DynamicModel($v['structure']); + $o->setupObjectsFromStructure($v['structure']); + $this->set($k, $o); + } + } + + + + function setArray ($assoc) { + foreach ($assoc as $k => $v) $this->set($k, $v); } @@ -156,14 +209,15 @@ function get($key, $strict = true) { if (array_key_exists($key, $this->data)) return $this->data[$key]; - else if (!array_key_exists($key, $this->data) && !array_key_exists($key, $this->structure)) - throw new Exception ($key. ' is not a known property of ' .get_called_class()); + else if (!array_key_exists($key, $this->data) && !array_key_exists($key, $this->structure)) + throw new Exception ('>>'.$key. '<< is not a known property of >>' .get_called_class().'<<'); + else if (!array_key_exists($key, $this->data) && array_key_exists('default', $this->structure[$key])) return $this->structure[$key]['default']; else if (!array_key_exists($key, $this->data)) - throw new Exception ('property: '.$key. ' has no default value in ' .get_called_class()); + throw new Exception ('property: >>'.$key. '<< has no default value in >>' .get_called_class().'<<'); diff --git a/classes/mvc/models/mvc_DynamicModel.php b/classes/mvc/models/mvc_DynamicModel.php index 5eb727c..7aae22a 100644 --- a/classes/mvc/models/mvc_DynamicModel.php +++ b/classes/mvc/models/mvc_DynamicModel.php @@ -3,20 +3,37 @@ class mvc_DynamicModel extends mvc_AbstractModel { protected $structure = array(); + /* generic */ public function __construct ($spec = array()) { + + $data = array(); foreach ($spec as $k => $properties) { $this->add($k, $properties); + if (isset($properties['value'])) + $data[$k] = $properties['value']; + elseif (isset($properties['default'])) + $data[$k] = $properties['default']; } + + parent::__construct($data); } + + public function add ($key, $properties) { $this->structure[$key] = array( 'type' => $properties['type'], + 'create' => isset($properties['create'])?$properties['create']:false, + 'structure' => isset($properties['structure'])?$properties['structure']:null, + 'model' => isset($properties['model'])?$properties['model']:null, 'default' => isset($properties['default'])?$properties['default']:null ); + if (isset($properties['default'])) + $this->set($key, $properties['default']); + if (isset($properties['value'])) $this->set($key, $properties['value']); From e2fdae9986a6bdbed2bed3e8fbe406f0944da19b Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 17 Mar 2015 13:16:59 +0000 Subject: [PATCH 148/164] minor fixes for object auto creation --- classes/mvc/models/mvc_AbstractModel.php | 5 ++--- classes/mvc/models/mvc_DynamicModel.php | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/classes/mvc/models/mvc_AbstractModel.php b/classes/mvc/models/mvc_AbstractModel.php index c82c05e..bc44ae8 100644 --- a/classes/mvc/models/mvc_AbstractModel.php +++ b/classes/mvc/models/mvc_AbstractModel.php @@ -58,7 +58,6 @@ private function _fromModel (self $data) { /* generic */ public function __construct ($data = array()) { - foreach ($this->structure as $k => $properties) { switch ($properties['type']) { @@ -66,7 +65,7 @@ public function __construct ($data = array()) { case 'o': // do the objects want to be created - if (isset($properties['create'])) { + if (isset($properties['create']) && $properties['create'] == true) { if (isset($properties['model']) && !is_null($properties['model'])) { $this->set($k, new $properties['model']); @@ -260,7 +259,7 @@ function __toString () { } function output ($type = self::OUTPUT_FORMAT_ASSOC) { - + $this->preOutput($type); switch ($type) { diff --git a/classes/mvc/models/mvc_DynamicModel.php b/classes/mvc/models/mvc_DynamicModel.php index 7aae22a..4926787 100644 --- a/classes/mvc/models/mvc_DynamicModel.php +++ b/classes/mvc/models/mvc_DynamicModel.php @@ -10,6 +10,7 @@ public function __construct ($spec = array()) { $data = array(); foreach ($spec as $k => $properties) { $this->add($k, $properties); + if (isset($properties['value'])) $data[$k] = $properties['value']; elseif (isset($properties['default'])) From 99d774ec4f66f63d174e84c558701103daefdb6c Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Fri, 27 Mar 2015 16:37:21 +0000 Subject: [PATCH 149/164] a few minor hooks --- classes/core/app/atsumi_AppHandler.php | 4 ++++ classes/widgets/form/elements/widget_TextAreaElement.php | 7 ++++++- classes/widgets/form/widget_Form.php | 7 +++++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/classes/core/app/atsumi_AppHandler.php b/classes/core/app/atsumi_AppHandler.php index 87d3ea3..0f139f1 100644 --- a/classes/core/app/atsumi_AppHandler.php +++ b/classes/core/app/atsumi_AppHandler.php @@ -137,6 +137,10 @@ public function getParserMetaData() { public function getBaseUri() { return $this->baseUri; } + + public function getController() { + return $this->controller; + } // SET FUNCTIONS diff --git a/classes/widgets/form/elements/widget_TextAreaElement.php b/classes/widgets/form/elements/widget_TextAreaElement.php index a2ec510..43c6685 100644 --- a/classes/widgets/form/elements/widget_TextAreaElement.php +++ b/classes/widgets/form/elements/widget_TextAreaElement.php @@ -4,6 +4,7 @@ class widget_TextAreaElement extends widget_AbstractElement { private $disabled = false; private $rows = 7; + private $placeholder = ''; public function __construct($args) { if(array_key_exists("disabled", $args)) @@ -11,6 +12,9 @@ public function __construct($args) { if(array_key_exists('rows', $args)) $this->rows = $args['rows']; + + if(array_key_exists('placeholder', $args)) + $this->placeholder = $args['placeholder']; } public function outputSpecific() { @@ -18,9 +22,10 @@ public function outputSpecific() { } function renderElement() { - return( sf("", + return( sf("", $this->getName(), ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', + strlen($this->placeholder)?sf(' placeholder="%s"', $this->placeholder):'', $this->getName(), $this->disabled?" disabled='true'":"", !is_null($this->rows)?sf(' rows="%s"',$this->rows):'', diff --git a/classes/widgets/form/widget_Form.php b/classes/widgets/form/widget_Form.php index a2e24fd..f777260 100644 --- a/classes/widgets/form/widget_Form.php +++ b/classes/widgets/form/widget_Form.php @@ -277,9 +277,12 @@ public function getFormBottom($options = array()) { // add the submit to the bottom of the form for now(will convert to element in next version) $html = sfl('
', array_key_exists('rowClasses',$options)?' '.$options['rowClasses']:''); - $html .= sfl(' ', + $html .= sfl(' %s%s', + array_key_exists('preButtonHtml', $options)?$options['preButtonHtml']:'', array_key_exists('buttonClasses',$options)?' '.$options['buttonClasses']:'', - $this->name, $this->getSubmit()); + $this->name, $this->getSubmit(), + array_key_exists('postButtonHtml', $options)?$options['postButtonHtml']:'' + ); $html .= sfl('
'); $html .= sfl(''); From 0f81ca48c38312e9492ead8eeaf897324ce6cd3f Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 31 Mar 2015 17:19:18 +0100 Subject: [PATCH 150/164] added support for post connection processing. Atsumi::app__dispatchConnection(); will close the HTTP connection and then calling Atsumi::app__postConnectionProcess() will trigger the after_{$page_name}() methods to be called --- classes/core/Atsumi.php | 99 -------------------------- classes/core/app/atsumi_AppHandler.php | 31 ++++++++ classes/core/debug/atsumi_Debug.php | 13 ++-- 3 files changed, 40 insertions(+), 103 deletions(-) diff --git a/classes/core/Atsumi.php b/classes/core/Atsumi.php index f056572..260e78d 100644 --- a/classes/core/Atsumi.php +++ b/classes/core/Atsumi.php @@ -165,105 +165,6 @@ public function _initStore () { $this->store = new mvc_DynamicModel(); } - /* - * NOTE: Below are all static functions which will be removed when php 5.3.0+ becomes the - * common and the PHP magic function __callStatic works correctly - */ - public static function start() { - $args = func_get_args(); - return self::__callStatic(__FUNCTION__, $args); - } - - public static function initApp($settings, $debug = false) { - $args = func_get_args(); - return self::__callStatic(__FUNCTION__, $args); - } - - public static function initStore() { - $args = func_get_args(); - return self::__callStatic(__FUNCTION__, $args); - } - - public static function app__setUriParser($parser) { - $args = func_get_args(); - return self::__callStatic(__FUNCTION__, $args); - } - - public static function app__setPath($path) { - $args = func_get_args(); - return self::__callStatic(__FUNCTION__, $args); - } - - public static function app__go($path) { - $args = func_get_args(); - return self::__callStatic(__FUNCTION__, $args); - } - - public static function app__render() { - $args = func_get_args(); - return self::__callStatic(__FUNCTION__, $args); - } - - public static function app__getParserData() { - $args = func_get_args(); - return self::__callStatic(__FUNCTION__, $args); - } - - public static function app__getParserMetaData() { - $args = func_get_args(); - return self::__callStatic(__FUNCTION__, $args); - } - - public static function app__getBaseUri() { - $args = func_get_args(); - return self::__callStatic(__FUNCTION__, $args); - } - - public static function app__createUri($controller, $method) { - $args = func_get_args(); - return self::__callStatic(__FUNCTION__, $args); - } - - public static function store__set($k, $v) { - $args = func_get_args(); - return self::__callStatic(__FUNCTION__, $args); - } - public static function store__get($k) { - $args = func_get_args(); - return self::__callStatic(__FUNCTION__, $args); - } - - public static function error__setRecoverer($recoverer) { - $args = func_get_args(); - return self::__callStatic(__FUNCTION__, $args); - } - - public static function error__addObserver($observer, $eventType) { - $args = func_get_args(); - return self::__callStatic(__FUNCTION__, $args); - } - - public static function error__removeObserver($observer) { - $args = func_get_args(); - return self::__callStatic(__FUNCTION__, $args); - } - - public static function error__setFloodControl(cache_HandlerInterface $cacheManager, $duration) { - $args = func_get_args(); - return self::__callStatic(__FUNCTION__, $args); - } - - public static function error__listen(Exception $e) { - $args = func_get_args(); - return self::__callStatic(__FUNCTION__, $args); - } - - public static function error__recover(Exception $e) { - $args = func_get_args(); - return self::__callStatic(__FUNCTION__, $args); - } - - } ?> \ No newline at end of file diff --git a/classes/core/app/atsumi_AppHandler.php b/classes/core/app/atsumi_AppHandler.php index 0f139f1..2107915 100644 --- a/classes/core/app/atsumi_AppHandler.php +++ b/classes/core/app/atsumi_AppHandler.php @@ -366,6 +366,18 @@ public function render() { $viewHandler->render($view, $viewData); atsumi_Debug::record('Rendering', sf('Rendering was performed by the %s view handler', get_class($viewHandler)), null, 'app:controller:render'); + + + // tell the debug to print if required as connection will be closed by __destruct + atsumi_Debug::printIfRequired(); + + // close the connection +// flush(); +// ob_flush(); +// if (session_id()) session_write_close(); + + + // Time and execute the post render atsumi_Debug::startTimer('app:controller:postRender'); $this->controller->postRender(); @@ -374,5 +386,24 @@ public function render() { // Log the whole processing time atsumi_Debug::record('Rendering Complete', 'All rendering was completed successfully', null, 'app:controller:rendering'); } + + public function dispatchConnection () { + + flush(); + ob_flush(); + if (session_id()) session_write_close(); + + fastcgi_finish_request(); + } + + public function postConnectionProcess () { + + // page_name turns in to after_name + $afterMethod = 'after_'.substr($this->parserData['method'],5); + + if (method_exists($this->controller, $afterMethod)) + $this->controller->processRequest($afterMethod, $this->parserData['args']); + + } } ?> diff --git a/classes/core/debug/atsumi_Debug.php b/classes/core/debug/atsumi_Debug.php index 836c66a..94fc91f 100755 --- a/classes/core/debug/atsumi_Debug.php +++ b/classes/core/debug/atsumi_Debug.php @@ -145,12 +145,18 @@ private function __construct() {} * @access public */ public function __destruct() { + self::printIfRequired(); + } + + static function printIfRequired() { + + $d = self::getInstance(); try { - if($this->active && $this->autoRender) - echo $this->render(); + if($d->active && $d->autoRender) + echo $d->render(); } catch (Exception $e) { } } - + /* GET FUNCTIONS */ /** @@ -700,7 +706,6 @@ protected function returnCss() { * @return string The debugger as a valid HTML5 string */ public function _render() { - if(!$this->active) return; From 3e184a3c861c458f72855d6ca2561cc5d81fda9e Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Thu, 2 Apr 2015 11:20:12 +0000 Subject: [PATCH 151/164] added Sentry exception listener, minor tweaks to debug to expose console data for use elsewhere --- classes/core/debug/atsumi_Debug.php | 19 +++++++++--- .../core/error/listeners/listener_Sentry.php | 29 +++++++++++++++++++ 2 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 classes/core/error/listeners/listener_Sentry.php diff --git a/classes/core/debug/atsumi_Debug.php b/classes/core/debug/atsumi_Debug.php index 94fc91f..b136da5 100755 --- a/classes/core/debug/atsumi_Debug.php +++ b/classes/core/debug/atsumi_Debug.php @@ -148,6 +148,11 @@ public function __destruct() { self::printIfRequired(); } + static function getConsoleData() { + $d = self::getInstance(); + return $d->_getConsoleData(); + } + static function printIfRequired() { $d = self::getInstance(); @@ -348,13 +353,14 @@ public function _endTimer($key = null) { public function _record($title, $desc, $data = null, $timer = false, $area = self::AREA_GENERAL) { if(!$this->active) return; - $timerString = ''; if ($timer === true) - $timerString = '(Process Time: '.self::_endTimer().')'; + $time = self::_endTimer(); else if (is_string($timer)) { - $timerString = '(Process Time: '.self::_endTimer($timer).' ms)'; + $time = self::_endTimer($timer); } + $timerString = '(Process Time: '.$time.')'; + $this->consoleData[] = array( @@ -362,7 +368,8 @@ public function _record($title, $desc, $data = null, $timer = false, $area = sel 'desc' => $desc, 'data' => $data, 'area' => $area, - 'timestamp' =>$timerString + 'time' => $time, + 'timestamp' => $timerString ); } @@ -387,6 +394,10 @@ public function _addArea($name, $color) { $this->areas[$name] = $color; } + public function _getConsoleData() { + return $this->consoleData; + } + /** * Formats a variable into a html5 valid string representation * @access protected diff --git a/classes/core/error/listeners/listener_Sentry.php b/classes/core/error/listeners/listener_Sentry.php new file mode 100644 index 0000000..f94395b --- /dev/null +++ b/classes/core/error/listeners/listener_Sentry.php @@ -0,0 +1,29 @@ +dsn = $dsn; + $this->release = $release; + $this->tags = $tags; + } + + public function notify(atsumi_Observable $sender, atsumi_EventArgs $args) { + + $client = new Raven_Client( + $this->dsn, + array ( + 'release' => $this->release, + 'tags' => $this->tags + ) + ); + + $client->captureException($args->exception); + + } +} +?> \ No newline at end of file From 513de121e2b640c8a3e270f401a8eb3005ad6382 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 14 Apr 2015 17:26:10 +0000 Subject: [PATCH 152/164] added session handler that uses a cache handler eg APC or memcache. Useful for load balancing and sessions --- .../session/storage/session_CacheStorage.php | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 classes/session/storage/session_CacheStorage.php diff --git a/classes/session/storage/session_CacheStorage.php b/classes/session/storage/session_CacheStorage.php new file mode 100644 index 0000000..d94670f --- /dev/null +++ b/classes/session/storage/session_CacheStorage.php @@ -0,0 +1,55 @@ +cache = $cache; + $this->ttl = (isset($options['life']) ? $options['life'] : SELF::DEFAULT_MAX_LIFE); + + ini_set('session.gc_divisor', 1); + ini_set('session.gc_maxlifetime', $this->ttl); + ini_set('session.gc_probability', 100); + + parent::__construct($options); + } + + public function read($id) { + + atsumi_Debug::startTimer('SESSION_READ'); + + $result = $this->cache->get($id, null, 'SESSION'); + + if(is_null($result)) + return ''; + + atsumi_Debug::record( + 'Session loaded from cache', + sf('Session ID: %s', $id), + $result, + 'SESSION_READ', + atsumi_Debug::AREA_SESSION, + $result + ); + return $result; + } + + public function write($id, $sessionData) { + + $this->cache->set($id, $sessionData, $this->ttl, 'SESSION'); + return true; + } + + public function destroy($id) { + $this->cache->set($id, null, -1, 'SESSION'); + return true; + } + + public function gc($maxlifetime) { + return true; + } +} \ No newline at end of file From c72070fa9f6b6332e102c309d4f6b6cb8524f83e Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 14 Apr 2015 17:26:32 +0000 Subject: [PATCH 153/164] few tweaks --- classes/core/debug/atsumi_Debug.php | 24 ++++++++++++++++++++---- classes/mvc/models/mvc_AbstractModel.php | 2 +- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/classes/core/debug/atsumi_Debug.php b/classes/core/debug/atsumi_Debug.php index b136da5..c663937 100755 --- a/classes/core/debug/atsumi_Debug.php +++ b/classes/core/debug/atsumi_Debug.php @@ -102,11 +102,17 @@ class atsumi_Debug { protected $timerMap = array(); /** - * Weather the debugger is active and recording information + * Is the debugger active and recording information * @var boolean */ protected $active = false; + /** + * If this is set to true then it will record data regardless of active + * @var boolean + */ + protected $record = false; + /** * A path to a log file used to output debug data * @var string @@ -179,7 +185,7 @@ protected static function getInstance() { } /** - * Returns weather or not the debugger is actively recording data + * Returns debugger active * @access public * @return boolean */ @@ -190,13 +196,17 @@ public function _getActive() { /* SET FUNCTIONS */ /** - * Sets weather or not the debugger is actively recording data + * Sets if debugger is actively recording data * @access public * @param boolean $val If the debugger should be recording [optional, default: true] */ public function _setActive($val = true) { $this->active = $val; } + + public function _setRecord($val = true) { + $this->record = $val; + } /** * Sets weather or not the debugger should automaticlly render on destruction @@ -351,7 +361,7 @@ public function _endTimer($key = null) { * @param string $area The area to add the data to */ public function _record($title, $desc, $data = null, $timer = false, $area = self::AREA_GENERAL) { - if(!$this->active) return; + if(!$this->active && !$this->record) return; if ($timer === true) $time = self::_endTimer(); @@ -885,6 +895,12 @@ public static function setViewData($data) { $args = func_get_args(); self::__callStatic(__FUNCTION__, $args); } + + public static function setRecord($data) { + $args = func_get_args(); + self::__callStatic(__FUNCTION__, $args); + } + public static function setParserData($data) { $args = func_get_args(); diff --git a/classes/mvc/models/mvc_AbstractModel.php b/classes/mvc/models/mvc_AbstractModel.php index bc44ae8..12a7fb3 100644 --- a/classes/mvc/models/mvc_AbstractModel.php +++ b/classes/mvc/models/mvc_AbstractModel.php @@ -259,7 +259,7 @@ function __toString () { } function output ($type = self::OUTPUT_FORMAT_ASSOC) { - + $this->preOutput($type); switch ($type) { From 8d72bac122e83c2e19d50a41c0cceef148ec5069 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Wed, 22 Apr 2015 13:40:02 +0000 Subject: [PATCH 154/164] added custom atrributes to text & hidden elements --- .../form/elements/widget_AbstractElement.php | 3 +++ .../form/elements/widget_HiddenElement.php | 19 +++++++++++++++---- .../form/elements/widget_TextElement.php | 19 +++++++++++++++++-- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/classes/widgets/form/elements/widget_AbstractElement.php b/classes/widgets/form/elements/widget_AbstractElement.php index 3aceb8e..1051b87 100644 --- a/classes/widgets/form/elements/widget_AbstractElement.php +++ b/classes/widgets/form/elements/widget_AbstractElement.php @@ -178,6 +178,9 @@ public function setLabel($in) { public function setError(Exception $e) { $this->errors[] = $e->getMessage(); } + public function setErrors($errors) { + $this->errors = $errors; + } public function setValue($input, $files = array()) { $this->value = isset($input[$this->name]) ? $input[$this->name] : null; diff --git a/classes/widgets/form/elements/widget_HiddenElement.php b/classes/widgets/form/elements/widget_HiddenElement.php index 7fe38f6..4bff36b 100644 --- a/classes/widgets/form/elements/widget_HiddenElement.php +++ b/classes/widgets/form/elements/widget_HiddenElement.php @@ -2,14 +2,25 @@ class widget_HiddenElement extends widget_AbstractElement { + protected $attributes = array(); + public function __construct($args) { + if (array_key_exists('attributes', $args) && is_array($args['attributes'])) + $this->attributes = $args['attributes']; } function renderElement() { - return(sf("", - $this->getName(), - parent::makeInputSafe($this->getValue()), - $this->getName() + + $attributesHtml = ''; + if (count($this->attributes)) + foreach ($this->attributes as $key => $val) + $attributesHtml.= sf(' %s="%s" ', $key, $val); + + return(sf("", + $this->getName(), + parent::makeInputSafe($this->getValue()), + $this->getName(), + $attributesHtml )); } } diff --git a/classes/widgets/form/elements/widget_TextElement.php b/classes/widgets/form/elements/widget_TextElement.php index 646e2b7..209cae9 100644 --- a/classes/widgets/form/elements/widget_TextElement.php +++ b/classes/widgets/form/elements/widget_TextElement.php @@ -10,6 +10,7 @@ class widget_TextElement extends widget_AbstractElement { protected $onFocus = null; protected $onBlur = null; protected $disabled = false; + protected $attributes = array(); protected $rows; public function __construct($args) { @@ -33,17 +34,31 @@ public function __construct($args) { if (array_key_exists('disabled', $args) && is_bool($args['disabled'])) $this->disabled = $args['disabled']; - + + + if (array_key_exists('attributes', $args) && is_array($args['attributes'])) + $this->attributes = $args['attributes']; + + + } function renderElement() { - return(sf('', + + $attributesHtml = ''; + if (count($this->attributes)) + foreach ($this->attributes as $key => $val) + $attributesHtml.= sf(' %s="%s" ', $key, $val); + + + return(sf('', $this->htmlType, $this->getName(), parent::makeInputSafe($this->getValue()), ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName(), $this->cssClassName, + $attributesHtml, strlen($this->placeholder)?sf(' placeholder="%s"', $this->placeholder):'', !is_null($this->onChange) && strlen($this->onChange)?sf(' onChange="%s"', $this->onChange):'', !is_null($this->onKeydown) && strlen($this->onKeydown)?sf(' onKeydown="%s"', $this->onKeydown):'', From a488e9113720c3b11efbe4bff22ba618d075a8e9 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Fri, 24 Apr 2015 16:21:59 +0000 Subject: [PATCH 155/164] added a few minor tweaks to forms --- classes/widgets/form/widget_Form.php | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/classes/widgets/form/widget_Form.php b/classes/widgets/form/widget_Form.php index f777260..c557bf0 100644 --- a/classes/widgets/form/widget_Form.php +++ b/classes/widgets/form/widget_Form.php @@ -252,16 +252,17 @@ public function getElement($elementName, $options = array()) { return $this->elementMap[$elementName]->render($options); } - public function getFormTop() { + public function getFormTop($options = array()) { - $html = sf('
', + $html = sf('', $this->name, $this->name, $this->name, $this->method, $this->ancorJump ? sf('%s#form_%s', $this->actionPath, $this->name) : $this->actionPath, $this->encoding, - count($this->cssClasses)?' '.implode(' ', $this->cssClasses):'' + count($this->cssClasses)?' '.implode(' ', $this->cssClasses):'', + isset($options['onSubmit'])?sf(' onsubmit="%s"',$options['onSubmit']):'' ); // add a hidden field to verify if this form has been posted @@ -276,14 +277,21 @@ public function getFormTop() { public function getFormBottom($options = array()) { // add the submit to the bottom of the form for now(will convert to element in next version) - $html = sfl('
', array_key_exists('rowClasses',$options)?' '.$options['rowClasses']:''); + $html = ''; + if (!isset($options['buttonOnly']) || $options['buttonOnly'] == false) + $html = sfl('
', array_key_exists('rowClasses',$options)?' '.$options['rowClasses']:''); + $html .= sfl(' %s%s', array_key_exists('preButtonHtml', $options)?$options['preButtonHtml']:'', array_key_exists('buttonClasses',$options)?' '.$options['buttonClasses']:'', - $this->name, $this->getSubmit(), + $this->name, + $this->getSubmit(), array_key_exists('postButtonHtml', $options)?$options['postButtonHtml']:'' ); - $html .= sfl('
'); + + if (!isset($options['buttonOnly']) || $options['buttonOnly'] == false) + $html .= sfl('
'); + $html .= sfl('
'); return $html; From 4d7119d6fdf6aee18f51f337ecf56fa2073981ce Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Wed, 29 Apr 2015 14:30:02 +0000 Subject: [PATCH 156/164] tweaks to form handling - can now pass html to be used in button --- classes/core/app/atsumi_AppHandler.php | 7 ------- classes/widgets/form/widget_Form.php | 6 +++--- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/classes/core/app/atsumi_AppHandler.php b/classes/core/app/atsumi_AppHandler.php index 2107915..eaa7814 100644 --- a/classes/core/app/atsumi_AppHandler.php +++ b/classes/core/app/atsumi_AppHandler.php @@ -370,13 +370,6 @@ public function render() { // tell the debug to print if required as connection will be closed by __destruct atsumi_Debug::printIfRequired(); - - // close the connection -// flush(); -// ob_flush(); -// if (session_id()) session_write_close(); - - // Time and execute the post render atsumi_Debug::startTimer('app:controller:postRender'); diff --git a/classes/widgets/form/widget_Form.php b/classes/widgets/form/widget_Form.php index c557bf0..498f2ae 100644 --- a/classes/widgets/form/widget_Form.php +++ b/classes/widgets/form/widget_Form.php @@ -281,11 +281,11 @@ public function getFormBottom($options = array()) { if (!isset($options['buttonOnly']) || $options['buttonOnly'] == false) $html = sfl('
', array_key_exists('rowClasses',$options)?' '.$options['rowClasses']:''); - $html .= sfl(' %s%s', + $html .= sfl(' %s%s', array_key_exists('preButtonHtml', $options)?$options['preButtonHtml']:'', - array_key_exists('buttonClasses',$options)?' '.$options['buttonClasses']:'', + array_key_exists('buttonClasses',$options)?' '.$options['buttonClasses']:'button button-submit', $this->name, - $this->getSubmit(), + array_key_exists('innerButtonHtml', $options)?$options['innerButtonHtml']:$this->getSubmit(), array_key_exists('postButtonHtml', $options)?$options['postButtonHtml']:'' ); From 376295ed56aa036aabec58128b7e075581fd6cae Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Fri, 1 May 2015 17:03:47 +0000 Subject: [PATCH 157/164] added onsubmit to form widgets. Added custom curl options to Http --- classes/utility/http/atsumi_Http.php | 7 ++++++- classes/widgets/form/widget_Form.php | 9 ++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/classes/utility/http/atsumi_Http.php b/classes/utility/http/atsumi_Http.php index 04eb906..afdd478 100644 --- a/classes/utility/http/atsumi_Http.php +++ b/classes/utility/http/atsumi_Http.php @@ -63,7 +63,7 @@ class atsumi_Http { const POST_METHOD_CURL = 1; const POST_METHOD_PECL = 2; - static public function post ($url, $fields, $method = 1, $httpHeaders = array(), &$httpRequestHeaders = false) { + static public function post ($url, $fields, $method = 1, $httpHeaders = array(), &$httpRequestHeaders = false, $options = array()) { switch ($method) { @@ -87,6 +87,11 @@ static public function post ($url, $fields, $method = 1, $httpHeaders = array(), if (count($httpHeaders)) curl_setopt($ch, CURLOPT_HTTPHEADER, $httpHeaders); + foreach ($options as $opt => $val) + curl_setopt($ch, $opt, $val); + + + $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); diff --git a/classes/widgets/form/widget_Form.php b/classes/widgets/form/widget_Form.php index 498f2ae..d115bcf 100644 --- a/classes/widgets/form/widget_Form.php +++ b/classes/widgets/form/widget_Form.php @@ -18,6 +18,7 @@ class widget_Form { private $ancorJump = false; // will the form jump to the ancor? private $actionPath = ''; private $cssClasses = array(); + private $onSubmit = null; public function __construct($formName = null, $autoLoadFormData = true) { if(!is_null($formName)) $this->setName($formName); @@ -192,6 +193,10 @@ public function setAncorJump($jump) { $this->ancorJump = $jump; } + public function setOnSubmit($in) { + $this->onSubmit = $in; + } + public function setSubmit($in) { if(!is_string($in)) throw new Exception("Submit text should be of type String"); @@ -262,7 +267,9 @@ public function getFormTop($options = array()) { $this->ancorJump ? sf('%s#form_%s', $this->actionPath, $this->name) : $this->actionPath, $this->encoding, count($this->cssClasses)?' '.implode(' ', $this->cssClasses):'', - isset($options['onSubmit'])?sf(' onsubmit="%s"',$options['onSubmit']):'' + isset($options['onSubmit'])? + sf(' onsubmit="%s"',$options['onSubmit']): + (isset($this->onSubmit)?sf(' onsubmit="%s"',$this->onSubmit):'') ); // add a hidden field to verify if this form has been posted From da3f1d6fd3618abd7533936d8ea457615d71ff96 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Mon, 4 May 2015 16:40:10 +0000 Subject: [PATCH 158/164] added option to not modify uris which is useful if you want your default locale not use a language uri prefix --- classes/mvc/models/core/mvc_LocaleModel.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/classes/mvc/models/core/mvc_LocaleModel.php b/classes/mvc/models/core/mvc_LocaleModel.php index 9825201..eb78a3e 100644 --- a/classes/mvc/models/core/mvc_LocaleModel.php +++ b/classes/mvc/models/core/mvc_LocaleModel.php @@ -9,6 +9,10 @@ class mvc_LocaleModel extends mvc_AbstractModel { ), 'country' => array( 'type' => 's', + ), + 'localiseUris' => array( + 'type' => 'b', + 'default' => true ) ); @@ -21,6 +25,9 @@ function getLocaleString () { } function uri ($uri) { + + if (!$this->get('localiseUris')) return $uri; + return sf('/%s%s%s', $this->getLocaleString(), substr($uri, 0, 1) !== '/'?'/':'', From eec1515026cae7a866550ab1601f72a3d78817a3 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Sun, 10 May 2015 19:17:29 +0000 Subject: [PATCH 159/164] tweak --- classes/core/debug/atsumi_Debug.php | 1 + 1 file changed, 1 insertion(+) diff --git a/classes/core/debug/atsumi_Debug.php b/classes/core/debug/atsumi_Debug.php index c663937..22717f3 100755 --- a/classes/core/debug/atsumi_Debug.php +++ b/classes/core/debug/atsumi_Debug.php @@ -368,6 +368,7 @@ public function _record($title, $desc, $data = null, $timer = false, $area = sel else if (is_string($timer)) { $time = self::_endTimer($timer); } + else $time = 0; $timerString = '(Process Time: '.$time.')'; From 52321fcc1bc96b9d5b731493bf8caea06b909239 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Wed, 13 May 2015 12:06:19 +0000 Subject: [PATCH 160/164] fixed bugs in sitemap handler --- classes/sitemap/sitemap_Handler.php | 85 ++++++++++++++++------------- 1 file changed, 48 insertions(+), 37 deletions(-) diff --git a/classes/sitemap/sitemap_Handler.php b/classes/sitemap/sitemap_Handler.php index d28510c..ac8e27a 100644 --- a/classes/sitemap/sitemap_Handler.php +++ b/classes/sitemap/sitemap_Handler.php @@ -6,51 +6,60 @@ class sitemap_Handler { /* deletes all urls for a host */ static public function purgeUrlsForHost($db, $host) { - $db->delete('sitemap', 'host = %s', $host); } /* adds a url to the sitemap table for inclusion next time the sitemap is generated */ static public function writeUrl(&$db, $loc, $lastMod = null, $changeFreq = null, $priority = null, $tableName = 'sitemap') { - $loc = trim($loc); - $checksum = crc32($loc); - - /* get the host name from url */ - preg_match('@^(?:http://)?([^/]+)@i', $loc, $matches); - $host = $matches[1]; - - $row = $db->select_1('select * from sitemap where checksum = \'%l\'::bigint AND loc = %s', $checksum, $loc); - - /* new location */ - if(is_null($row)) { - $db->insert( - 'sitemap', - 'checksum = \'%l\'::bigint', $checksum, - 'host = %s', $host, - 'loc = %s', $loc, - 'last_mod = %T', $lastMod, - 'change_freq = %S', $changeFreq, - 'priority = %l', is_null($priority)?'NULL':$priority - ); - - /* update as details have changed */ - } elseif($row->t_last_mod != $lastMod || $row->s_change_freq != $changeFreq || $row->s_priority != $priority) { - $db->update_1( - 'sitemap', - 'checksum = \'%l\'::bigint AND loc = %s', $row->i_checksum, $loc, - 'last_mod = %T', $lastMod, - 'change_freq = %S', $changeFreq, - 'priority = %l', is_null($priority)?'NULL':$priority + try { + + $loc = trim($loc); + $checksum = crc32($loc); + + /* get the host name from url */ + preg_match('@^(?:https?:\/\/)?((?:www\.)?[^\/]+)@i', $loc, $matches); + + $host = $matches[1]; + + $row = $db->select_1( + 'select * from sitemap where loc = %s', + $loc ); - + + /* new location */ + if(is_null($row)) { + $db->insert( + 'sitemap', + 'checksum = \'%l\'::bigint', $checksum, + 'host = %s', $host, + 'loc = %s', $loc, + 'last_mod = %T', $lastMod, + 'change_freq = %S', $changeFreq, + 'priority = %l', is_null($priority)?'NULL':$priority + ); + + /* update as details have changed */ + } elseif($row->t_last_mod != $lastMod || $row->s_change_freq != $changeFreq || $row->s_priority != $priority) { + $db->update_1( + 'sitemap', + 'loc = %s', $loc, + 'last_mod = %T', $lastMod, + 'change_freq = %S', $changeFreq, + 'priority = %l', is_null($priority)?'NULL':$priority + ); + + } + + /* nulling used variables (big sitemaps need every scrap of memory!) */ + $row = null; + $host = null; + $checksum = null; + $matches = null; + + } catch (Exception $e) { + Atsumi::error__listen($e); } - - /* nulling used variables (big sitemaps need every scrap of memory!) */ - $row = null; - $host = null; - $checksum = null; - $matches = null; } /* gets total number of urls for host */ @@ -73,9 +82,11 @@ static public function writeXml(&$db, $host, $xmlFilePath, $xmlUrlRoot, $maxUrls if(is_null($maxUrlsPerSitemap)) $maxUrlsPerSitemap = self::MAX_URLS_PER_SITEMAP; + /* how many items required */ $count = self::getUrlCount($db, $host, $tablename); + /* array of sitemap files */ $siteMapArr = array(); From 1268b0e72ed39331eb54dddd689d597a7d0155e3 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Tue, 19 May 2015 12:54:00 +0000 Subject: [PATCH 161/164] onChange hook --- .../form/elements/widget_RadioElement.php | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/classes/widgets/form/elements/widget_RadioElement.php b/classes/widgets/form/elements/widget_RadioElement.php index d2b93d1..736ad41 100644 --- a/classes/widgets/form/elements/widget_RadioElement.php +++ b/classes/widgets/form/elements/widget_RadioElement.php @@ -2,18 +2,33 @@ class widget_RadioElement extends widget_AbstractElement { private $options = array(); + private $onChange = null; public function __construct($args) { $this->options = $args['options']; + $this->onChange = isset($args['onChange'])?$args['onChange']:null; } function renderElement() { $html = ""; $elementValue = $this->getValue(); foreach($this->options as $value => $option) { - if(strval($elementValue) == strval($value)) - $html .= sfl('
', $this->getName(), $value, ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName(),$value, $this->getName(), $value, $option); - else - $html .= sfl('
', $this->getName(), $value, ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', $this->getName(),$value, $this->getName(), $value, $option); + + $html .= sfl( + '
'. + ' '. + ' '. + '
', + $this->getName(), + $value, + $this->onChange?sf(' onchange="%s"', $this->onChange):'', + strval($elementValue) == strval($value) ? sf('checked="checked"') : '', + ($this->tabindex) ? sf('tabindex="%s"', $this->tabindex) : '', + $this->getName(), + $value, + $this->getName(), + $value, + $option + ); } return sf("
%s
",$html); From 1c3d5f376277105ae918f615cbb1daa772e75c68 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Sat, 30 May 2015 16:37:06 +0000 Subject: [PATCH 162/164] uses php.ini to find upload location --- classes/widgets/form/elements/widget_FileElement.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/classes/widgets/form/elements/widget_FileElement.php b/classes/widgets/form/elements/widget_FileElement.php index 0868f2f..bf72ce5 100644 --- a/classes/widgets/form/elements/widget_FileElement.php +++ b/classes/widgets/form/elements/widget_FileElement.php @@ -19,8 +19,8 @@ public function setValue($input = null, $files = null) { if(is_array($uploadedFile) && $uploadedFile['error'] == 0) { $this->fileToken = md5(microtime()); - copy($uploadedFile['tmp_name'], "/tmp/".$this->fileToken); - $uploadedFile['tmp_name'] = "/tmp/".$this->fileToken; + copy($uploadedFile['tmp_name'], ini_get('upload_tmp_dir')."/".$this->fileToken); + $uploadedFile['tmp_name'] = ini_get('upload_tmp_dir')."/".$this->fileToken; $this->originalName = $uploadedFile['name']; $_SESSION[$this->fileToken] = $uploadedFile; $this->value = $uploadedFile; From 0fe911d331cd45e4f30a97139dc8409454ab5710 Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Thu, 11 Jun 2015 13:27:49 +0000 Subject: [PATCH 163/164] few updates to form elements, can get only errors, and also can clear() a file element --- classes/widgets/form/elements/widget_AbstractElement.php | 5 ++++- classes/widgets/form/elements/widget_FileElement.php | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/classes/widgets/form/elements/widget_AbstractElement.php b/classes/widgets/form/elements/widget_AbstractElement.php index 1051b87..3c28c1d 100644 --- a/classes/widgets/form/elements/widget_AbstractElement.php +++ b/classes/widgets/form/elements/widget_AbstractElement.php @@ -96,7 +96,10 @@ public function render($options = array()) { } - protected function renderErrors() { + public function hasErrors () { + return count($this->errors)?true:false; + } + public function renderErrors() { if(!count($this->errors) || !$this->submitted || $this->forceDefault) return; $div = "
"; $div .= "
    "; diff --git a/classes/widgets/form/elements/widget_FileElement.php b/classes/widgets/form/elements/widget_FileElement.php index bf72ce5..50b7f99 100644 --- a/classes/widgets/form/elements/widget_FileElement.php +++ b/classes/widgets/form/elements/widget_FileElement.php @@ -32,6 +32,12 @@ public function setValue($input = null, $files = null) { } } + public function clear() { + $this->fileToken = null; + $this->originalName = ''; + $this->value = null; + } + protected function outputGeneric() { return array( "name" => $this->getName(), From 7ac808df383d017f559fa71025eb89ab26b064fb Mon Sep 17 00:00:00 2001 From: Jimmy Forrester-Fellowes Date: Wed, 17 Jun 2015 17:18:36 +0000 Subject: [PATCH 164/164] https version of recaptcha --- classes/widgets/form/elements/widget_RecaptchaElement.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/classes/widgets/form/elements/widget_RecaptchaElement.php b/classes/widgets/form/elements/widget_RecaptchaElement.php index 4e2480c..718ab49 100644 --- a/classes/widgets/form/elements/widget_RecaptchaElement.php +++ b/classes/widgets/form/elements/widget_RecaptchaElement.php @@ -12,9 +12,9 @@ public function __construct($args) { } function renderElement() { $output = sf(' - + ', $this->publicKey, $this->publicKey);