cs-webapplibs-commits Mailing List for CS Web Application Libraries (Page 5)
Status: Beta
                
                Brought to you by:
                
                    crazedsanity
                    
                
            You can subscribe to this list here.
| 2009 | Jan | Feb | Mar | Apr | May | Jun | Jul | Aug (47) | Sep (8) | Oct (1) | Nov (3) | Dec | 
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2010 | Jan | Feb | Mar (1) | Apr | May (3) | Jun (14) | Jul (5) | Aug | Sep (5) | Oct (2) | Nov | Dec | 
| 2011 | Jan (15) | Feb (7) | Mar (1) | Apr (1) | May (1) | Jun | Jul (1) | Aug | Sep | Oct (2) | Nov (3) | Dec (1) | 
| 2012 | Jan (1) | Feb | Mar (1) | Apr | May | Jun | Jul | Aug | Sep | Oct | Nov | Dec | 
| 
      
      
      From: <cra...@us...> - 2009-08-20 16:21:57
      
     | 
| Revision: 122
          http://cs-webapplibs.svn.sourceforge.net/cs-webapplibs/?rev=122&view=rev
Author:   crazedsanity
Date:     2009-08-20 16:21:45 +0000 (Thu, 20 Aug 2009)
Log Message:
-----------
Moved cs_versionAbstract to cs-content, abstract class into abstract folder.
NOTE::: as stated in a recent commit to cs-content, moving cs_versionAbstract
into the cs-content project means the two projects are not inextricably
attached to one another, and that this project is truly an extension of
cs-content.  Previously, since everything in cs-content required
cs_versionAbstract{}, this project would ALWAYS have to be available (which
wasn't a problem when cs_versionAbstract{} was in it's own project,
"cs-versionparse").
/cs_authToken.class.php:	
	* fix location of cs_webapplibsAbstract.
/cs_version.abstract.class.php [MOVED]:
	* moved to cs-content/abstract/
/cs_webdblogger.class.php:
	* fix location of cs_webapplibsAbstract.
/cs_webdbupgrade.class.php:
	* fix location of cs_webapplibsAbstract.
/tests/testOfCSWebAppLibs.php:
	* test_version_basics() [DELETED]
	* test_check_higher() [DELETED]
	* middleTestClass{} [DELETED]
/tests/files/version* [DELETED]:
	* moved to cs-content for testing cs_versionAbstract
Modified Paths:
--------------
    trunk/0.3/cs_authToken.class.php
    trunk/0.3/cs_webdblogger.class.php
    trunk/0.3/cs_webdbupgrade.class.php
    trunk/0.3/tests/testOfCSWebAppLibs.php
Added Paths:
-----------
    trunk/0.3/abstract/
    trunk/0.3/abstract/cs_webapplibs.abstract.class.php
Removed Paths:
-------------
    trunk/0.3/cs_version.abstract.class.php
    trunk/0.3/cs_webapplibs.abstract.class.php
    trunk/0.3/tests/files/version1
    trunk/0.3/tests/files/version2
    trunk/0.3/tests/files/version3
Copied: trunk/0.3/abstract/cs_webapplibs.abstract.class.php (from rev 119, trunk/0.3/cs_webapplibs.abstract.class.php)
===================================================================
--- trunk/0.3/abstract/cs_webapplibs.abstract.class.php	                        (rev 0)
+++ trunk/0.3/abstract/cs_webapplibs.abstract.class.php	2009-08-20 16:21:45 UTC (rev 122)
@@ -0,0 +1,20 @@
+<?php
+/*
+ * Created on Aug 19, 2009
+ *
+ *  SVN INFORMATION:::
+ * -------------------
+ * Last Author::::::::: $Author$ 
+ * Current Revision:::: $Revision$ 
+ * Repository Location: $HeadURL$ 
+ * Last Updated:::::::: $Date$
+ */
+
+require_once(constant('LIBDIR') .'/cs-content/abstract/cs_version.abstract.class.php');
+
+
+abstract class cs_webapplibsAbstract extends cs_versionAbstract {
+	
+}
+
+?>
Modified: trunk/0.3/cs_authToken.class.php
===================================================================
--- trunk/0.3/cs_authToken.class.php	2009-08-20 15:52:11 UTC (rev 121)
+++ trunk/0.3/cs_authToken.class.php	2009-08-20 16:21:45 UTC (rev 122)
@@ -11,7 +11,7 @@
  */
 
 
-require_once(dirname(__FILE__) .'/cs_webapplibs.abstract.class.php');
+require_once(dirname(__FILE__) .'/abstract/cs_webapplibs.abstract.class.php');
 
 class cs_authToken extends cs_webapplibsAbstract {
 	
Deleted: trunk/0.3/cs_version.abstract.class.php
===================================================================
--- trunk/0.3/cs_version.abstract.class.php	2009-08-20 15:52:11 UTC (rev 121)
+++ trunk/0.3/cs_version.abstract.class.php	2009-08-20 16:21:45 UTC (rev 122)
@@ -1,398 +0,0 @@
-<?php
-/*
- * Created on January 01, 2009 by Dan Falconer
- * 
- * SVN INFORMATION:::
- * -------------------
- * Last Author::::::::: $Author$ 
- * Current Revision:::: $Revision$ 
- * Repository Location: $HeadURL$ 
- * Last Updated:::::::: $Date$
- */
-
-abstract class cs_versionAbstract {
-	
-	public $isTest = FALSE;
-	
-	
-	
-	private $versionFileLocation=null;
-	private $fullVersionString;
-	private $suffixList = array(
-		'ALPHA', 	//very unstable
-		'BETA', 	//kinda unstable, but probably useable
-		'RC'		//all known bugs fixed, searching for unknown ones
-	);
-	
-	
-	
-	abstract public function __construct();
-	
-	
-	
-	//=========================================================================
-	/**
-	 * Retrieve our version string from the VERSION file.
-	 */
-	final public function get_version($asArray=false) {
-		$retval = NULL;
-		
-		$this->auto_set_version_file();
-		
-		if(file_exists($this->versionFileLocation)) {
-			$myMatches = array();
-			$findIt = preg_match('/VERSION: (.+)/', file_get_contents($this->versionFileLocation), $matches);
-			
-			if($findIt == 1 && count($matches) == 2) {
-				$fullVersionString = $matches[1];
-				$versionInfo = $this->parse_version_string($fullVersionString);
-				$this->fullVersionString = $this->build_full_version_string($versionInfo);
-				
-				
-				if($asArray) {
-					$retval = $versionInfo;
-					$retval['version_string'] = $this->fullVersionString;
-				}
-				else {
-					$retval = $this->build_full_version_string($versionInfo);
-				}
-			}
-			else {
-				throw new exception(__METHOD__ .": failed to retrieve version string in file " .
-						"(". $this->versionFileLocation .")");
-			}
-		}
-		else {
-			throw new exception(__METHOD__ .": failed to retrieve version information, file " .
-					"(". $this->versionFileLocation .") does not exist or was not set");
-		}
-		
-		return($retval);
-	}//end get_version()
-	//=========================================================================
-	
-	
-	
-	//=========================================================================
-	public function __get($var) {
-		return($this->$var);
-	}//end __get()
-	//=========================================================================
-	
-	
-	
-	//=========================================================================
-	final public function get_project() {
-		$retval = NULL;
-		$this->auto_set_version_file();
-		if(file_exists($this->versionFileLocation)) {
-			$myMatches = array();
-			$findIt = preg_match('/PROJECT: (.+)/', file_get_contents($this->versionFileLocation), $matches);
-			
-			if($findIt == 1 && count($matches) == 2 && strlen($matches[1])) {
-				$retval = $matches[1];
-			}
-			else {
-				throw new exception(__METHOD__ .": failed to retrieve project string");
-			}
-		}
-		else {
-			throw new exception(__METHOD__ .": failed to retrieve project information");
-		}
-		
-		return($retval);
-	}//end get_project()
-	//=========================================================================
-	
-	
-	
-	//=========================================================================
-	public function set_version_file_location($location) {
-		if(file_exists($location)) {
-			$this->versionFileLocation = $location;
-		}
-		else {
-			throw new exception(__METHOD__ .": invalid location of VERSION file (". $location .")");
-		}
-	}//end set_version_file_location()
-	//=========================================================================
-	
-	
-	
-	//=========================================================================
-	protected function auto_set_version_file() {
-		if(!strlen($this->versionFileLocation)) {
-			$bt = debug_backtrace();
-			foreach($bt as $callNum=>$data) {
-				if(strlen($data['class'])) {
-					if($data['class'] != __CLASS__) {
-						$dir = dirname($data['file']);
-						if(preg_match('/tests$/', $dir)) {
-							$dir = preg_replace('/\/tests$/', '', $dir);
-						}
-						elseif(preg_match('/test$/', $dir)) {
-							$dir = preg_replace('/\/test$/', '', $dir);
-						}
-						break;
-					}
-				}
-				else {
-					throw new exception(__METHOD__ .": failed to locate the calling class in backtrace");
-				}
-			}
-			
-			if(file_exists($dir .'/VERSION')) {
-				$this->set_version_file_location($dir .'/VERSION');
-			}
-			else {
-				throw new exception(__METHOD__ .": failed to automatically set version file (tried ". $dir ."/VERSION)");
-			}
-		}
-	}//end auto_set_version_file()
-	//=========================================================================
-	
-	
-	
-	//=========================================================================
-	/**
-	 * 
-	 * TODO: add logic to split apart the suffix (i.e. "-ALPHA5" broken into "ALPHA" and "5").
-	 */
-	public function parse_version_string($version) {
-		if(is_string($version) && strlen($version) && preg_match('/\./', $version)) {
-			$version = preg_replace('/ /', '', $version);
-			
-			$pieces = explode('.', $version);
-			$retval = array(
-				'version_major'			=> $pieces[0],
-				'version_minor'			=> $pieces[1]
-			);
-			if(isset($pieces[2]) && strlen($pieces[2])) {
-				$retval['version_maintenance'] = $pieces[2];
-			}
-			else {
-				$retval['version_maintenance'] = 0;
-			}
-			
-			if(preg_match('/-/', $retval['version_maintenance'])) {
-				$bits = explode('-', $retval['version_maintenance']);
-				$retval['version_maintenance'] = $bits[0];
-				$suffix = $bits[1];
-			}
-			elseif(preg_match('/-/', $retval['version_minor'])) {
-				$bits = explode('-', $retval['version_minor']);
-				$retval['version_minor'] = $bits[0];
-				$suffix = $bits[1];
-			}
-			else {
-				$suffix = "";
-			}
-			$retval['version_suffix'] = $suffix;
-		}
-		else {
-			throw new exception(__METHOD__ .": invalid version string passed (". $version .")");
-		}
-		
-		return($retval);
-	}//end parse_version_string()
-	//=========================================================================
-	
-	
-	
-	//=========================================================================
-	public function build_full_version_string(array $versionInfo) {
-		$requiredIndexes = array(
-			'version_major', 'version_minor', 'version_maintenance', 'version_suffix'
-		);
-		
-		$missing="";
-		$count=0;
-		foreach($requiredIndexes as $indexName) {
-			if(isset($versionInfo[$indexName])) {
-				$count++;
-			}
-			else {
-				if(strlen($missing)) {
-					$missing .= ", ". $indexName;
-				}
-				else {
-					$missing = $indexName;
-				}
-			}
-		}
-		
-		if($count == count($requiredIndexes) && !strlen($missing)) {
-			$suffix = $versionInfo['version_suffix'];
-			unset($versionInfo['version_suffix']);
-			
-			$retval = "";
-			foreach($versionInfo as $name=>$value) {
-				if(strlen($retval)) {
-					$retval .= ".". $value;
-				}
-				else {
-					$retval = $value;
-				}
-			}
-			if(strlen($suffix)) {
-				$retval .= "-". $suffix;
-			}
-		}
-		else {
-			throw new exception(__METHOD__ .": missing indexes in given array (". $missing .")");
-		}
-		
-		return($retval);
-		
-	}//end build_full_version_string()
-	//=========================================================================
-	
-	
-	
-	//=========================================================================
-	public function is_higher_version($version, $checkIfHigher) {
-		$retval = FALSE;
-		$this->gfObj = new cs_globalFunctions;
-		if(!is_string($version) || !is_string($checkIfHigher)) {
-			throw new exception(__METHOD__ .": no valid version strings, version=(". $version ."), checkIfHigher=(". $checkIfHigher .")");
-		}
-		elseif($version == $checkIfHigher) {
-			$retval = FALSE;
-		}
-		else {
-			$curVersionArr = $this->parse_version_string($version);
-			$checkVersionArr = $this->parse_version_string($checkIfHigher);
-			
-			unset($curVersionArr['version_string'], $checkVersionArr['version_string']);
-			
-			
-			$curVersionSuffix = $curVersionArr['version_suffix'];
-			$checkVersionSuffix = $checkVersionArr['version_suffix'];
-			
-			
-			unset($curVersionArr['version_suffix']);
-			
-			foreach($curVersionArr as $index=>$versionNumber) {
-				$checkThis = $checkVersionArr[$index];
-				
-				if(is_numeric($checkThis) && is_numeric($versionNumber)) {
-					//set them as integers.
-					settype($versionNumber, 'int');
-					settype($checkThis, 'int');
-					
-					if($checkThis > $versionNumber) {
-						$retval = TRUE;
-						break;
-					}
-					elseif($checkThis == $versionNumber) {
-						//they're equal...
-					}
-					else {
-						//TODO: should there maybe be an option to throw an exception (freak out) here?
-					}
-				}
-				else {
-					throw new exception(__METHOD__ .": ". $index ." is not numeric in one of the strings " .
-						"(versionNumber=". $versionNumber .", checkThis=". $checkThis .")");
-				}
-			}
-			
-			//now deal with those damnable suffixes, but only if the versions are so far identical: if 
-			//	the "$checkIfHigher" is actually higher, don't bother (i.e. suffixes don't matter when
-			//	we already know there's a major, minor, or maintenance version that's also higher.
-			if($retval === FALSE) {
-				//EXAMPLE: $version="1.0.0-BETA3", $checkIfHigher="1.1.0"
-				// Moving from a non-suffixed version to a suffixed version isn't supported, but the inverse is:
-				//		i.e. (1.0.0-BETA3 to 1.0.0) is okay, but (1.0.0 to 1.0.0-BETA3) is NOT.
-				//		Also: (1.0.0-BETA3 to 1.0.0-BETA4) is okay, but (1.0.0-BETA4 to 1.0.0-BETA3) is NOT.
-				if(strlen($curVersionSuffix) && strlen($checkVersionSuffix) && $curVersionSuffix == $checkVersionSuffix) {
-					//matching suffixes.
-				}
-				elseif(strlen($curVersionSuffix) || strlen($checkVersionSuffix)) {
-					//we know the suffixes are there and DO match.
-					if(strlen($curVersionSuffix) && strlen($checkVersionSuffix)) {
-						//okay, here's where we do some crazy things...
-						$curVersionData = $this->parse_suffix($curVersionSuffix);
-						$checkVersionData = $this->parse_suffix($checkVersionSuffix);
-						
-						if($curVersionData['type'] == $checkVersionData['type']) {
-							//got the same suffix type (like "BETA"), check the number.
-							if($checkVersionData['number'] > $curVersionData['number']) {
-								//new version's suffix number higher than current...
-								$retval = TRUE;
-							}
-							elseif($checkVersionData['number'] == $curVersionData['number']) {
-								//new version's suffix number is EQUAL TO current...
-								$retval = FALSE;
-							}
-							else {
-								//new version's suffix number is LESS THAN current...
-								$retval = FALSE;
-							}
-						}
-						else {
-							//not the same suffix... see if the new one is higher.
-							$suffixValues = array_flip($this->suffixList);
-							if($suffixValues[$checkVersionData['type']] > $suffixValues[$curVersionData['type']]) {
-								$retval = TRUE;
-							}
-							else {
-								//current suffix type is higher...
-							}
-						}
-						
-					}
-					elseif(strlen($curVersionSuffix) && !strlen($checkVersionSuffix)) {
-						//i.e. "1.0.0-BETA1" to "1.0.0" --->>> OKAY!
-						$retval = TRUE;
-					}
-					elseif(!strlen($curVersionSuffix) && strlen($checkVersionSuffix)) {
-						//i.e. "1.0.0" to "1.0.0-BETA1" --->>> NOT ACCEPTABLE!
-					}
-				}
-				else {
-					//no suffix to care about
-				}
-			}
-		}
-		
-		return($retval);
-		
-	}//end is_higher_version()
-	//=========================================================================
-	
-	
-	
-	//=========================================================================
-	protected function parse_suffix($suffix) {
-		$retval = NULL;
-		if(strlen($suffix)) {
-			//determine what kind it is.
-			foreach($this->suffixList as $type) {
-				if(preg_match('/^'. $type .'/', $suffix)) {
-					$checkThis = preg_replace('/^'. $type .'/', '', $suffix);
-					if(strlen($checkThis) && is_numeric($checkThis)) {
-						//oooh... it's something like "BETA3"
-						$retval = array(
-							'type'		=> $type,
-							'number'	=> $checkThis
-						);
-					}
-					else {
-						throw new exception(__METHOD__ .": invalid suffix (". $suffix .")");
-					}
-					break;
-				}
-			}
-		}
-		else {
-			throw new exception(__METHOD__ .": invalid suffix (". $suffix .")");
-		}
-		
-		return($retval);
-	}//end parse_suffix()
-	//=========================================================================
-	
-	
-}
-?>
\ No newline at end of file
Deleted: trunk/0.3/cs_webapplibs.abstract.class.php
===================================================================
--- trunk/0.3/cs_webapplibs.abstract.class.php	2009-08-20 15:52:11 UTC (rev 121)
+++ trunk/0.3/cs_webapplibs.abstract.class.php	2009-08-20 16:21:45 UTC (rev 122)
@@ -1,20 +0,0 @@
-<?php
-/*
- * Created on Aug 19, 2009
- *
- *  SVN INFORMATION:::
- * -------------------
- * Last Author::::::::: $Author$ 
- * Current Revision:::: $Revision$ 
- * Repository Location: $HeadURL$ 
- * Last Updated:::::::: $Date$
- */
-
-require_once(dirname(__FILE__) .'/cs_version.abstract.class.php');
-
-
-abstract class cs_webapplibsAbstract extends cs_versionAbstract {
-	
-}
-
-?>
Modified: trunk/0.3/cs_webdblogger.class.php
===================================================================
--- trunk/0.3/cs_webdblogger.class.php	2009-08-20 15:52:11 UTC (rev 121)
+++ trunk/0.3/cs_webdblogger.class.php	2009-08-20 16:21:45 UTC (rev 122)
@@ -27,7 +27,7 @@
 
 //NOTE::: this class **REQUIRES** cs-content for its "cs_phpDB" class.
 
-require_once(dirname(__FILE__) .'/cs_webapplibs.abstract.class.php');
+require_once(dirname(__FILE__) .'/abstract/cs_webapplibs.abstract.class.php');
 require_once(dirname(__FILE__) .'/cs_webdbupgrade.class.php');
 
 class cs_webdblogger extends cs_webapplibsAbstract {
Modified: trunk/0.3/cs_webdbupgrade.class.php
===================================================================
--- trunk/0.3/cs_webdbupgrade.class.php	2009-08-20 15:52:11 UTC (rev 121)
+++ trunk/0.3/cs_webdbupgrade.class.php	2009-08-20 16:21:45 UTC (rev 122)
@@ -12,7 +12,7 @@
  * 
  */
 
-require_once(dirname(__FILE__) .'/cs_webapplibs.abstract.class.php');
+require_once(dirname(__FILE__) .'/abstract/cs_webapplibs.abstract.class.php');
 		require_once(dirname(__FILE__) .'/cs_webdblogger.class.php');
 
 class cs_webdbupgrade extends cs_webapplibsAbstract {
Deleted: trunk/0.3/tests/files/version1
===================================================================
--- trunk/0.3/tests/files/version1	2009-08-20 15:52:11 UTC (rev 121)
+++ trunk/0.3/tests/files/version1	2009-08-20 16:21:45 UTC (rev 122)
@@ -1,3 +0,0 @@
-
-PROJECT: test1
-VERSION: 0.1.2-ALPHA8754
\ No newline at end of file
Deleted: trunk/0.3/tests/files/version2
===================================================================
--- trunk/0.3/tests/files/version2	2009-08-20 15:52:11 UTC (rev 121)
+++ trunk/0.3/tests/files/version2	2009-08-20 16:21:45 UTC (rev 122)
@@ -1,3 +0,0 @@
-
-PROJECT: test2
-VERSION: 5.4
\ No newline at end of file
Deleted: trunk/0.3/tests/files/version3
===================================================================
--- trunk/0.3/tests/files/version3	2009-08-20 15:52:11 UTC (rev 121)
+++ trunk/0.3/tests/files/version3	2009-08-20 16:21:45 UTC (rev 122)
@@ -1,3 +0,0 @@
-
-PROJECT: test3 stuff
-VERSION: 5.4.3-BETA5543
\ No newline at end of file
Modified: trunk/0.3/tests/testOfCSWebAppLibs.php
===================================================================
--- trunk/0.3/tests/testOfCSWebAppLibs.php	2009-08-20 15:52:11 UTC (rev 121)
+++ trunk/0.3/tests/testOfCSWebAppLibs.php	2009-08-20 16:21:45 UTC (rev 122)
@@ -11,7 +11,6 @@
  * $LastChangedRevision$
  */
 
-require_once(dirname(__FILE__) .'/../cs_version.abstract.class.php');
 require_once(dirname(__FILE__) .'/../cs_authToken.class.php');
 
 class testOfCSWebAppLibs extends UnitTestCase {
@@ -24,108 +23,8 @@
 	//--------------------------------------------------------------------------
 	
 	
-	//--------------------------------------------------------------------------
-	function test_version_basics() {
-		
-		$tests = array(
-			'files/version1'	=> array(
-				'0.1.2-ALPHA8754',
-				'test1',
-				array(
-					'version_major'			=> 0,
-					'version_minor'			=> 1,
-					'version_maintenance'	=> 2,
-					'version_suffix'		=> 'ALPHA8754'
-				)
-			),
-			'files/version2'	=> array(
-				'5.4.0',
-				'test2',
-				array(
-					'version_major'			=> 5,
-					'version_minor'			=> 4,
-					'version_maintenance'	=> 0,
-					'version_suffix'		=> null
-				)
-			),
-			'files/version3'	=> array(
-				'5.4.3-BETA5543',
-				'test3 stuff',
-				array(
-					'version_major'			=> 5,
-					'version_minor'			=> 4,
-					'version_maintenance'	=> 3,
-					'version_suffix'		=> 'BETA5543'
-				)
-			)
-		);
-		
-		foreach($tests as $fileName=>$expectedArr) {
-			$ver = new middleTestClass();
-			$ver->set_version_file_location(dirname(__FILE__) .'/'. $fileName);
-			
-			$this->assertEqual($expectedArr[0], $ver->get_version(), "Failed to match string from file (". $fileName .")");
-			$this->assertEqual($expectedArr[1], $ver->get_project(), "Failed to match project from file (". $fileName .")");
-			
-			//now check that pulling the version as an array is the same...
-			$checkItArr = $ver->get_version(true);
-			$expectThis = $expectedArr[2];
-			$expectThis['version_string'] = $expectedArr[0];
-		}
-	}//end test_version_basics()
-	//--------------------------------------------------------------------------
 	
-	
-	
 	//--------------------------------------------------------------------------
-	function test_check_higher() {
-		
-		//NOTE: the first item should ALWAYS be higher.
-		$tests = array(
-			'basic, no suffix'	=> array('1.0.1', '1.0.0'),
-			'basic + suffix'	=> array('1.0.0-ALPHA1', '1.0.0-ALPHA0'),
-			'basic w/o maint'	=> array('1.0.1', '1.0'),
-			'suffix check'		=> array('1.0.0-BETA1', '1.0.0-ALPHA1'),
-			'suffix check2'		=> array('1.0.0-ALPHA10', '1.0.0-ALPHA1'),
-			'suffix check3'		=> array('1.0.1', '1.0.0-RC1')
-		);
-		
-		foreach($tests as $name=>$checkData) {
-			$ver = new middleTestClass;
-			$this->assertTrue($ver->is_higher_version($checkData[1], $checkData[0]));
-			$this->assertFalse($ver->is_higher_version($checkData[0], $checkData[1]));
-		}
-		
-		//now check to ensure there's no problem with parsing equivalent versions.
-		$tests = array(
-			'no suffix'				=> array('1.0', '1.0.0'),
-			'no maint + suffix'		=> array('1.0-ALPHA1', '1.0.0-ALPHA1'),
-			'no maint + BETA'		=> array('1.0-BETA5555', '1.0.0-BETA5555'),
-			'no maint + RC'			=> array('1.0-RC33', '1.0.0-RC33'),
-			'maint with space'		=> array('1.0-RC  33', '1.0.0-RC33'),
-			'extra spaces'			=> array(' 1.0   ', '1.0.0')
-		);
-		foreach($tests as $name=>$checkData) {
-			$ver = new middleTestClass;
-			
-			//rip apart & recreate first version to test against the expected...
-			$derivedFullVersion = $ver->build_full_version_string($ver->parse_version_string($checkData[0]));
-			$this->assertEqual($derivedFullVersion, $checkData[1], "TEST=(". $name ."): derived version " .
-					"(". $derivedFullVersion .") doesn't match expected (". $checkData[1] .")");
-			
-			//now rip apart & recreate the expected version (second) and make sure it matches itself.
-			$derivedFullVersion = $ver->build_full_version_string($ver->parse_version_string($checkData[1]));
-			$this->assertEqual($derivedFullVersion, $checkData[1], "TEST=(". $name ."): derived version " .
-					"(". $derivedFullVersion .") doesn't match expected (". $checkData[1] .")");
-		}
-		
-		
-	}//end test_check_higher()
-	//--------------------------------------------------------------------------
-	
-	
-	
-	//--------------------------------------------------------------------------
 	private function create_dbconn() {
 		$dbParams = array(
 			'host'		=> constant('DB_PG_HOST'),
@@ -249,10 +148,6 @@
 }
 
 
-class middleTestClass extends cs_versionAbstract {
-	function __construct(){}
-} 
-
 class authTokenTester extends cs_authToken {
 	public $isTest=true;
 	
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
 | 
| 
      
      
      From: <cra...@us...> - 2009-08-20 15:52:17
      
     | 
| Revision: 121
          http://cs-webapplibs.svn.sourceforge.net/cs-webapplibs/?rev=121&view=rev
Author:   crazedsanity
Date:     2009-08-20 15:52:11 +0000 (Thu, 20 Aug 2009)
Log Message:
-----------
More tests, add "last_updated" to tokens.
/cs_authToken.class.php:
	* create_token():
		-- call _generic_update() to do the update statement.
	* update_token_uses():
		-- call _generic_update() to do the update statement.
	* get_token_data():
		-- ARG CHANGE: NEW ARG: #2 ($onlyNonExpired=true)
		-- add ability to get ANY token's data (for unit testing & eventually 
		for logging purposes).
	* remove_expired_tokens() [NEW]:
		-- destroys tokens that are expired (doesn't do any checking as far
		as whether or not there are uses left).
	* _generic_update() [NEW]:
		-- besides the update string given, it also updates the (new)
		"last_updated" column.
		-- NOTE::: this was done so that the cs_sessionDB{} class from
		cs-content could potentially just call methods in this class to
		create & expire records... its just an idea for now.
/setup/schema.mysql.sql:
	* cswal_auth_token_table:
		-- added "last_updated" (timestamp) column.
		-- NOTE::: didn't set the default as NOW() because MySQL won't allow
		more than one timestamp field to have that as the default...?
/setup/schema.pgsql.sql:
	* mirrored changes to mysql schema.
/tests/testOfCSWebAppLibs.php:
	* test_token_basics():
		-- removed duplicate code into a private method, "basic_token_tests()"
		-- added test for creating an already-expired token.
		-- TODO: retrieve the token's data before authenticating, then test
		to see if it was removed afterward.
	* basic_token_tests() [NEW]:
		-- set of tests that are performed for pretty much every token
		created, so code was moved here so it is more standardized.
Modified Paths:
--------------
    trunk/0.3/cs_authToken.class.php
    trunk/0.3/setup/schema.mysql.sql
    trunk/0.3/setup/schema.pgsql.sql
    trunk/0.3/tests/testOfCSWebAppLibs.php
Modified: trunk/0.3/cs_authToken.class.php
===================================================================
--- trunk/0.3/cs_authToken.class.php	2009-08-20 14:55:21 UTC (rev 120)
+++ trunk/0.3/cs_authToken.class.php	2009-08-20 15:52:11 UTC (rev 121)
@@ -105,9 +105,7 @@
 			//now that we have the ID, let's create the real has string.
 			$finalHash = $this->create_hash_string($tokenId, $uid, $checksum, $stringToHash);
 			
-			$this->db->run_update("UPDATE ". $this->table ." SET token='". $finalHash ."' WHERE " .
-					"auth_token_id=". $tokenId);
-			
+			$this->_generic_update($tokenId, "token='". $finalHash ."'");
 			$tokenInfo = array(
 				'id'	=> $tokenId,
 				'hash'	=> $finalHash
@@ -134,11 +132,8 @@
 	 * @return (exception)	FAIL: exception denotes problem
 	 */
 	protected function update_token_uses($tokenId) {
-		
 		try {
-			$sql = "UPDATE ". $this->table ." SET total_uses= total_uses+1 " .
-					"WHERE auth_token_id=". $tokenId;
-			$updateRes = $this->db->run_update($sql);
+			$updateRes = $this->_generic_update($tokenId, "total_uses= total_uses+1");
 		}
 		catch(exception $e) {
 			throw new exception(__METHOD__ .": failed to update usage count::: ". $e->getMessage());
@@ -260,10 +255,15 @@
 	 * @return (array)		PASS: contains data about the given ID
 	 * @return (exception)	FAIL: exception contains error details.
 	 */
-	protected function get_token_data($tokenId) {
+	protected function get_token_data($tokenId, $onlyNonExpired=true) {
 		try {
-			$data = $this->db->run_query("SELECT * FROM ". $this->table ." WHERE auth_token_id=". $tokenId
-					." AND expiration::date >= CURRENT_DATE", 'auth_token_id');
+			$sql = "SELECT * FROM ". $this->table ." WHERE auth_token_id=". $tokenId;
+			if($onlyNonExpired === true) {
+				$sql .= " AND expiration::date >= CURRENT_DATE";
+			}
+			
+			$data = $this->db->run_query($sql, 'auth_token_id');
+			
 			if(is_array($data) && count($data) == 1) {
 				$tokenData = $data;
 			}
@@ -281,5 +281,49 @@
 	}//end get_token_data();
 	//=========================================================================
 	
+	
+	
+	//=========================================================================
+	/**
+	 * Deletes any tokens that are past expiration (does not test for total vs. 
+	 * max uses; authenticate_token() does that).
+	 * 
+	 * @param (null)		(void)
+	 */
+	public function remove_expired_tokens() {
+		$sql = "SELECT * FROM ". $this->table ." WHERE NOW() > expiration";
+		
+		try {
+			$data = $this->db->run_query($sql, 'auth_token_id');
+			
+			if(is_array($data)) {
+				foreach($data as $tokenId => $tokenData) {
+					//TODO: add logging here?
+					$this->destroy_token($tokenId);
+				}
+			}
+		}
+		catch(exception $e) {
+			throw new exception(__METHOD__ .": error encountered while expiring tokens::: ". $e->getMessage());
+		}
+	}//end remove_expired_tokens()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	private function _generic_update($tokenId, $updateString) {
+		try {
+			$sql = "UPDATE ". $this->table ." SET ". $updateString .", last_updated=NOW() " .
+					"WHERE auth_token_id=". $tokenId;
+			$updateRes = $this->db->run_update($sql);
+		}
+		catch(exception $e) {
+			throw new exception("failed to update token::: ". $e->getMessage());
+		}
+		return($updateRes);
+	}//end generic_update()
+	//=========================================================================
+	
 }
 ?>
Modified: trunk/0.3/setup/schema.mysql.sql
===================================================================
--- trunk/0.3/setup/schema.mysql.sql	2009-08-20 14:55:21 UTC (rev 120)
+++ trunk/0.3/setup/schema.mysql.sql	2009-08-20 15:52:11 UTC (rev 121)
@@ -156,5 +156,6 @@
 	max_uses integer DEFAULT NULL,
 	total_uses integer NOT NULL DEFAULT 0,
 	creation timestamp NOT NULL DEFAULT NOW(),
+	last_updated timestamp,
 	expiration timestamp NOT NULL
 );
\ No newline at end of file
Modified: trunk/0.3/setup/schema.pgsql.sql
===================================================================
--- trunk/0.3/setup/schema.pgsql.sql	2009-08-20 14:55:21 UTC (rev 120)
+++ trunk/0.3/setup/schema.pgsql.sql	2009-08-20 15:52:11 UTC (rev 121)
@@ -100,6 +100,7 @@
 	max_uses integer DEFAULT NULL,
 	total_uses integer NOT NULL DEFAULT 0,
 	creation timestamp NOT NULL DEFAULT NOW(),
+	last_updated timestamp,
 	expiration timestamp NOT NULL
 );
 
Modified: trunk/0.3/tests/testOfCSWebAppLibs.php
===================================================================
--- trunk/0.3/tests/testOfCSWebAppLibs.php	2009-08-20 14:55:21 UTC (rev 120)
+++ trunk/0.3/tests/testOfCSWebAppLibs.php	2009-08-20 15:52:11 UTC (rev 121)
@@ -164,6 +164,7 @@
 	//--------------------------------------------------------------------------
 	
 	
+	
 	//--------------------------------------------------------------------------
 	function test_token_basics() {
 		$db = $this->create_dbconn();
@@ -185,12 +186,7 @@
 		{
 			//Generic test to ensure we get the appropriate data back.
 			$tokenData = $tok->create_token(1, 'test', 'abc123', null, 1);
-			$this->assertTrue(is_array($tokenData));
-			$this->assertTrue((count($tokenData) == 2));
-			$this->assertTrue(isset($tokenData['id']));
-			$this->assertTrue(isset($tokenData['hash']));
-			$this->assertTrue(($tokenData['id'] > 0));
-			$this->assertTrue((strlen($tokenData['hash']) == 32));
+			$this->basic_token_tests($tokenData, 1, 'test');
 			
 			if(!$this->assertEqual($tok->authenticate_token($tokenData['id'], 'test', $tokenData['hash']), 1)) {
 				$this->gfObj->debug_print($tok->tokenData($tokenData['id']),1);
@@ -205,12 +201,7 @@
 		{
 			//Generic test to ensure we get the appropriate data back.
 			$tokenData = $tok->create_token(1, 'test', 'abc123', '2 years');
-			$this->assertTrue(is_array($tokenData));
-			$this->assertTrue((count($tokenData) == 2));
-			$this->assertTrue(isset($tokenData['id']));
-			$this->assertTrue(isset($tokenData['hash']));
-			$this->assertTrue(($tokenData['id'] > 0));
-			$this->assertTrue((strlen($tokenData['hash']) == 32));
+			$this->basic_token_tests($tokenData, 1, 'test');
 			
 			$this->assertEqual($tok->authenticate_token($tokenData['id'], 'test', $tokenData['hash']), 1);
 		}
@@ -218,17 +209,43 @@
 		//try to create a token with max_uses of 0.
 		{
 			$tokenData = $tok->create_token(2, 'test', 'xxxxyyyyyxxxx', null, 0);
+			$this->basic_token_tests($tokenData, 2, 'test');
 			$checkData = $tok->tokenData($tokenData['id']);
 			$checkData = $checkData[$tokenData['id']];
 			
 			$this->assertTrue(is_array($checkData));
-			if(!$this->assertEqual($tokenData['id'], $checkData['auth_token_id'])) {
-				$this->gfObj->debug_print($checkData);
-			}
+			$this->assertEqual($tokenData['id'], $checkData['auth_token_id']);
 			$this->assertEqual($checkData['max_uses'], null);
 		}
+		
+		//try creating a token that is purposely expired, make sure it exists, then make sure authentication fails.
+		{
+			$tokenData = $tok->create_token(88, 'test', 'This is a big old TEST', '-3 days');
+			if($this->assertTrue(is_array($tokenData))) {
+				$this->basic_token_tests($tokenData, 88, 'This is a big old TEST');
+				$this->assertFalse($tok->authenticate_token($tokenData['id'], 'test', $tokenData['hash']));
+			}
+		}
 	}//end test_token_basics()
 	//--------------------------------------------------------------------------
+	
+	
+	
+	//--------------------------------------------------------------------------
+	private function basic_token_tests(array $tokenData, $uid, $checksum) {
+		
+		if($this->assertTrue(is_array($tokenData)) && $this->assertTrue(is_numeric($uid)) && $this->assertTrue(strlen($checksum))) {
+			
+			$this->assertTrue(is_array($tokenData));
+			$this->assertTrue((count($tokenData) == 2));
+			$this->assertTrue(isset($tokenData['id']));
+			$this->assertTrue(isset($tokenData['hash']));
+			$this->assertTrue(($tokenData['id'] > 0));
+			$this->assertTrue((strlen($tokenData['hash']) == 32));
+		}
+		
+	}//end basic_token_tests()
+	//--------------------------------------------------------------------------
 }
 
 
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
 | 
| 
      
      
      From: <cra...@us...> - 2009-08-20 14:55:30
      
     | 
| Revision: 120
          http://cs-webapplibs.svn.sourceforge.net/cs-webapplibs/?rev=120&view=rev
Author:   crazedsanity
Date:     2009-08-20 14:55:21 +0000 (Thu, 20 Aug 2009)
Log Message:
-----------
Drop tables (instead of DB), extra tests.
/cs_authToken.class.php:
	* __construct():
		-- create an instance of cs_webdbupgrade{} to see if an upgrade needs
		to be performed.
		-- NOTE::: this can be removed simply by adding logging, as
		cs_webdblogger::__construct() checks for upgrades.
/tests/testOfCSWebAppLibs.php:
	* create_dbconn() RENAMED FROM create_db()
	* remove_tables() [NEW]:
		-- does a DROP ... CASCADE on all tables belonging to cs-webapplibs
	* test_token_basics():
		-- updated references to use create_dbconn()
		-- move tests around slighly so I can view the database after & know
		for sure that the record that had only 1 use is actually missing
		(because there is a gap in auth_token_id's).
		-- test for creating a token with 0 max uses (to see that the invalid
		value for max_uses is ignored).
Modified Paths:
--------------
    trunk/0.3/cs_authToken.class.php
    trunk/0.3/tests/testOfCSWebAppLibs.php
Modified: trunk/0.3/cs_authToken.class.php
===================================================================
--- trunk/0.3/cs_authToken.class.php	2009-08-20 13:57:54 UTC (rev 119)
+++ trunk/0.3/cs_authToken.class.php	2009-08-20 14:55:21 UTC (rev 120)
@@ -41,6 +41,8 @@
 		}
 		$this->gfObj = new cs_globalFunctions();
 		
+		$upg = new cs_webdbupgrade(dirname(__FILE__) .'/VERSION', dirname(__FILE__) .'/upgrades/upgrade.xml');
+		$upg->check_versions(true);
 	}//end __construct()
 	//=========================================================================
 	
Modified: trunk/0.3/tests/testOfCSWebAppLibs.php
===================================================================
--- trunk/0.3/tests/testOfCSWebAppLibs.php	2009-08-20 13:57:54 UTC (rev 119)
+++ trunk/0.3/tests/testOfCSWebAppLibs.php	2009-08-20 14:55:21 UTC (rev 120)
@@ -126,7 +126,7 @@
 	
 	
 	//--------------------------------------------------------------------------
-	private function create_db() {
+	private function create_dbconn() {
 		$dbParams = array(
 			'host'		=> constant('DB_PG_HOST'),
 			'dbname'	=> constant('DB_PG_DBNAME'),
@@ -141,9 +141,33 @@
 	//--------------------------------------------------------------------------
 	
 	
+	
 	//--------------------------------------------------------------------------
+	private function remove_tables() {
+		$tableList = array(
+			'cswal_auth_token_table', 'cswal_version_table', 'cswdbl_attribute_table', 
+			'cswdbl_category_table', 'cswdbl_class_table', 'cswdbl_event_table', 
+			'cswdbl_log_attribute_table', 'cswdbl_log_table', 
+		);
+		
+		$db = $this->create_dbconn();
+		foreach($tableList as $name) {
+			try {
+				$db->run_update("DROP TABLE ". $name ." CASCADE", true);
+			}
+			catch(exception $e) {
+				//force an error.
+				$this->assertTrue(false, "Error while dropping (". $name .")::: ". $e->getMessage());
+			}
+		}
+	}//end remove_tables()
+	//--------------------------------------------------------------------------
+	
+	
+	//--------------------------------------------------------------------------
 	function test_token_basics() {
-		$db = $this->create_db();
+		$db = $this->create_dbconn();
+		$this->remove_tables();
 		$tok = new authTokenTester($db);
 		
 		//Generic test to ensure we get the appropriate data back.
@@ -157,11 +181,10 @@
 		
 		$this->assertEqual($tok->authenticate_token($tokenData['id'], 'test', $tokenData['hash']), 1);
 		
-		
-		//now create a token with a maximum lifetime...
+		//create a token with only 1 available use and try to authenticate it twice.
 		{
 			//Generic test to ensure we get the appropriate data back.
-			$tokenData = $tok->create_token(1, 'test', 'abc123', '2 years');
+			$tokenData = $tok->create_token(1, 'test', 'abc123', null, 1);
 			$this->assertTrue(is_array($tokenData));
 			$this->assertTrue((count($tokenData) == 2));
 			$this->assertTrue(isset($tokenData['id']));
@@ -169,13 +192,19 @@
 			$this->assertTrue(($tokenData['id'] > 0));
 			$this->assertTrue((strlen($tokenData['hash']) == 32));
 			
-			$this->assertEqual($tok->authenticate_token($tokenData['id'], 'test', $tokenData['hash']), 1);
+			if(!$this->assertEqual($tok->authenticate_token($tokenData['id'], 'test', $tokenData['hash']), 1)) {
+				$this->gfObj->debug_print($tok->tokenData($tokenData['id']),1);
+			}
+			if(!$this->assertTrue(($tok->authenticate_token($tokenData['id'], 'test', $tokenData['hash']) === null), "Able to authenticate twice on a token with only 1 use")) {
+				$this->gfObj->debug_print($tok->tokenData($tokenData['id']));
+			}
 		}
 		
-		//create a token with only 1 available use and try to authenticate it twice.
+		
+		//now create a token with a maximum lifetime...
 		{
 			//Generic test to ensure we get the appropriate data back.
-			$tokenData = $tok->create_token(1, 'test', 'abc123', null, 1);
+			$tokenData = $tok->create_token(1, 'test', 'abc123', '2 years');
 			$this->assertTrue(is_array($tokenData));
 			$this->assertTrue((count($tokenData) == 2));
 			$this->assertTrue(isset($tokenData['id']));
@@ -183,12 +212,20 @@
 			$this->assertTrue(($tokenData['id'] > 0));
 			$this->assertTrue((strlen($tokenData['hash']) == 32));
 			
-			if(!$this->assertEqual($tok->authenticate_token($tokenData['id'], 'test', $tokenData['hash']), 1)) {
-				$this->gfObj->debug_print($tok->tokenData($tokenData['id']),1);
+			$this->assertEqual($tok->authenticate_token($tokenData['id'], 'test', $tokenData['hash']), 1);
+		}
+		
+		//try to create a token with max_uses of 0.
+		{
+			$tokenData = $tok->create_token(2, 'test', 'xxxxyyyyyxxxx', null, 0);
+			$checkData = $tok->tokenData($tokenData['id']);
+			$checkData = $checkData[$tokenData['id']];
+			
+			$this->assertTrue(is_array($checkData));
+			if(!$this->assertEqual($tokenData['id'], $checkData['auth_token_id'])) {
+				$this->gfObj->debug_print($checkData);
 			}
-			if(!$this->assertTrue(($tok->authenticate_token($tokenData['id'], 'test', $tokenData['hash']) === null), "Able to authenticate twice on a token with only 1 use")) {
-				$this->gfObj->debug_print($tok->tokenData($tokenData['id']));
-			}
+			$this->assertEqual($checkData['max_uses'], null);
 		}
 	}//end test_token_basics()
 	//--------------------------------------------------------------------------
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
 | 
| 
      
      
      From: <cra...@us...> - 2009-08-20 13:58:05
      
     | 
| Revision: 119
          http://cs-webapplibs.svn.sourceforge.net/cs-webapplibs/?rev=119&view=rev
Author:   crazedsanity
Date:     2009-08-20 13:57:54 +0000 (Thu, 20 Aug 2009)
Log Message:
-----------
Combine tests into a single batch of tests: this allows for doing database changes without affecting other tests (and potentially rebuilding the db after every test).
Added Paths:
-----------
    trunk/0.3/tests/testOfCSWebAppLibs.php
Removed Paths:
-------------
    trunk/0.3/tests/testOfAuthToken.php
    trunk/0.3/tests/testOfCSVersionParse.php
Deleted: trunk/0.3/tests/testOfAuthToken.php
===================================================================
--- trunk/0.3/tests/testOfAuthToken.php	2009-08-20 02:20:49 UTC (rev 118)
+++ trunk/0.3/tests/testOfAuthToken.php	2009-08-20 13:57:54 UTC (rev 119)
@@ -1,104 +0,0 @@
-<?php
-/*
- * Created on Jan 25, 2009
- * 
- * FILE INFORMATION:
- * 
- * $HeadURL$
- * $Id$
- * $LastChangedDate$
- * $LastChangedBy$
- * $LastChangedRevision$
- */
-
-require_once(dirname(__FILE__) .'/../cs_authToken.class.php');
-
-class testOfCSAuthToken extends UnitTestCase {
-	
-	//--------------------------------------------------------------------------
-	function __construct() {
-		$this->gfObj = new cs_globalFunctions;
-		$this->gfObj->debugPrintOpt=1;
-	}//end __construct()
-	//--------------------------------------------------------------------------
-	
-	
-	
-	//--------------------------------------------------------------------------
-	private function create_db() {
-		$dbParams = array(
-			'host'		=> constant('DB_PG_HOST'),
-			'dbname'	=> constant('DB_PG_DBNAME'),
-			'user'		=> constant('DB_PG_DBUSER'),
-			'password'	=> constant('DB_PG_DBPASS'),
-			'port'		=> constant('DB_PG_PORT')
-		);
-		$db = new cs_phpDB(constant('DBTYPE'));
-		$db->connect($dbParams);
-		return($db);
-	}//end create_db()
-	//--------------------------------------------------------------------------
-	
-	
-	//--------------------------------------------------------------------------
-	function test_basics() {
-		$db = $this->create_db();
-		$tok = new authTokenTester($db);
-		
-		//Generic test to ensure we get the appropriate data back.
-		$tokenData = $tok->create_token(1, 'test', 'abc123');
-		$this->assertTrue(is_array($tokenData));
-		$this->assertTrue((count($tokenData) == 2));
-		$this->assertTrue(isset($tokenData['id']));
-		$this->assertTrue(isset($tokenData['hash']));
-		$this->assertTrue(($tokenData['id'] > 0));
-		$this->assertTrue((strlen($tokenData['hash']) == 32));
-		
-		$this->assertEqual($tok->authenticate_token($tokenData['id'], 'test', $tokenData['hash']), 1);
-		
-		
-		//now create a token with a maximum lifetime...
-		{
-			//Generic test to ensure we get the appropriate data back.
-			$tokenData = $tok->create_token(1, 'test', 'abc123', '2 years');
-			$this->assertTrue(is_array($tokenData));
-			$this->assertTrue((count($tokenData) == 2));
-			$this->assertTrue(isset($tokenData['id']));
-			$this->assertTrue(isset($tokenData['hash']));
-			$this->assertTrue(($tokenData['id'] > 0));
-			$this->assertTrue((strlen($tokenData['hash']) == 32));
-			
-			$this->assertEqual($tok->authenticate_token($tokenData['id'], 'test', $tokenData['hash']), 1);
-		}
-		
-		//create a token with only 1 available use and try to authenticate it twice.
-		{
-			//Generic test to ensure we get the appropriate data back.
-			$tokenData = $tok->create_token(1, 'test', 'abc123', null, 1);
-			$this->assertTrue(is_array($tokenData));
-			$this->assertTrue((count($tokenData) == 2));
-			$this->assertTrue(isset($tokenData['id']));
-			$this->assertTrue(isset($tokenData['hash']));
-			$this->assertTrue(($tokenData['id'] > 0));
-			$this->assertTrue((strlen($tokenData['hash']) == 32));
-			
-			if(!$this->assertEqual($tok->authenticate_token($tokenData['id'], 'test', $tokenData['hash']), 1)) {
-				$this->gfObj->debug_print($tok->tokenData($tokenData['id']),1);
-			}
-			if(!$this->assertTrue(($tok->authenticate_token($tokenData['id'], 'test', $tokenData['hash']) === null), "Able to authenticate twice on a token with only 1 use")) {
-				$this->gfObj->debug_print($tok->tokenData($tokenData['id']));
-			}
-		}
-	}//end test_basics()
-	//--------------------------------------------------------------------------
-}
-
-class authTokenTester extends cs_authToken {
-	public $isTest=true;
-	
-	public function tokenData($id) {
-		return($this->get_token_data($id));
-	}
-}
-
-?>
Deleted: trunk/0.3/tests/testOfCSVersionParse.php
===================================================================
--- trunk/0.3/tests/testOfCSVersionParse.php	2009-08-20 02:20:49 UTC (rev 118)
+++ trunk/0.3/tests/testOfCSVersionParse.php	2009-08-20 13:57:54 UTC (rev 119)
@@ -1,127 +0,0 @@
-<?php
-/*
- * Created on Jan 25, 2009
- * 
- * FILE INFORMATION:
- * 
- * $HeadURL$
- * $Id$
- * $LastChangedDate$
- * $LastChangedBy$
- * $LastChangedRevision$
- */
-
-
-
-class testOfCSVersionParse extends UnitTestCase {
-	
-	function __construct() {
-		$this->gfObj = new cs_globalFunctions;
-	}//end __construct()
-	
-	
-	//--------------------------------------------------------------------------
-	function test_basics() {
-		
-		$tests = array(
-			'files/version1'	=> array(
-				'0.1.2-ALPHA8754',
-				'test1',
-				array(
-					'version_major'			=> 0,
-					'version_minor'			=> 1,
-					'version_maintenance'	=> 2,
-					'version_suffix'		=> 'ALPHA8754'
-				)
-			),
-			'files/version2'	=> array(
-				'5.4.0',
-				'test2',
-				array(
-					'version_major'			=> 5,
-					'version_minor'			=> 4,
-					'version_maintenance'	=> 0,
-					'version_suffix'		=> null
-				)
-			),
-			'files/version3'	=> array(
-				'5.4.3-BETA5543',
-				'test3 stuff',
-				array(
-					'version_major'			=> 5,
-					'version_minor'			=> 4,
-					'version_maintenance'	=> 3,
-					'version_suffix'		=> 'BETA5543'
-				)
-			)
-		);
-		
-		foreach($tests as $fileName=>$expectedArr) {
-			$ver = new middleTestClass();
-			$ver->set_version_file_location(dirname(__FILE__) .'/'. $fileName);
-			
-			$this->assertEqual($expectedArr[0], $ver->get_version(), "Failed to match string from file (". $fileName .")");
-			$this->assertEqual($expectedArr[1], $ver->get_project(), "Failed to match project from file (". $fileName .")");
-			
-			//now check that pulling the version as an array is the same...
-			$checkItArr = $ver->get_version(true);
-			$expectThis = $expectedArr[2];
-			$expectThis['version_string'] = $expectedArr[0];
-		}
-	}//end test_basics()
-	//--------------------------------------------------------------------------
-	
-	
-	
-	//--------------------------------------------------------------------------
-	function test_check_higher() {
-		
-		//NOTE: the first item should ALWAYS be higher.
-		$tests = array(
-			'basic, no suffix'	=> array('1.0.1', '1.0.0'),
-			'basic + suffix'	=> array('1.0.0-ALPHA1', '1.0.0-ALPHA0'),
-			'basic w/o maint'	=> array('1.0.1', '1.0'),
-			'suffix check'		=> array('1.0.0-BETA1', '1.0.0-ALPHA1'),
-			'suffix check2'		=> array('1.0.0-ALPHA10', '1.0.0-ALPHA1'),
-			'suffix check3'		=> array('1.0.1', '1.0.0-RC1')
-		);
-		
-		foreach($tests as $name=>$checkData) {
-			$ver = new middleTestClass;
-			$this->assertTrue($ver->is_higher_version($checkData[1], $checkData[0]));
-			$this->assertFalse($ver->is_higher_version($checkData[0], $checkData[1]));
-		}
-		
-		//now check to ensure there's no problem with parsing equivalent versions.
-		$tests = array(
-			'no suffix'				=> array('1.0', '1.0.0'),
-			'no maint + suffix'		=> array('1.0-ALPHA1', '1.0.0-ALPHA1'),
-			'no maint + BETA'		=> array('1.0-BETA5555', '1.0.0-BETA5555'),
-			'no maint + RC'			=> array('1.0-RC33', '1.0.0-RC33'),
-			'maint with space'		=> array('1.0-RC  33', '1.0.0-RC33'),
-			'extra spaces'			=> array(' 1.0   ', '1.0.0')
-		);
-		foreach($tests as $name=>$checkData) {
-			$ver = new middleTestClass;
-			
-			//rip apart & recreate first version to test against the expected...
-			$derivedFullVersion = $ver->build_full_version_string($ver->parse_version_string($checkData[0]));
-			$this->assertEqual($derivedFullVersion, $checkData[1], "TEST=(". $name ."): derived version " .
-					"(". $derivedFullVersion .") doesn't match expected (". $checkData[1] .")");
-			
-			//now rip apart & recreate the expected version (second) and make sure it matches itself.
-			$derivedFullVersion = $ver->build_full_version_string($ver->parse_version_string($checkData[1]));
-			$this->assertEqual($derivedFullVersion, $checkData[1], "TEST=(". $name ."): derived version " .
-					"(". $derivedFullVersion .") doesn't match expected (". $checkData[1] .")");
-		}
-		
-		
-	}//end test_check_higher()
-	//--------------------------------------------------------------------------
-}
-
-
-class middleTestClass extends cs_versionAbstract {
-	function __construct(){}
-} 
-?>
Copied: trunk/0.3/tests/testOfCSWebAppLibs.php (from rev 117, trunk/0.3/tests/testOfCSVersionParse.php)
===================================================================
--- trunk/0.3/tests/testOfCSWebAppLibs.php	                        (rev 0)
+++ trunk/0.3/tests/testOfCSWebAppLibs.php	2009-08-20 13:57:54 UTC (rev 119)
@@ -0,0 +1,209 @@
+<?php
+/*
+ * Created on Jan 25, 2009
+ * 
+ * FILE INFORMATION:
+ * 
+ * $HeadURL$
+ * $Id$
+ * $LastChangedDate$
+ * $LastChangedBy$
+ * $LastChangedRevision$
+ */
+
+require_once(dirname(__FILE__) .'/../cs_version.abstract.class.php');
+require_once(dirname(__FILE__) .'/../cs_authToken.class.php');
+
+class testOfCSWebAppLibs extends UnitTestCase {
+	
+	//--------------------------------------------------------------------------
+	function __construct() {
+		$this->gfObj = new cs_globalFunctions;
+		$this->gfObj->debugPrintOpt=1;
+	}//end __construct()
+	//--------------------------------------------------------------------------
+	
+	
+	//--------------------------------------------------------------------------
+	function test_version_basics() {
+		
+		$tests = array(
+			'files/version1'	=> array(
+				'0.1.2-ALPHA8754',
+				'test1',
+				array(
+					'version_major'			=> 0,
+					'version_minor'			=> 1,
+					'version_maintenance'	=> 2,
+					'version_suffix'		=> 'ALPHA8754'
+				)
+			),
+			'files/version2'	=> array(
+				'5.4.0',
+				'test2',
+				array(
+					'version_major'			=> 5,
+					'version_minor'			=> 4,
+					'version_maintenance'	=> 0,
+					'version_suffix'		=> null
+				)
+			),
+			'files/version3'	=> array(
+				'5.4.3-BETA5543',
+				'test3 stuff',
+				array(
+					'version_major'			=> 5,
+					'version_minor'			=> 4,
+					'version_maintenance'	=> 3,
+					'version_suffix'		=> 'BETA5543'
+				)
+			)
+		);
+		
+		foreach($tests as $fileName=>$expectedArr) {
+			$ver = new middleTestClass();
+			$ver->set_version_file_location(dirname(__FILE__) .'/'. $fileName);
+			
+			$this->assertEqual($expectedArr[0], $ver->get_version(), "Failed to match string from file (". $fileName .")");
+			$this->assertEqual($expectedArr[1], $ver->get_project(), "Failed to match project from file (". $fileName .")");
+			
+			//now check that pulling the version as an array is the same...
+			$checkItArr = $ver->get_version(true);
+			$expectThis = $expectedArr[2];
+			$expectThis['version_string'] = $expectedArr[0];
+		}
+	}//end test_version_basics()
+	//--------------------------------------------------------------------------
+	
+	
+	
+	//--------------------------------------------------------------------------
+	function test_check_higher() {
+		
+		//NOTE: the first item should ALWAYS be higher.
+		$tests = array(
+			'basic, no suffix'	=> array('1.0.1', '1.0.0'),
+			'basic + suffix'	=> array('1.0.0-ALPHA1', '1.0.0-ALPHA0'),
+			'basic w/o maint'	=> array('1.0.1', '1.0'),
+			'suffix check'		=> array('1.0.0-BETA1', '1.0.0-ALPHA1'),
+			'suffix check2'		=> array('1.0.0-ALPHA10', '1.0.0-ALPHA1'),
+			'suffix check3'		=> array('1.0.1', '1.0.0-RC1')
+		);
+		
+		foreach($tests as $name=>$checkData) {
+			$ver = new middleTestClass;
+			$this->assertTrue($ver->is_higher_version($checkData[1], $checkData[0]));
+			$this->assertFalse($ver->is_higher_version($checkData[0], $checkData[1]));
+		}
+		
+		//now check to ensure there's no problem with parsing equivalent versions.
+		$tests = array(
+			'no suffix'				=> array('1.0', '1.0.0'),
+			'no maint + suffix'		=> array('1.0-ALPHA1', '1.0.0-ALPHA1'),
+			'no maint + BETA'		=> array('1.0-BETA5555', '1.0.0-BETA5555'),
+			'no maint + RC'			=> array('1.0-RC33', '1.0.0-RC33'),
+			'maint with space'		=> array('1.0-RC  33', '1.0.0-RC33'),
+			'extra spaces'			=> array(' 1.0   ', '1.0.0')
+		);
+		foreach($tests as $name=>$checkData) {
+			$ver = new middleTestClass;
+			
+			//rip apart & recreate first version to test against the expected...
+			$derivedFullVersion = $ver->build_full_version_string($ver->parse_version_string($checkData[0]));
+			$this->assertEqual($derivedFullVersion, $checkData[1], "TEST=(". $name ."): derived version " .
+					"(". $derivedFullVersion .") doesn't match expected (". $checkData[1] .")");
+			
+			//now rip apart & recreate the expected version (second) and make sure it matches itself.
+			$derivedFullVersion = $ver->build_full_version_string($ver->parse_version_string($checkData[1]));
+			$this->assertEqual($derivedFullVersion, $checkData[1], "TEST=(". $name ."): derived version " .
+					"(". $derivedFullVersion .") doesn't match expected (". $checkData[1] .")");
+		}
+		
+		
+	}//end test_check_higher()
+	//--------------------------------------------------------------------------
+	
+	
+	
+	//--------------------------------------------------------------------------
+	private function create_db() {
+		$dbParams = array(
+			'host'		=> constant('DB_PG_HOST'),
+			'dbname'	=> constant('DB_PG_DBNAME'),
+			'user'		=> constant('DB_PG_DBUSER'),
+			'password'	=> constant('DB_PG_DBPASS'),
+			'port'		=> constant('DB_PG_PORT')
+		);
+		$db = new cs_phpDB(constant('DBTYPE'));
+		$db->connect($dbParams);
+		return($db);
+	}//end create_db()
+	//--------------------------------------------------------------------------
+	
+	
+	//--------------------------------------------------------------------------
+	function test_token_basics() {
+		$db = $this->create_db();
+		$tok = new authTokenTester($db);
+		
+		//Generic test to ensure we get the appropriate data back.
+		$tokenData = $tok->create_token(1, 'test', 'abc123');
+		$this->assertTrue(is_array($tokenData));
+		$this->assertTrue((count($tokenData) == 2));
+		$this->assertTrue(isset($tokenData['id']));
+		$this->assertTrue(isset($tokenData['hash']));
+		$this->assertTrue(($tokenData['id'] > 0));
+		$this->assertTrue((strlen($tokenData['hash']) == 32));
+		
+		$this->assertEqual($tok->authenticate_token($tokenData['id'], 'test', $tokenData['hash']), 1);
+		
+		
+		//now create a token with a maximum lifetime...
+		{
+			//Generic test to ensure we get the appropriate data back.
+			$tokenData = $tok->create_token(1, 'test', 'abc123', '2 years');
+			$this->assertTrue(is_array($tokenData));
+			$this->assertTrue((count($tokenData) == 2));
+			$this->assertTrue(isset($tokenData['id']));
+			$this->assertTrue(isset($tokenData['hash']));
+			$this->assertTrue(($tokenData['id'] > 0));
+			$this->assertTrue((strlen($tokenData['hash']) == 32));
+			
+			$this->assertEqual($tok->authenticate_token($tokenData['id'], 'test', $tokenData['hash']), 1);
+		}
+		
+		//create a token with only 1 available use and try to authenticate it twice.
+		{
+			//Generic test to ensure we get the appropriate data back.
+			$tokenData = $tok->create_token(1, 'test', 'abc123', null, 1);
+			$this->assertTrue(is_array($tokenData));
+			$this->assertTrue((count($tokenData) == 2));
+			$this->assertTrue(isset($tokenData['id']));
+			$this->assertTrue(isset($tokenData['hash']));
+			$this->assertTrue(($tokenData['id'] > 0));
+			$this->assertTrue((strlen($tokenData['hash']) == 32));
+			
+			if(!$this->assertEqual($tok->authenticate_token($tokenData['id'], 'test', $tokenData['hash']), 1)) {
+				$this->gfObj->debug_print($tok->tokenData($tokenData['id']),1);
+			}
+			if(!$this->assertTrue(($tok->authenticate_token($tokenData['id'], 'test', $tokenData['hash']) === null), "Able to authenticate twice on a token with only 1 use")) {
+				$this->gfObj->debug_print($tok->tokenData($tokenData['id']));
+			}
+		}
+	}//end test_token_basics()
+	//--------------------------------------------------------------------------
+}
+
+
+class middleTestClass extends cs_versionAbstract {
+	function __construct(){}
+} 
+
+class authTokenTester extends cs_authToken {
+	public $isTest=true;
+	
+	public function tokenData($id) {
+		return($this->get_token_data($id));
+	}
+}
+?>
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
 | 
| 
      
      
      From: <cra...@us...> - 2009-08-20 02:20:56
      
     | 
| Revision: 118
          http://cs-webapplibs.svn.sourceforge.net/cs-webapplibs/?rev=118&view=rev
Author:   crazedsanity
Date:     2009-08-20 02:20:49 +0000 (Thu, 20 Aug 2009)
Log Message:
-----------
Add missing requirement for cs_webdbupgrade.
Modified Paths:
--------------
    trunk/0.3/cs_webdblogger.class.php
Modified: trunk/0.3/cs_webdblogger.class.php
===================================================================
--- trunk/0.3/cs_webdblogger.class.php	2009-08-20 01:59:58 UTC (rev 117)
+++ trunk/0.3/cs_webdblogger.class.php	2009-08-20 02:20:49 UTC (rev 118)
@@ -28,6 +28,7 @@
 //NOTE::: this class **REQUIRES** cs-content for its "cs_phpDB" class.
 
 require_once(dirname(__FILE__) .'/cs_webapplibs.abstract.class.php');
+require_once(dirname(__FILE__) .'/cs_webdbupgrade.class.php');
 
 class cs_webdblogger extends cs_webapplibsAbstract {
 	/** Database handle */
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
 | 
| 
      
      
      From: <cra...@us...> - 2009-08-20 02:00:08
      
     | 
| Revision: 117
          http://cs-webapplibs.svn.sourceforge.net/cs-webapplibs/?rev=117&view=rev
Author:   crazedsanity
Date:     2009-08-20 01:59:58 +0000 (Thu, 20 Aug 2009)
Log Message:
-----------
Updated test to work (last commit removed wrong method).
Modified Paths:
--------------
    trunk/0.3/tests/testOfAuthToken.php
Modified: trunk/0.3/tests/testOfAuthToken.php
===================================================================
--- trunk/0.3/tests/testOfAuthToken.php	2009-08-20 01:50:46 UTC (rev 116)
+++ trunk/0.3/tests/testOfAuthToken.php	2009-08-20 01:59:58 UTC (rev 117)
@@ -25,18 +25,21 @@
 	
 	
 	//--------------------------------------------------------------------------
-	function setUp() {
-		$tok = new cs_authToken($this->create_db());
-		try {
-			$tok->load_table();
-		}
-		catch(exception $e) {
-		}
-	}//end setUp()
+	private function create_db() {
+		$dbParams = array(
+			'host'		=> constant('DB_PG_HOST'),
+			'dbname'	=> constant('DB_PG_DBNAME'),
+			'user'		=> constant('DB_PG_DBUSER'),
+			'password'	=> constant('DB_PG_DBPASS'),
+			'port'		=> constant('DB_PG_PORT')
+		);
+		$db = new cs_phpDB(constant('DBTYPE'));
+		$db->connect($dbParams);
+		return($db);
+	}//end create_db()
 	//--------------------------------------------------------------------------
 	
 	
-	
 	//--------------------------------------------------------------------------
 	function test_basics() {
 		$db = $this->create_db();
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
 | 
| 
      
      
      From: <cra...@us...> - 2009-08-20 01:51:04
      
     | 
| Revision: 116
          http://cs-webapplibs.svn.sourceforge.net/cs-webapplibs/?rev=116&view=rev
Author:   crazedsanity
Date:     2009-08-20 01:50:46 +0000 (Thu, 20 Aug 2009)
Log Message:
-----------
Incorporate upgrades from cs-webdblogger v0.2.0 into the main schema.
Modified Paths:
--------------
    trunk/0.3/setup/schema.mysql.sql
    trunk/0.3/setup/schema.pgsql.sql
    trunk/0.3/upgrades/upgrade.xml
Removed Paths:
-------------
    trunk/0.3/upgrades/sql/
    trunk/0.3/upgrades/upgradeTo0.2.0.php
    trunk/0.3/upgrades/upgradeTo0.2.1.php
Modified: trunk/0.3/setup/schema.mysql.sql
===================================================================
--- trunk/0.3/setup/schema.mysql.sql	2009-08-20 01:44:14 UTC (rev 115)
+++ trunk/0.3/setup/schema.mysql.sql	2009-08-20 01:50:46 UTC (rev 116)
@@ -92,6 +92,50 @@
 -- 
 ALTER TABLE `cswdbl_log_table`
   ADD CONSTRAINT `cswdbl_log_table_event_id_fkey` FOREIGN KEY (`event_id`) REFERENCES `cswdbl_event_table` (`event_id`);
+
+
+
+-- 
+-- Table structure for table `cswdbl_log_attribute_table`
+-- 
+
+CREATE TABLE `cswdbl_log_attribute_table` (
+  `log_attribute_id` int(11) NOT NULL auto_increment,
+  `log_id` int(11) NOT NULL,
+  `attribute_id` int(11) NOT NULL,
+  `value_text` text NOT NULL,
+  PRIMARY KEY  (`log_attribute_id`),
+  KEY `cswdbl_log_attribute_table_log_id_fkey` (`log_id`),
+  KEY `cswdbl_log_attribute_table_attribute_id_fkey` (`attribute_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
+
+-- --------------------------------------------------------
+
+-- 
+-- Table structure for table `cswdbl_log_table`
+-- 
+
+CREATE TABLE `cswdbl_log_table` (
+  `log_id` int(11) NOT NULL auto_increment,
+  `creation` timestamp NOT NULL default CURRENT_TIMESTAMP,
+  `event_id` int(11) NOT NULL,
+  `uid` int(11) NOT NULL,
+  `affected_uid` int(11) NOT NULL,
+  `details` text NOT NULL,
+  PRIMARY KEY  (`log_id`),
+  KEY `cswdbl_log_table_event_id_fkey` (`event_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
+
+
+
+-- 
+-- Constraints for table `cswdbl_log_attribute_table`
+-- 
+ALTER TABLE `cswdbl_log_attribute_table`
+  ADD CONSTRAINT `cswdbl_log_attribute_table_attribute_id_fkey` FOREIGN KEY (`attribute_id`) REFERENCES `cswdbl_attribute_table` (`attribute_id`),
+  ADD CONSTRAINT `cswdbl_log_attribute_table_log_id_fkey` FOREIGN KEY (`log_id`) REFERENCES `cswdbl_log_table` (`log_id`);
+
+
   
   
 -- This table create statement MUST work in PostgreSQL v8.2.x+ AND MySQL v5.0.x+: 
Modified: trunk/0.3/setup/schema.pgsql.sql
===================================================================
--- trunk/0.3/setup/schema.pgsql.sql	2009-08-20 01:44:14 UTC (rev 115)
+++ trunk/0.3/setup/schema.pgsql.sql	2009-08-20 01:50:46 UTC (rev 116)
@@ -63,6 +63,25 @@
 	details text NOT NULL
 );
 
+
+--
+-- List of distinct attribute names.
+--
+CREATE TABLE cswdbl_attribute_table (
+	attribute_id serial NOT NULL PRIMARY KEY,
+	attribute_name text NOT NULL UNIQUE
+);
+
+--
+-- Linkage for attributes to logs.
+--
+CREATE TABLE cswdbl_log_attribute_table (
+	log_attribute_id serial NOT NULL PRIMARY KEY,
+	log_id int NOT NULL REFERENCES cswdbl_log_table(log_id),
+	attribute_id int NOT NULL REFERENCES cswdbl_attribute_table(attribute_id),
+	value_text text
+);
+
 -- This table create statement MUST work in PostgreSQL v8.2.x+ AND MySQL v5.0.x+: 
 -- otherwise separate schema files have to be created and the code will have to 
 -- do extra checking...
Modified: trunk/0.3/upgrades/upgrade.xml
===================================================================
--- trunk/0.3/upgrades/upgrade.xml	2009-08-20 01:44:14 UTC (rev 115)
+++ trunk/0.3/upgrades/upgrade.xml	2009-08-20 01:50:46 UTC (rev 116)
@@ -18,12 +18,5 @@
 	to get skipped, or an exception to be thrown, potentially leaving the system 
 	in an unstable state.  Unstable is bad, m'kay?</system_note>
 	
-	<matching>
-		<v0.2.0>
-			<target_version>0.2.1</target_version>
-			<script_name>upgradeTo0.2.1.php</script_name>
-			<class_name>upgrade_to_0_2_1</class_name>
-			<call_method>run_upgrade</call_method>
-		</v0.2.0>
-	</matching>
+	<matching />
 </upgrade>
Deleted: trunk/0.3/upgrades/upgradeTo0.2.0.php
===================================================================
--- trunk/0.3/upgrades/upgradeTo0.2.0.php	2009-08-20 01:44:14 UTC (rev 115)
+++ trunk/0.3/upgrades/upgradeTo0.2.0.php	2009-08-20 01:50:46 UTC (rev 116)
@@ -1,95 +0,0 @@
-<?php
-
-class upgrade_to_0_2_0 {
-	
-	//=========================================================================
-	public function __construct(cs_phpDB &$db) {
-		if(!$db->is_connected()) {
-			throw new exception(__METHOD__ .": database is not connected");
-		}
-		$this->db = $db;
-		
-		$this->gfObj = new cs_globalFunctions;
-		$this->gfObj->debugPrintOpt = 1;
-		
-	}//end __construct()
-	//=========================================================================
-	
-	
-	
-	//=========================================================================
-	public function run_upgrade() {
-		
-		
-		$upgradeFilename = dirname(__FILE__) ."/sql/upgradeTo0_2_0.". $this->db->get_dbtype() .".sql";
-		if(!file_exists($upgradeFilename)) {
-			throw new exception(__METHOD__ .": missing upgrade filename (". $upgradeFilename ."), " .
-					"probably due to unsupported database type (". $this->db->get_dbtype() .")");
-		}
-		
-		
-		$tables = array(
-			'cat'	=> array(
-						'cswdbl_category_table',
-						'log_category_table',
-						'category_id'
-					),
-			'class'	=> array(
-						'cswdbl_class_table',
-						'log_class_table',
-						'class_id'
-					),
-			'event'	=> array(
-						'cswdbl_event_table',
-						'log_event_table',
-						'event_id'
-					),
-			'log'	=> array(
-						'cswdbl_log_table',
-						'log_table',
-						'log_id'
-					)
-			);
-		
-		//run the SQL file.
-		$file = dirname(__FILE__) .'/sql/upgradeTo0_2_0.'. $this->db->get_dbtype() .'.sql';
-		$upgradeRes = false;
-		if(file_exists($file)) {
-			$this->db->run_update(file_get_contents($file),true);
-			$totalToSync = count($tables);
-			$totalSynced = 0;
-			
-			//now make sure there's the same amount of data in BOTH tables.
-			foreach($tables as $key => $tables) {
-				$c1 = $this->db->run_query("SELECT * FROM ". $tables[0]);
-				$num1 = $this->db->numRows();
-				$c2 = $this->db->run_query("SELECT * FROM ". $tables[1]);
-				$num2 = $this->db->numRows();
-				
-				if($num1 === $num2) {
-					$totalSynced++;
-					$this->db->run_update("DROP TABLE ". $tables[1] ." CASCADE", true);
-					
-					if($this->db->get_dbtype() == 'pgsql') {
-						//Update the sequence...
-						$seq = $tables[0] .'_'. $tables[2] .'_seq';
-						$this->db->run_update("SELECT setval('". $seq ."', (SELECT max(". $tables[2] .") FROM ". $tables[0] ."))",true);
-					}
-				}
-				else {
-					throw new exception(__METHOD__ .": failed to sync ". $tables[0] ." with ". $tables[1] ." (". $num1 ." != ". $num2 .")");
-				}
-			}
-			$upgradeRes = true;
-		}
-		else {
-			throw new exception(__METHOD__ .": missing upgrade SQL file (". $file .")");
-		}
-		
-		return($upgradeRes);
-		
-	}//end run_upgrade()
-	//=========================================================================
-}
-
-?>
Deleted: trunk/0.3/upgrades/upgradeTo0.2.1.php
===================================================================
--- trunk/0.3/upgrades/upgradeTo0.2.1.php	2009-08-20 01:44:14 UTC (rev 115)
+++ trunk/0.3/upgrades/upgradeTo0.2.1.php	2009-08-20 01:50:46 UTC (rev 116)
@@ -1,47 +0,0 @@
-<?php
-
-class upgrade_to_0_2_1 {
-	
-	//=========================================================================
-	public function __construct(cs_phpDB &$db) {
-		if(!$db->is_connected()) {
-			throw new exception(__METHOD__ .": database is not connected");
-		}
-		$this->db = $db;
-		
-		$this->gfObj = new cs_globalFunctions;
-		$this->gfObj->debugPrintOpt = 1;
-		
-	}//end __construct()
-	//=========================================================================
-	
-	
-	
-	//=========================================================================
-	public function run_upgrade() {
-		
-		
-		$upgradeFilename = dirname(__FILE__) ."/sql/upgradeTo0_2_1.". $this->db->get_dbtype() .".sql";
-		if(!file_exists($upgradeFilename)) {
-			throw new exception(__METHOD__ .": missing upgrade filename (". $upgradeFilename ."), " .
-					"probably due to unsupported database type (". $this->db->get_dbtype() .")");
-		}
-		
-		//run the SQL file.
-		$file = dirname(__FILE__) .'/sql/upgradeTo0_2_1.'. $this->db->get_dbtype() .'.sql';
-		$upgradeRes = false;
-		if(file_exists($file)) {
-			$this->db->run_update(file_get_contents($file),true);
-			$upgradeRes = true;
-		}
-		else {
-			throw new exception(__METHOD__ .": missing upgrade SQL file (". $file .")");
-		}
-		
-		return($upgradeRes);
-		
-	}//end run_upgrade()
-	//=========================================================================
-}
-
-?>
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
 | 
| 
      
      
      From: <cra...@us...> - 2009-08-20 01:44:23
      
     | 
| Revision: 115
          http://cs-webapplibs.svn.sourceforge.net/cs-webapplibs/?rev=115&view=rev
Author:   crazedsanity
Date:     2009-08-20 01:44:14 +0000 (Thu, 20 Aug 2009)
Log Message:
-----------
Updated schema, tests, & set cs_authToken to be MySQL-compatible.
TODO::: incorporate changes in cs-webdbupgrade that include the attribute
tables into the main SQL file(s).
/cs_authToken.class.php:
	* load_table() [DELETED]:
		-- this table should be included in with all the other tables.
	* create_token():
		-- set a default value for expiration so the database doesn't have to
		set that.
		-- NOTE::: any tokens with a NULL expiration and unlimited uses will
		NEVER be automatically removed.
/cs_webdbupgrade.class.php:
	* __construct():
		-- set DB_TABLE and DB_PRIMARYKEY to hardcoded values.
	* load_table():
		-- updated location of schema file
		-- NOTE::: this should probably be named "load_schema()", as it is
		actually loading several different tables now.
/setup/authtoken_schema.pgsql.sql [DELETED]:
	* incorporated into the pgsql & mysql schema files.
/setup/schema.mysql.sql:
	* changed "cs_version_table" to cswal_version_table (cswal ==
	CS-WebAppLibs), removed unnecessary columns (previously removed in
	cs-webdbupgrade v0.2.0).
	* create cswal_auth_token_table.
/setup/schema.pgsql.sql:
	* (same as the mysql version).
/tests/testOfAuthToken.php:
	* remove code for creating & destroying tables (NOTE: my test site
	actually destroys & rebuilds the entire database... this should probably
	be changed to a series of "DROP TABLE" statements).
Modified Paths:
--------------
    trunk/0.3/cs_authToken.class.php
    trunk/0.3/cs_webdbupgrade.class.php
    trunk/0.3/setup/schema.mysql.sql
    trunk/0.3/setup/schema.pgsql.sql
    trunk/0.3/tests/testOfAuthToken.php
Removed Paths:
-------------
    trunk/0.3/setup/authtoken_schema.pgsql.sql
Modified: trunk/0.3/cs_authToken.class.php
===================================================================
--- trunk/0.3/cs_authToken.class.php	2009-08-19 20:12:33 UTC (rev 114)
+++ trunk/0.3/cs_authToken.class.php	2009-08-20 01:44:14 UTC (rev 115)
@@ -48,29 +48,6 @@
 	
 	//=========================================================================
 	/**
-	 * Load table into the database...
-	 */
-	public function load_table() {
-		$file = dirname(__FILE__) .'/setup/authtoken_schema.'. $this->db->get_dbtype() .'.sql';
-		
-		if(file_exists($file)) {
-			try {
-				$this->db->run_update(file_get_contents($file), true);
-			}
-			catch(exception $e) {
-				throw new exception(__METHOD__ .": error while trying to load table::: ". $e->getMessage());
-			}
-		}
-		else {
-			throw new exception(__METHOD__ .": unsupported database type (". $this->db->get_dbtype() .")");
-		}
-	}//end load_table()
-	//=========================================================================
-	
-	
-	
-	//=========================================================================
-	/**
 	 * Standardized method of creating a hash from a string.
 	 * 
 	 * @param $tokenId			(int) matches auth_token_id column....
@@ -110,6 +87,8 @@
 			'checksum'	=> $checksum,
 			'token'		=> '____INCOMPLETE____'
 		);
+		
+		$insertData['expiration'] = strftime('%Y-%m-%d %T', strtotime('1 day'));
 		if(!is_null($lifetime) && strlen($lifetime)) {
 			$insertData['expiration'] = strftime('%Y-%m-%d %T', strtotime($lifetime));
 		}
Modified: trunk/0.3/cs_webdbupgrade.class.php
===================================================================
--- trunk/0.3/cs_webdbupgrade.class.php	2009-08-19 20:12:33 UTC (rev 114)
+++ trunk/0.3/cs_webdbupgrade.class.php	2009-08-20 01:44:14 UTC (rev 115)
@@ -99,13 +99,8 @@
 			$this->gfObj->debugPrintOpt = constant('DEBUGPRINTOPT');
 		}
 		
-		if(!defined(__CLASS__ .'-DB_PRIMARYKEY') || !defined(__CLASS__ .'-DB_TABLE')) {
-			throw new exception(__METHOD__ .": no setting for DB_TABLE or DB_PRIMARYKEY, cannot continue");
-		}
-		else {
-			$this->config['DB_TABLE'] = constant(__CLASS__ .'-DB_TABLE');
-			$this->config['DB_PRIMARYKEY'] = constant(__CLASS__ .'-DB_PRIMARYKEY');
-		}
+		$this->config['DB_TABLE'] = 'cswal_version_table';
+		$this->config['DB_PRIMARYKEY'] = 'version_id';
 		$this->sequenceName = $this->config['DB_TABLE'] .'_'. $this->config['DB_PRIMARYKEY'] .'_seq';
 		
 		if(!defined('DBTYPE')) {
@@ -908,7 +903,7 @@
 	
 	//=========================================================================
 	public function load_table() {
-		$schemaFileLocation = dirname(__FILE__) .'/schema/schema.sql';
+		$schemaFileLocation = dirname(__FILE__) .'/setup/schema.'. $this->db->get_dbtype() .'.sql';
 		$schema = file_get_contents($schemaFileLocation);
 		$schema = str_replace('{tableName}', $this->config['DB_TABLE'], $schema);
 		$schema = str_replace('{primaryKey}', $this->config['DB_PRIMARYKEY'], $schema);
Deleted: trunk/0.3/setup/authtoken_schema.pgsql.sql
===================================================================
--- trunk/0.3/setup/authtoken_schema.pgsql.sql	2009-08-19 20:12:33 UTC (rev 114)
+++ trunk/0.3/setup/authtoken_schema.pgsql.sql	2009-08-20 01:44:14 UTC (rev 115)
@@ -1,20 +0,0 @@
---
--- SVN INFORMATION:::
--- ---------------
---	SVN Signature::::::: $Id$
---	Last Author::::::::: $Author$
---	Current Revision:::: $Revision$
---	Repository Location: $HeadURL$
---	Last Updated:::::::: $Date$
---
-
-CREATE TABLE cswal_auth_token_table (
-	auth_token_id serial NOT NULL PRIMARY KEY,
-	uid integer NOT NULL DEFAULT 0,
-	checksum text NOT NULL,
-	token text NOT NULL,
-	max_uses integer DEFAULT NULL,
-	total_uses integer NOT NULL DEFAULT 0,
-	creation timestamp NOT NULL DEFAULT NOW(),
-	expiration timestamp NOT NULL DEFAULT NOW() + '1 day'::interval
-);
\ No newline at end of file
Modified: trunk/0.3/setup/schema.mysql.sql
===================================================================
--- trunk/0.3/setup/schema.mysql.sql	2009-08-19 20:12:33 UTC (rev 114)
+++ trunk/0.3/setup/schema.mysql.sql	2009-08-20 01:44:14 UTC (rev 115)
@@ -98,14 +98,19 @@
 -- otherwise separate schema files have to be created and the code will have to 
 -- do extra checking...
 -- 
--- The "{tableName}" portion will be replaced with the value of the configured 
--- "DB_TABLE" setting.
-CREATE TABLE cs_version_table (
+CREATE TABLE cswal_version_table (
 	version_id int NOT NULL PRIMARY KEY,
 	project_name varchar(30) NOT NULL UNIQUE,
-	version_string varchar(50) NOT NULL,
-	version_major integer NOT NULL,
-	version_minor integer NOT NULL,
-	version_maintenance integer NOT NULL,
-	version_suffix varchar(20) NOT NULL
+	version_string varchar(50) NOT NULL
 );
+
+CREATE TABLE cswal_auth_token_table (
+	auth_token_id serial NOT NULL PRIMARY KEY,
+	uid integer NOT NULL DEFAULT 0,
+	checksum text NOT NULL,
+	token text NOT NULL,
+	max_uses integer DEFAULT NULL,
+	total_uses integer NOT NULL DEFAULT 0,
+	creation timestamp NOT NULL DEFAULT NOW(),
+	expiration timestamp NOT NULL
+);
\ No newline at end of file
Modified: trunk/0.3/setup/schema.pgsql.sql
===================================================================
--- trunk/0.3/setup/schema.pgsql.sql	2009-08-19 20:12:33 UTC (rev 114)
+++ trunk/0.3/setup/schema.pgsql.sql	2009-08-20 01:44:14 UTC (rev 115)
@@ -66,13 +66,21 @@
 -- This table create statement MUST work in PostgreSQL v8.2.x+ AND MySQL v5.0.x+: 
 -- otherwise separate schema files have to be created and the code will have to 
 -- do extra checking...
-CREATE TABLE cs_version_table (
+CREATE TABLE cswal_version_table (
 	version_id serial NOT NULL PRIMARY KEY,
 	project_name varchar(30) NOT NULL UNIQUE,
-	version_string varchar(50) NOT NULL,
-	version_major integer NOT NULL,
-	version_minor integer NOT NULL,
-	version_maintenance integer NOT NULL,
-	version_suffix varchar(20) NOT NULL
+	version_string varchar(50) NOT NULL
 );
 
+
+CREATE TABLE cswal_auth_token_table (
+	auth_token_id serial NOT NULL PRIMARY KEY,
+	uid integer NOT NULL DEFAULT 0,
+	checksum text NOT NULL,
+	token text NOT NULL,
+	max_uses integer DEFAULT NULL,
+	total_uses integer NOT NULL DEFAULT 0,
+	creation timestamp NOT NULL DEFAULT NOW(),
+	expiration timestamp NOT NULL
+);
+
Modified: trunk/0.3/tests/testOfAuthToken.php
===================================================================
--- trunk/0.3/tests/testOfAuthToken.php	2009-08-19 20:12:33 UTC (rev 114)
+++ trunk/0.3/tests/testOfAuthToken.php	2009-08-20 01:44:14 UTC (rev 115)
@@ -38,35 +38,6 @@
 	
 	
 	//--------------------------------------------------------------------------
-	function tearDown() {
-		$db = $this->create_db();
-		try {
-			$db->run_update('DROP TABLE cswal_auth_token_table', true);
-		}
-		catch(exception $e) {
-		}
-	}//end
-	//--------------------------------------------------------------------------
-	
-	
-	
-	//--------------------------------------------------------------------------
-	private function create_db() {
-		$dbParams = array(
-			'host'		=> constant('DB_PG_HOST'),
-			'dbname'	=> constant('DB_PG_DBNAME'),
-			'user'		=> constant('DB_PG_DBUSER'),
-			'password'	=> constant('DB_PG_DBPASS'),
-			'port'		=> constant('DB_PG_PORT')
-		);
-		$db = new cs_phpDB(constant('DBTYPE'));
-		$db->connect($dbParams);
-		return($db);
-	}//end create_db()
-	//--------------------------------------------------------------------------
-	
-	
-	//--------------------------------------------------------------------------
 	function test_basics() {
 		$db = $this->create_db();
 		$tok = new authTokenTester($db);
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
 | 
| 
      
      
      From: <cra...@us...> - 2009-08-19 20:12:40
      
     | 
| Revision: 114
          http://cs-webapplibs.svn.sourceforge.net/cs-webapplibs/?rev=114&view=rev
Author:   crazedsanity
Date:     2009-08-19 20:12:33 +0000 (Wed, 19 Aug 2009)
Log Message:
-----------
Changes to work with MySQL.
/cs_authToken.class.php:
	* create_token():
		-- use a combination of strftime(strtotime()) to create an
		appropriate expiration date based on a given lifetime (i.e. "1 day").
		-- use "expiration" column instead of duration.
	* get_token_data():
		-- updated to use expiration instead of creation + duration.
/setup/authToken_schema.pgsql.sql:
	* cswal_auth_token_table:
		-- change creation to a timestamp.
		-- change duration(interval) to expiration(timestamp)
Modified Paths:
--------------
    trunk/0.3/cs_authToken.class.php
    trunk/0.3/setup/authtoken_schema.pgsql.sql
Modified: trunk/0.3/cs_authToken.class.php
===================================================================
--- trunk/0.3/cs_authToken.class.php	2009-08-19 19:24:52 UTC (rev 113)
+++ trunk/0.3/cs_authToken.class.php	2009-08-19 20:12:33 UTC (rev 114)
@@ -111,7 +111,7 @@
 			'token'		=> '____INCOMPLETE____'
 		);
 		if(!is_null($lifetime) && strlen($lifetime)) {
-			$insertData['duration'] = $lifetime;
+			$insertData['expiration'] = strftime('%Y-%m-%d %T', strtotime($lifetime));
 		}
 		if(!is_null($maxUses) && is_numeric($maxUses) && $maxUses > 0) {
 			$insertData['max_uses'] = $maxUses;
@@ -282,7 +282,7 @@
 	protected function get_token_data($tokenId) {
 		try {
 			$data = $this->db->run_query("SELECT * FROM ". $this->table ." WHERE auth_token_id=". $tokenId
-					." AND (creation + duration)::date >= CURRENT_DATE", 'auth_token_id');
+					." AND expiration::date >= CURRENT_DATE", 'auth_token_id');
 			if(is_array($data) && count($data) == 1) {
 				$tokenData = $data;
 			}
Modified: trunk/0.3/setup/authtoken_schema.pgsql.sql
===================================================================
--- trunk/0.3/setup/authtoken_schema.pgsql.sql	2009-08-19 19:24:52 UTC (rev 113)
+++ trunk/0.3/setup/authtoken_schema.pgsql.sql	2009-08-19 20:12:33 UTC (rev 114)
@@ -15,6 +15,6 @@
 	token text NOT NULL,
 	max_uses integer DEFAULT NULL,
 	total_uses integer NOT NULL DEFAULT 0,
-	creation date NOT NULL DEFAULT NOW(),
-	duration interval NOT NULL DEFAULT '1 day'::interval
+	creation timestamp NOT NULL DEFAULT NOW(),
+	expiration timestamp NOT NULL DEFAULT NOW() + '1 day'::interval
 );
\ No newline at end of file
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
 | 
| 
      
      
      From: <cra...@us...> - 2009-08-19 19:25:00
      
     | 
| Revision: 113
          http://cs-webapplibs.svn.sourceforge.net/cs-webapplibs/?rev=113&view=rev
Author:   crazedsanity
Date:     2009-08-19 19:24:52 +0000 (Wed, 19 Aug 2009)
Log Message:
-----------
Headers, use get_token_data() in authenticate_token().
/cs_authToken.class.php:
	* ADDED HEADERS:
		-- __constructor()
		-- load_table()
		-- create_hash_string()
		-- create_token()
		-- update_token_uses()
		-- destroy_token()
		-- authenticate_token()
		-- get_token_data()
	* authenticate_token():
		-- call get_token_data()
		-- do extra checking on the return value that was previously in SQL.
	* get_token_data():
		-- updated to only show records that haven't expired.
		-- extra checking for if it returns boolean false.
Modified Paths:
--------------
    trunk/0.3/cs_authToken.class.php
Modified: trunk/0.3/cs_authToken.class.php
===================================================================
--- trunk/0.3/cs_authToken.class.php	2009-08-19 19:01:21 UTC (rev 112)
+++ trunk/0.3/cs_authToken.class.php	2009-08-19 19:24:52 UTC (rev 113)
@@ -28,6 +28,9 @@
 	private $seq = 'cswal_auth_token_table_auth_token_id_seq';
 	
 	//=========================================================================
+	/**
+	 * The CONSTRUCTOR.  Sets internal properties & such.
+	 */
 	public function __construct(cs_phpDB $db) {
 		
 		if($db->is_connected()) {
@@ -44,6 +47,9 @@
 	
 	
 	//=========================================================================
+	/**
+	 * Load table into the database...
+	 */
 	public function load_table() {
 		$file = dirname(__FILE__) .'/setup/authtoken_schema.'. $this->db->get_dbtype() .'.sql';
 		
@@ -64,6 +70,17 @@
 	
 	
 	//=========================================================================
+	/**
+	 * Standardized method of creating a hash from a string.
+	 * 
+	 * @param $tokenId			(int) matches auth_token_id column....
+	 * @param $uid				(int) matches uid column...
+	 * @param $checksum			(str) This is the value that can be used by the 
+	 * 								calling code to see if the given uid matches 
+	 * 								this data (i.e. using an email address/username).
+	 * @param $stringToHash		(str) Data used to help create a hash, usually 
+	 * 								something very unique.
+	 */
 	protected function create_hash_string($tokenId, $uid, $checksum, $stringToHash=NULL) {
 		return(md5($tokenId ."_". $uid ."_". $checksum ."_". $stringToHash));
 	}//end create_hash_string()
@@ -72,6 +89,20 @@
 	
 	
 	//=========================================================================
+	/**
+	 * Build a token record in the database that can be authenticated against later.
+	 * 
+	 * @param $uid			(int) matches uid column...
+	 * @param $checksum		(str) matches checksum column...
+	 * @param $stringToHash	(str) unique value to help build hash from.
+	 * @param $lifetime		(str,optional) string (interval) representing how 
+	 * 							long the token should last.
+	 * @param $maxUses		(int,optional) Number of times it can be authenticated 
+	 * 							against before being removed.
+	 * 
+	 * @return (array)		PASS: contains id & hash for the token.
+	 * @return (exception)	FAIL: exception contains error details.
+	 */
 	public function create_token($uid, $checksum, $stringToHash, $lifetime=null, $maxUses=null) {
 		
 		$insertData = array(
@@ -112,6 +143,15 @@
 	
 	
 	//=========================================================================
+	/**
+	 * Update the number of times the given token has been used (even if the 
+	 * maximum uses hasn't been set).
+	 * 
+	 * @param $tokenId		(int) auth_token_id to look up.
+	 * 
+	 * @return (int)		PASS: updated this many records (should always be 1)
+	 * @return (exception)	FAIL: exception denotes problem
+	 */
 	protected function update_token_uses($tokenId) {
 		
 		try {
@@ -129,6 +169,14 @@
 	
 	
 	//=========================================================================
+	/**
+	 * Deletes the given token ID from the database.
+	 * 
+	 * @param $tokenId		(int) auth_token_id to delete
+	 * 
+	 * @return (int)		PASS: this many were deleted (should always be 1)
+	 * @return (exception)	FAIL: exception contains error details
+	 */
 	protected function destroy_token($tokenId) {
 		try {
 			$sql = "DELETE FROM ". $this->table ." WHERE auth_token_id=". $tokenId;
@@ -162,22 +210,23 @@
 	 * if($tokenUid == $realUid) {
 	 *	      //token is truly authentic
 	 * }
+	 * 
+	 * @param $tokenId		(int) auth_token_id to check against
+	 * @param $checksum		(str) required 'checksum' value.
+	 * @param $hash			(str) required 'token' value.
 	 */
 	public function authenticate_token($tokenId, $checksum, $hash) {
 		
 		$authTokenRes = null;
 		
 		if(is_numeric($tokenId) && strlen($checksum) && strlen($hash) == 32) {
-			$sql = "SELECT * FROM ". $this->table ." WHERE auth_token_id=". $tokenId
-					." AND (creation + duration)::date >= CURRENT_DATE";
-			
 			try {
-				$data = $this->db->run_query($sql, 'auth_token_id');
+				$data = $this->get_token_data($tokenId);
 				
 				if(count($data) == 1 && isset($data[$tokenId]) && is_array($data[$tokenId])) {
 					$data = $data[$tokenId];
 					
-					if($data['token'] == $hash && $data['checksum'] == $checksum) {
+					if($data['token'] == $hash && $data['checksum']) {
 						
 						$methodCall = 'update_token_uses';
 						if(is_numeric($data['max_uses'])) {
@@ -220,15 +269,34 @@
 	//=========================================================================
 	
 	
+	
 	//=========================================================================
+	/**
+	 * Retrieve data for the given ID.
+	 * 
+	 * @param $tokenId		(int) auth_token_id to look up.
+	 * 
+	 * @return (array)		PASS: contains data about the given ID
+	 * @return (exception)	FAIL: exception contains error details.
+	 */
 	protected function get_token_data($tokenId) {
 		try {
-			$data = $this->db->run_query("SELECT * FROM ". $this->table ." WHERE auth_token_id=". $tokenId);
+			$data = $this->db->run_query("SELECT * FROM ". $this->table ." WHERE auth_token_id=". $tokenId
+					." AND (creation + duration)::date >= CURRENT_DATE", 'auth_token_id');
+			if(is_array($data) && count($data) == 1) {
+				$tokenData = $data;
+			}
+			elseif($data === false) {
+				$tokenData = false;
+			}
+			else {
+				throw new exception("too many records returned (". count($data) .")");
+			}
 		}
 		catch(exception $e) {
 			throw new exception(__METHOD__ .": failed to retrieve tokenId (". $tokenId .")::: ". $e->getMessage());
 		}
-		return($data);
+		return($tokenData);
 	}//end get_token_data();
 	//=========================================================================
 	
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
 | 
| 
      
      
      From: <cra...@us...> - 2009-08-19 19:01:31
      
     | 
| Revision: 112
          http://cs-webapplibs.svn.sourceforge.net/cs-webapplibs/?rev=112&view=rev
Author:   crazedsanity
Date:     2009-08-19 19:01:21 +0000 (Wed, 19 Aug 2009)
Log Message:
-----------
Can authenticate against a given token.
/cs_authToken.class.php:
	* create_token():
		-- ARG CHANGE: NEW ARG: #4 ($lifetime=null)
		-- ARG CHANGE: NEW ARG: #5 ($maxUses=null)
		-- allow setting of duration ($lifetime) or max_uses ($maxUses) when
		creating the token.
		-- update the $insertData array with 'duration' or 'max_uses' data if
		appropriate, based on args #4 and #5
	* update_token_uses() [NEW]:
		-- updates the number of times a given token has been used.
	* destroy_token() [NEW]:
		-- deletes a specific token.
	* authenticate_token() [NEW]:
		-- authenticates the given data against a token.
		-- this handles updating uses & destroying tokens that have been used
		their maximum number of times: this means that mass token expiration
		can be done based purely on creation + duration.
	* get_token_data() [NEW]:
		-- returns data about the given token.
/setup/authtoken_schema.pgsql.sql:
	* cswal_auth_token_table:
		-- changed "use_limit" to "max_uses" for sanity.
/tests/testOfAuthToken.php:
	* additional tests for authentication.
Modified Paths:
--------------
    trunk/0.3/cs_authToken.class.php
    trunk/0.3/setup/authtoken_schema.pgsql.sql
    trunk/0.3/tests/testOfAuthToken.php
Modified: trunk/0.3/cs_authToken.class.php
===================================================================
--- trunk/0.3/cs_authToken.class.php	2009-08-19 17:44:04 UTC (rev 111)
+++ trunk/0.3/cs_authToken.class.php	2009-08-19 19:01:21 UTC (rev 112)
@@ -72,15 +72,19 @@
 	
 	
 	//=========================================================================
-	public function create_token($uid, $checksum, $stringToHash) {
+	public function create_token($uid, $checksum, $stringToHash, $lifetime=null, $maxUses=null) {
 		
-		$this->db->beginTrans();
-		
 		$insertData = array(
 			'uid'		=> $uid,
 			'checksum'	=> $checksum,
 			'token'		=> '____INCOMPLETE____'
 		);
+		if(!is_null($lifetime) && strlen($lifetime)) {
+			$insertData['duration'] = $lifetime;
+		}
+		if(!is_null($maxUses) && is_numeric($maxUses) && $maxUses > 0) {
+			$insertData['max_uses'] = $maxUses;
+		}
 		try {
 			$sql = "INSERT INTO cswal_auth_token_table ". 
 					$this->gfObj->string_from_array($insertData, 'insert', null, 'sql');
@@ -105,5 +109,128 @@
 	}//end create_token()
 	//=========================================================================
 	
+	
+	
+	//=========================================================================
+	protected function update_token_uses($tokenId) {
+		
+		try {
+			$sql = "UPDATE ". $this->table ." SET total_uses= total_uses+1 " .
+					"WHERE auth_token_id=". $tokenId;
+			$updateRes = $this->db->run_update($sql);
+		}
+		catch(exception $e) {
+			throw new exception(__METHOD__ .": failed to update usage count::: ". $e->getMessage());
+		}
+		return($updateRes);
+	}//end update_token_uses()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	protected function destroy_token($tokenId) {
+		try {
+			$sql = "DELETE FROM ". $this->table ." WHERE auth_token_id=". $tokenId;
+			$deleteRes = $this->db->run_update($sql);
+		}
+		catch(exception $e) {
+			throw new exception(__METHOD__ .": failed to ");
+		}
+		
+		return($deleteRes);
+	}//end destroy_token()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	/**
+	 * Determine if a token is authentic: the id is used to make the search as
+	 * fast as possible, while the hash & checksum are given to compare against.
+	 * Failure results in FALSE, while success returns the contact_id for the
+	 * given token.
+	 *
+	 * NOTE: the calling program can leave it to this method to say if the
+	 * token is authentic, or use a checksum which can in turn be used to get
+	 * a specific contact_id; when they authenticate, the return of this
+	 * method must then match the contact_id retrieved from the checksum...
+	 *
+	 * EXAMPLE:
+	 * $tokenUid = cs_authToken::authenticate_token($tokenId, $hash, $checksum);
+	 * $realUid = userClass::get_uid_from_email($checksum);
+	 * if($tokenUid == $realUid) {
+	 *	      //token is truly authentic
+	 * }
+	 */
+	public function authenticate_token($tokenId, $checksum, $hash) {
+		
+		$authTokenRes = null;
+		
+		if(is_numeric($tokenId) && strlen($checksum) && strlen($hash) == 32) {
+			$sql = "SELECT * FROM ". $this->table ." WHERE auth_token_id=". $tokenId
+					." AND (creation + duration)::date >= CURRENT_DATE";
+			
+			try {
+				$data = $this->db->run_query($sql, 'auth_token_id');
+				
+				if(count($data) == 1 && isset($data[$tokenId]) && is_array($data[$tokenId])) {
+					$data = $data[$tokenId];
+					
+					if($data['token'] == $hash && $data['checksum'] == $checksum) {
+						
+						$methodCall = 'update_token_uses';
+						if(is_numeric($data['max_uses'])) {
+							$authTokenRes = null;
+							if($data['max_uses'] == $data['total_uses']) {
+								//reached max uses already... (maybe this should throw an exception?)
+								$methodCall = 'destroy_token';
+							}
+							elseif($data['total_uses'] < $data['max_uses']) {
+								$authTokenRes = $data['uid'];
+								if(($data['total_uses'] +1) == $data['max_uses']) {
+									//this is the last use: just destroy it.
+									$methodCall = 'destroy_token';
+								}
+							}
+							else {
+								throw new exception(__METHOD__ .": token (". $tokenId .") used more than max allowed uses [total=". $data['total_uses'] .", max=". $data['max_uses'] ."]");
+							}
+						}
+						else {
+							$authTokenRes = $data['uid'];
+						}
+						$this->$methodCall($tokenId);
+					}
+				}
+				elseif($data === false) {
+					$authTokenRes = null;
+				}
+				else {
+					throw new exception(__METHOD__ .": invalid data returned:: ". $this->gfObj->debug_var_dump($data,0));
+				}
+			}
+			catch(exception $e) {
+				throw new exception(__METHOD__ .": failed to authenticate token::: ". $e->getMessage());
+			}
+		}
+		
+		return($authTokenRes);
+	}//end authenticate_token()
+	//=========================================================================
+	
+	
+	//=========================================================================
+	protected function get_token_data($tokenId) {
+		try {
+			$data = $this->db->run_query("SELECT * FROM ". $this->table ." WHERE auth_token_id=". $tokenId);
+		}
+		catch(exception $e) {
+			throw new exception(__METHOD__ .": failed to retrieve tokenId (". $tokenId .")::: ". $e->getMessage());
+		}
+		return($data);
+	}//end get_token_data();
+	//=========================================================================
+	
 }
 ?>
Modified: trunk/0.3/setup/authtoken_schema.pgsql.sql
===================================================================
--- trunk/0.3/setup/authtoken_schema.pgsql.sql	2009-08-19 17:44:04 UTC (rev 111)
+++ trunk/0.3/setup/authtoken_schema.pgsql.sql	2009-08-19 19:01:21 UTC (rev 112)
@@ -13,7 +13,7 @@
 	uid integer NOT NULL DEFAULT 0,
 	checksum text NOT NULL,
 	token text NOT NULL,
-	use_limit integer DEFAULT NULL,
+	max_uses integer DEFAULT NULL,
 	total_uses integer NOT NULL DEFAULT 0,
 	creation date NOT NULL DEFAULT NOW(),
 	duration interval NOT NULL DEFAULT '1 day'::interval
Modified: trunk/0.3/tests/testOfAuthToken.php
===================================================================
--- trunk/0.3/tests/testOfAuthToken.php	2009-08-19 17:44:04 UTC (rev 111)
+++ trunk/0.3/tests/testOfAuthToken.php	2009-08-19 19:01:21 UTC (rev 112)
@@ -69,19 +69,62 @@
 	//--------------------------------------------------------------------------
 	function test_basics() {
 		$db = $this->create_db();
-		$tok = new cs_authToken($db);
+		$tok = new authTokenTester($db);
 		
 		//Generic test to ensure we get the appropriate data back.
-		$tokenData = $tok->create_token(0, 'test', 'abc123');
+		$tokenData = $tok->create_token(1, 'test', 'abc123');
 		$this->assertTrue(is_array($tokenData));
 		$this->assertTrue((count($tokenData) == 2));
 		$this->assertTrue(isset($tokenData['id']));
 		$this->assertTrue(isset($tokenData['hash']));
 		$this->assertTrue(($tokenData['id'] > 0));
 		$this->assertTrue((strlen($tokenData['hash']) == 32));
+		
+		$this->assertEqual($tok->authenticate_token($tokenData['id'], 'test', $tokenData['hash']), 1);
+		
+		
+		//now create a token with a maximum lifetime...
+		{
+			//Generic test to ensure we get the appropriate data back.
+			$tokenData = $tok->create_token(1, 'test', 'abc123', '2 years');
+			$this->assertTrue(is_array($tokenData));
+			$this->assertTrue((count($tokenData) == 2));
+			$this->assertTrue(isset($tokenData['id']));
+			$this->assertTrue(isset($tokenData['hash']));
+			$this->assertTrue(($tokenData['id'] > 0));
+			$this->assertTrue((strlen($tokenData['hash']) == 32));
+			
+			$this->assertEqual($tok->authenticate_token($tokenData['id'], 'test', $tokenData['hash']), 1);
+		}
+		
+		//create a token with only 1 available use and try to authenticate it twice.
+		{
+			//Generic test to ensure we get the appropriate data back.
+			$tokenData = $tok->create_token(1, 'test', 'abc123', null, 1);
+			$this->assertTrue(is_array($tokenData));
+			$this->assertTrue((count($tokenData) == 2));
+			$this->assertTrue(isset($tokenData['id']));
+			$this->assertTrue(isset($tokenData['hash']));
+			$this->assertTrue(($tokenData['id'] > 0));
+			$this->assertTrue((strlen($tokenData['hash']) == 32));
+			
+			if(!$this->assertEqual($tok->authenticate_token($tokenData['id'], 'test', $tokenData['hash']), 1)) {
+				$this->gfObj->debug_print($tok->tokenData($tokenData['id']),1);
+			}
+			if(!$this->assertTrue(($tok->authenticate_token($tokenData['id'], 'test', $tokenData['hash']) === null), "Able to authenticate twice on a token with only 1 use")) {
+				$this->gfObj->debug_print($tok->tokenData($tokenData['id']));
+			}
+		}
 	}//end test_basics()
 	//--------------------------------------------------------------------------
 }
 
+class authTokenTester extends cs_authToken {
+	public $isTest=true;
+	
+	public function tokenData($id) {
+		return($this->get_token_data($id));
+	}
+}
 
 ?>
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
 | 
| 
      
      
      From: <cra...@us...> - 2009-08-19 17:44:19
      
     | 
| Revision: 111
          http://cs-webapplibs.svn.sourceforge.net/cs-webapplibs/?rev=111&view=rev
Author:   crazedsanity
Date:     2009-08-19 17:44:04 +0000 (Wed, 19 Aug 2009)
Log Message:
-----------
Testing of cs_authToken & ability to create a token.
Modified Paths:
--------------
    trunk/0.3/cs_authToken.class.php
    trunk/0.3/setup/authtoken_schema.pgsql.sql
    trunk/0.3/tests/testOfAuthToken.php
Modified: trunk/0.3/cs_authToken.class.php
===================================================================
--- trunk/0.3/cs_authToken.class.php	2009-08-19 17:14:22 UTC (rev 110)
+++ trunk/0.3/cs_authToken.class.php	2009-08-19 17:44:04 UTC (rev 111)
@@ -18,6 +18,15 @@
 	/** Database object. */
 	private $db;
 	
+	/** Object that helps deal with strings. */
+	private $gfObj;
+	
+	/** Name of the table */
+	private $table = 'cswal_auth_token_table';
+	
+	/** Sequence name for the given table (for PostgreSQL) */
+	private $seq = 'cswal_auth_token_table_auth_token_id_seq';
+	
 	//=========================================================================
 	public function __construct(cs_phpDB $db) {
 		
@@ -27,6 +36,7 @@
 		else {
 			throw new exception(__METHOD__ .": database object not connected");
 		}
+		$this->gfObj = new cs_globalFunctions();
 		
 	}//end __construct()
 	//=========================================================================
@@ -54,7 +64,46 @@
 	
 	
 	//=========================================================================
+	protected function create_hash_string($tokenId, $uid, $checksum, $stringToHash=NULL) {
+		return(md5($tokenId ."_". $uid ."_". $checksum ."_". $stringToHash));
+	}//end create_hash_string()
 	//=========================================================================
 	
+	
+	
+	//=========================================================================
+	public function create_token($uid, $checksum, $stringToHash) {
+		
+		$this->db->beginTrans();
+		
+		$insertData = array(
+			'uid'		=> $uid,
+			'checksum'	=> $checksum,
+			'token'		=> '____INCOMPLETE____'
+		);
+		try {
+			$sql = "INSERT INTO cswal_auth_token_table ". 
+					$this->gfObj->string_from_array($insertData, 'insert', null, 'sql');
+			$tokenId = $this->db->run_insert($sql, $this->seq);
+			
+			//now that we have the ID, let's create the real has string.
+			$finalHash = $this->create_hash_string($tokenId, $uid, $checksum, $stringToHash);
+			
+			$this->db->run_update("UPDATE ". $this->table ." SET token='". $finalHash ."' WHERE " .
+					"auth_token_id=". $tokenId);
+			
+			$tokenInfo = array(
+				'id'	=> $tokenId,
+				'hash'	=> $finalHash
+			);
+		}
+		catch(exception $e) {
+			throw new exception(__METHOD__ .": failed to create token::: ". $e->getMessage());
+		}
+		
+		return($tokenInfo);
+	}//end create_token()
+	//=========================================================================
+	
 }
 ?>
Modified: trunk/0.3/setup/authtoken_schema.pgsql.sql
===================================================================
--- trunk/0.3/setup/authtoken_schema.pgsql.sql	2009-08-19 17:14:22 UTC (rev 110)
+++ trunk/0.3/setup/authtoken_schema.pgsql.sql	2009-08-19 17:44:04 UTC (rev 111)
@@ -13,6 +13,8 @@
 	uid integer NOT NULL DEFAULT 0,
 	checksum text NOT NULL,
 	token text NOT NULL,
+	use_limit integer DEFAULT NULL,
+	total_uses integer NOT NULL DEFAULT 0,
 	creation date NOT NULL DEFAULT NOW(),
 	duration interval NOT NULL DEFAULT '1 day'::interval
 );
\ No newline at end of file
Modified: trunk/0.3/tests/testOfAuthToken.php
===================================================================
--- trunk/0.3/tests/testOfAuthToken.php	2009-08-19 17:14:22 UTC (rev 110)
+++ trunk/0.3/tests/testOfAuthToken.php	2009-08-19 17:44:04 UTC (rev 111)
@@ -71,7 +71,14 @@
 		$db = $this->create_db();
 		$tok = new cs_authToken($db);
 		
-		
+		//Generic test to ensure we get the appropriate data back.
+		$tokenData = $tok->create_token(0, 'test', 'abc123');
+		$this->assertTrue(is_array($tokenData));
+		$this->assertTrue((count($tokenData) == 2));
+		$this->assertTrue(isset($tokenData['id']));
+		$this->assertTrue(isset($tokenData['hash']));
+		$this->assertTrue(($tokenData['id'] > 0));
+		$this->assertTrue((strlen($tokenData['hash']) == 32));
 	}//end test_basics()
 	//--------------------------------------------------------------------------
 }
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
 | 
| 
      
      
      From: <cra...@us...> - 2009-08-19 17:14:30
      
     | 
| Revision: 110
          http://cs-webapplibs.svn.sourceforge.net/cs-webapplibs/?rev=110&view=rev
Author:   crazedsanity
Date:     2009-08-19 17:14:22 +0000 (Wed, 19 Aug 2009)
Log Message:
-----------
Beginnings of cs_authToken class, for storing of generic tokens to test against.
Added Paths:
-----------
    trunk/0.3/cs_authToken.class.php
    trunk/0.3/setup/authtoken_schema.pgsql.sql
    trunk/0.3/tests/testOfAuthToken.php
Added: trunk/0.3/cs_authToken.class.php
===================================================================
--- trunk/0.3/cs_authToken.class.php	                        (rev 0)
+++ trunk/0.3/cs_authToken.class.php	2009-08-19 17:14:22 UTC (rev 110)
@@ -0,0 +1,60 @@
+<?php
+/*
+ * Created on Aug 19, 2009
+ *
+ *  SVN INFORMATION:::
+ * -------------------
+ * Last Author::::::::: $Author$ 
+ * Current Revision:::: $Revision$ 
+ * Repository Location: $HeadURL$ 
+ * Last Updated:::::::: $Date$
+ */
+
+
+require_once(dirname(__FILE__) .'/cs_webapplibs.abstract.class.php');
+
+class cs_authToken extends cs_webapplibsAbstract {
+	
+	/** Database object. */
+	private $db;
+	
+	//=========================================================================
+	public function __construct(cs_phpDB $db) {
+		
+		if($db->is_connected()) {
+			$this->db = $db;
+		}
+		else {
+			throw new exception(__METHOD__ .": database object not connected");
+		}
+		
+	}//end __construct()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	public function load_table() {
+		$file = dirname(__FILE__) .'/setup/authtoken_schema.'. $this->db->get_dbtype() .'.sql';
+		
+		if(file_exists($file)) {
+			try {
+				$this->db->run_update(file_get_contents($file), true);
+			}
+			catch(exception $e) {
+				throw new exception(__METHOD__ .": error while trying to load table::: ". $e->getMessage());
+			}
+		}
+		else {
+			throw new exception(__METHOD__ .": unsupported database type (". $this->db->get_dbtype() .")");
+		}
+	}//end load_table()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	//=========================================================================
+	
+}
+?>
Property changes on: trunk/0.3/cs_authToken.class.php
___________________________________________________________________
Added: svn:keywords
   + Id
Author
Revision
HeadURL
Date
Added: trunk/0.3/setup/authtoken_schema.pgsql.sql
===================================================================
--- trunk/0.3/setup/authtoken_schema.pgsql.sql	                        (rev 0)
+++ trunk/0.3/setup/authtoken_schema.pgsql.sql	2009-08-19 17:14:22 UTC (rev 110)
@@ -0,0 +1,18 @@
+--
+-- SVN INFORMATION:::
+-- ---------------
+--	SVN Signature::::::: $Id$
+--	Last Author::::::::: $Author$
+--	Current Revision:::: $Revision$
+--	Repository Location: $HeadURL$
+--	Last Updated:::::::: $Date$
+--
+
+CREATE TABLE cswal_auth_token_table (
+	auth_token_id serial NOT NULL PRIMARY KEY,
+	uid integer NOT NULL DEFAULT 0,
+	checksum text NOT NULL,
+	token text NOT NULL,
+	creation date NOT NULL DEFAULT NOW(),
+	duration interval NOT NULL DEFAULT '1 day'::interval
+);
\ No newline at end of file
Property changes on: trunk/0.3/setup/authtoken_schema.pgsql.sql
___________________________________________________________________
Added: svn:keywords
   + Id
Author
Revision
HeadURL
Date
Copied: trunk/0.3/tests/testOfAuthToken.php (from rev 109, trunk/0.3/tests/testOfCSVersionParse.php)
===================================================================
--- trunk/0.3/tests/testOfAuthToken.php	                        (rev 0)
+++ trunk/0.3/tests/testOfAuthToken.php	2009-08-19 17:14:22 UTC (rev 110)
@@ -0,0 +1,80 @@
+<?php
+/*
+ * Created on Jan 25, 2009
+ * 
+ * FILE INFORMATION:
+ * 
+ * $HeadURL$
+ * $Id$
+ * $LastChangedDate$
+ * $LastChangedBy$
+ * $LastChangedRevision$
+ */
+
+require_once(dirname(__FILE__) .'/../cs_authToken.class.php');
+
+class testOfCSAuthToken extends UnitTestCase {
+	
+	//--------------------------------------------------------------------------
+	function __construct() {
+		$this->gfObj = new cs_globalFunctions;
+		$this->gfObj->debugPrintOpt=1;
+	}//end __construct()
+	//--------------------------------------------------------------------------
+	
+	
+	
+	//--------------------------------------------------------------------------
+	function setUp() {
+		$tok = new cs_authToken($this->create_db());
+		try {
+			$tok->load_table();
+		}
+		catch(exception $e) {
+		}
+	}//end setUp()
+	//--------------------------------------------------------------------------
+	
+	
+	
+	//--------------------------------------------------------------------------
+	function tearDown() {
+		$db = $this->create_db();
+		try {
+			$db->run_update('DROP TABLE cswal_auth_token_table', true);
+		}
+		catch(exception $e) {
+		}
+	}//end
+	//--------------------------------------------------------------------------
+	
+	
+	
+	//--------------------------------------------------------------------------
+	private function create_db() {
+		$dbParams = array(
+			'host'		=> constant('DB_PG_HOST'),
+			'dbname'	=> constant('DB_PG_DBNAME'),
+			'user'		=> constant('DB_PG_DBUSER'),
+			'password'	=> constant('DB_PG_DBPASS'),
+			'port'		=> constant('DB_PG_PORT')
+		);
+		$db = new cs_phpDB(constant('DBTYPE'));
+		$db->connect($dbParams);
+		return($db);
+	}//end create_db()
+	//--------------------------------------------------------------------------
+	
+	
+	//--------------------------------------------------------------------------
+	function test_basics() {
+		$db = $this->create_db();
+		$tok = new cs_authToken($db);
+		
+		
+	}//end test_basics()
+	//--------------------------------------------------------------------------
+}
+
+
+?>
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
 | 
| 
      
      
      From: <cra...@us...> - 2009-08-19 16:13:28
      
     | 
| Revision: 109
          http://cs-webapplibs.svn.sourceforge.net/cs-webapplibs/?rev=109&view=rev
Author:   crazedsanity
Date:     2009-08-19 16:13:16 +0000 (Wed, 19 Aug 2009)
Log Message:
-----------
Use a main abstract class which they all extend, fix require_once() paths.
Modified Paths:
--------------
    trunk/0.3/cs_webdblogger.class.php
    trunk/0.3/cs_webdbupgrade.class.php
Added Paths:
-----------
    trunk/0.3/cs_webapplibs.abstract.class.php
Added: trunk/0.3/cs_webapplibs.abstract.class.php
===================================================================
--- trunk/0.3/cs_webapplibs.abstract.class.php	                        (rev 0)
+++ trunk/0.3/cs_webapplibs.abstract.class.php	2009-08-19 16:13:16 UTC (rev 109)
@@ -0,0 +1,20 @@
+<?php
+/*
+ * Created on Aug 19, 2009
+ *
+ *  SVN INFORMATION:::
+ * -------------------
+ * Last Author::::::::: $Author$ 
+ * Current Revision:::: $Revision$ 
+ * Repository Location: $HeadURL$ 
+ * Last Updated:::::::: $Date$
+ */
+
+require_once(dirname(__FILE__) .'/cs_version.abstract.class.php');
+
+
+abstract class cs_webapplibsAbstract extends cs_versionAbstract {
+	
+}
+
+?>
Property changes on: trunk/0.3/cs_webapplibs.abstract.class.php
___________________________________________________________________
Added: svn:keywords
   + Id
Author
Revision
HeadURL
Date
Modified: trunk/0.3/cs_webdblogger.class.php
===================================================================
--- trunk/0.3/cs_webdblogger.class.php	2009-08-19 16:10:34 UTC (rev 108)
+++ trunk/0.3/cs_webdblogger.class.php	2009-08-19 16:13:16 UTC (rev 109)
@@ -27,9 +27,9 @@
 
 //NOTE::: this class **REQUIRES** cs-content for its "cs_phpDB" class.
 
-require_once(constant('LIBDIR') .'/cs-versionparse/cs_version.abstract.class.php');
+require_once(dirname(__FILE__) .'/cs_webapplibs.abstract.class.php');
 
-class cs_webdblogger extends cs_versionAbstract {
+class cs_webdblogger extends cs_webapplibsAbstract {
 	/** Database handle */
 	public $db;
 	
Modified: trunk/0.3/cs_webdbupgrade.class.php
===================================================================
--- trunk/0.3/cs_webdbupgrade.class.php	2009-08-19 16:10:34 UTC (rev 108)
+++ trunk/0.3/cs_webdbupgrade.class.php	2009-08-19 16:13:16 UTC (rev 109)
@@ -12,10 +12,10 @@
  * 
  */
 
-require_once(constant('LIBDIR') .'/cs-versionparse/cs_version.abstract.class.php');
-require_once(constant('LIBDIR') .'/cs_debug.php');
+require_once(dirname(__FILE__) .'/cs_webapplibs.abstract.class.php');
+		require_once(dirname(__FILE__) .'/cs_webdblogger.class.php');
 
-class cs_webdbupgrade extends cs_versionAbstract {
+class cs_webdbupgrade extends cs_webapplibsAbstract {
 	
 	/** cs_fileSystem{} object: for filesystem read/write operations. */
 	private $fsObj;
@@ -89,7 +89,6 @@
 		require_once(constant('LIBDIR') .'/cs-content/cs_globalFunctions.class.php');
 		require_once(constant('LIBDIR') .'/cs-content/cs_fileSystem.class.php');
 		require_once(constant('LIBDIR') .'/cs-content/cs_phpDB.class.php');
-		require_once(constant('LIBDIR') .'/cs-webdblogger/cs_webdblogger.class.php');
 		require_once(constant('LIBDIR') .'/cs-phpxml/cs_phpxmlParser.class.php');
 		require_once(constant('LIBDIR') .'/cs-phpxml/cs_phpxmlCreator.class.php');
 		require_once(constant('LIBDIR') .'/cs-phpxml/cs_arrayToPath.class.php');
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
 | 
| 
      
      
      From: <cra...@us...> - 2009-08-19 16:10:42
      
     | 
| Revision: 108
          http://cs-webapplibs.svn.sourceforge.net/cs-webapplibs/?rev=108&view=rev
Author:   crazedsanity
Date:     2009-08-19 16:10:34 +0000 (Wed, 19 Aug 2009)
Log Message:
-----------
Copy tests folder for cs_versionAbstract.
Added Paths:
-----------
    trunk/0.3/tests/
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
 | 
| 
      
      
      From: <cra...@us...> - 2009-08-19 15:47:19
      
     | 
| Revision: 107
          http://cs-webapplibs.svn.sourceforge.net/cs-webapplibs/?rev=107&view=rev
Author:   crazedsanity
Date:     2009-08-19 15:47:08 +0000 (Wed, 19 Aug 2009)
Log Message:
-----------
Ignore Eclipse's ".project" file.
Property Changed:
----------------
    trunk/0.3/
Property changes on: trunk/0.3
___________________________________________________________________
Added: svn:ignore
   + .project
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
 | 
| 
      
      
      From: <cra...@us...> - 2009-08-19 15:44:59
      
     | 
| Revision: 106
          http://cs-webapplibs.svn.sourceforge.net/cs-webapplibs/?rev=106&view=rev
Author:   crazedsanity
Date:     2009-08-19 15:44:52 +0000 (Wed, 19 Aug 2009)
Log Message:
-----------
Consolidate schema into setup files & documentation into "docs" folder.
Modified Paths:
--------------
    trunk/0.3/docs/README.txt
    trunk/0.3/setup/schema.mysql.sql
    trunk/0.3/setup/schema.pgsql.sql
Removed Paths:
-------------
    trunk/0.3/CREDITS
    trunk/0.3/LICENSE
    trunk/0.3/README.txt
    trunk/0.3/schema/
Deleted: trunk/0.3/CREDITS
===================================================================
--- trunk/0.3/CREDITS	2009-08-19 15:35:35 UTC (rev 105)
+++ trunk/0.3/CREDITS	2009-08-19 15:44:52 UTC (rev 106)
@@ -1,3 +0,0 @@
-
-Lead Developer: Dan Falconer (cra...@us...)
-
Deleted: trunk/0.3/LICENSE
===================================================================
--- trunk/0.3/LICENSE	2009-08-19 15:35:35 UTC (rev 105)
+++ trunk/0.3/LICENSE	2009-08-19 15:44:52 UTC (rev 106)
@@ -1,291 +0,0 @@
-NOTE: a full HTML version of this license can be found at: 
-http://www.opensource.org/licenses/gpl-license.php
-
-It has been reproduced below without any HTML.
-==========================================================================
-
-The GNU General Public License (GPL)
-
-Version 2, June 1991
-
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
-                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-		Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users.  This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it.  (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.)  You can apply it to
-your programs, too.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
-
-  To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have.  You must make sure that they, too, receive or can get the
-source code.  And you must show them these terms so they know their
-rights.
-
-  We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
-  Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software.  If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
-  Finally, any free program is threatened constantly by software
-patents.  We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary.  To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-
-		    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License.  The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language.  (Hereinafter, translation is included without limitation in
-the term "modification".)  Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
-  1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
-  2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-
-    a) You must cause the modified files to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    b) You must cause any work that you distribute or publish, that in
-    whole or in part contains or is derived from the Program or any
-    part thereof, to be licensed as a whole at no charge to all third
-    parties under the terms of this License.
-
-    c) If the modified program normally reads commands interactively
-    when run, you must cause it, when started running for such
-    interactive use in the most ordinary way, to print or display an
-    announcement including an appropriate copyright notice and a
-    notice that there is no warranty (or else, saying that you provide
-    a warranty) and that users may redistribute the program under
-    these conditions, and telling the user how to view a copy of this
-    License.  (Exception: if the Program itself is interactive but
-    does not normally print such an announcement, your work based on
-    the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
-    a) Accompany it with the complete corresponding machine-readable
-    source code, which must be distributed under the terms of Sections
-    1 and 2 above on a medium customarily used for software interchange; or,
-
-    b) Accompany it with a written offer, valid for at least three
-    years, to give any third party, for a charge no more than your
-    cost of physically performing source distribution, a complete
-    machine-readable copy of the corresponding source code, to be
-    distributed under the terms of Sections 1 and 2 above on a medium
-    customarily used for software interchange; or,
-
-    c) Accompany it with the information you received as to the offer
-    to distribute corresponding source code.  (This alternative is
-    allowed only for noncommercial distribution and only if you
-    received the program in object code or executable form with such
-    an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it.  For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable.  However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
-  4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License.  Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
-  5. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Program or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
-  6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
-  7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
-  8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded.  In such case, this License incorporates
-the limitation as if written in the body of this License.
-
-  9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time.  Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation.  If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
-  10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission.  For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this.  Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
-NO WARRANTY
-
-  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
-  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
-		     END OF TERMS AND CONDITIONS
Deleted: trunk/0.3/README.txt
===================================================================
--- trunk/0.3/README.txt	2009-08-19 15:35:35 UTC (rev 105)
+++ trunk/0.3/README.txt	2009-08-19 15:44:52 UTC (rev 106)
@@ -1,30 +0,0 @@
-
-=== CS Web DB Logger ===
-Once the appropriate schema has been built, code can be updated easily to start logging:
-
-//Create the class...
-$this->log = new cs_webdblogger($dbObj, 'Generic Activity');
-
-//Now call the logger.
-$this->log->log_by_class('User viewed page', 'info', $this->userId);
-
-
-
-UNDERSTANDING THE DATABASE SCHEMA:::
-I understand things best from real data, so here goes::::
-
-live_cs_project=# select rca.name as category, rcl.name as class, le.description from log_event_table AS le INNER JOIN log_class_table AS rcl USING (log_class_id) INNER JOIN log_category_table AS rca USING (log_category_id) limit 5;
- category | class  |       description
-----------+--------+--------------------------
- Project  | Create | Project: created record
- Project  | Delete | Project: deleted record
- Project  | Update | Project: updated record
- Project  | Error  | Project: ERROR
- Helpdesk | Create | Helpdesk: Created record
-(5 rows)
-
-The category indicates what system it is attached to, and class is a more 
-generic way of indicating what type of action it is. 
-
-
-$Id$ 
\ No newline at end of file
Modified: trunk/0.3/docs/README.txt
===================================================================
--- trunk/0.3/docs/README.txt	2009-08-19 15:35:35 UTC (rev 105)
+++ trunk/0.3/docs/README.txt	2009-08-19 15:44:52 UTC (rev 106)
@@ -1,6 +1,39 @@
 
 $Id$
 
+=== CS Web DB Logger ===
+Once the appropriate schema has been built, code can be updated easily to start logging:
+
+//Create the class...
+$this->log = new cs_webdblogger($dbObj, 'Generic Activity');
+
+//Now call the logger.
+$this->log->log_by_class('User viewed page', 'info', $this->userId);
+
+
+
+UNDERSTANDING THE DATABASE SCHEMA:::
+I understand things best from real data, so here goes::::
+
+live_cs_project=# select rca.name as category, rcl.name as class, le.description from log_event_table AS le INNER JOIN log_class_table AS rcl USING (log_class_id) INNER JOIN log_category_table AS rca USING (log_category_id) limit 5;
+ category | class  |       description
+----------+--------+--------------------------
+ Project  | Create | Project: created record
+ Project  | Delete | Project: deleted record
+ Project  | Update | Project: updated record
+ Project  | Error  | Project: ERROR
+ Helpdesk | Create | Helpdesk: Created record
+(5 rows)
+
+The category indicates what system it is attached to, and class is a more 
+generic way of indicating what type of action it is. 
+
+
+
+
+=== CS Web DB Upgrade ===
+
+
 This system is built to make upgrading a database-driven app seamless.  No need
 to coordinate SQL or schema changes with the code updates: previously, one would 
 have to take the entire website down, run the schema/SQL change, update the code, 
Modified: trunk/0.3/setup/schema.mysql.sql
===================================================================
--- trunk/0.3/setup/schema.mysql.sql	2009-08-19 15:35:35 UTC (rev 105)
+++ trunk/0.3/setup/schema.mysql.sql	2009-08-19 15:44:52 UTC (rev 106)
@@ -92,3 +92,20 @@
 -- 
 ALTER TABLE `cswdbl_log_table`
   ADD CONSTRAINT `cswdbl_log_table_event_id_fkey` FOREIGN KEY (`event_id`) REFERENCES `cswdbl_event_table` (`event_id`);
+  
+  
+-- This table create statement MUST work in PostgreSQL v8.2.x+ AND MySQL v5.0.x+: 
+-- otherwise separate schema files have to be created and the code will have to 
+-- do extra checking...
+-- 
+-- The "{tableName}" portion will be replaced with the value of the configured 
+-- "DB_TABLE" setting.
+CREATE TABLE cs_version_table (
+	version_id int NOT NULL PRIMARY KEY,
+	project_name varchar(30) NOT NULL UNIQUE,
+	version_string varchar(50) NOT NULL,
+	version_major integer NOT NULL,
+	version_minor integer NOT NULL,
+	version_maintenance integer NOT NULL,
+	version_suffix varchar(20) NOT NULL
+);
Modified: trunk/0.3/setup/schema.pgsql.sql
===================================================================
--- trunk/0.3/setup/schema.pgsql.sql	2009-08-19 15:35:35 UTC (rev 105)
+++ trunk/0.3/setup/schema.pgsql.sql	2009-08-19 15:44:52 UTC (rev 106)
@@ -63,3 +63,16 @@
 	details text NOT NULL
 );
 
+-- This table create statement MUST work in PostgreSQL v8.2.x+ AND MySQL v5.0.x+: 
+-- otherwise separate schema files have to be created and the code will have to 
+-- do extra checking...
+CREATE TABLE cs_version_table (
+	version_id serial NOT NULL PRIMARY KEY,
+	project_name varchar(30) NOT NULL UNIQUE,
+	version_string varchar(50) NOT NULL,
+	version_major integer NOT NULL,
+	version_minor integer NOT NULL,
+	version_maintenance integer NOT NULL,
+	version_suffix varchar(20) NOT NULL
+);
+
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
 | 
| 
      
      
      From: <cra...@us...> - 2009-08-19 15:35:48
      
     | 
| Revision: 105
          http://cs-webapplibs.svn.sourceforge.net/cs-webapplibs/?rev=105&view=rev
Author:   crazedsanity
Date:     2009-08-19 15:35:35 +0000 (Wed, 19 Aug 2009)
Log Message:
-----------
Copied stuff from cs-webdbupgrade...
Modified Paths:
--------------
    trunk/0.3/README.txt
Added Paths:
-----------
    trunk/0.3/cs_webdbupgrade.class.php
    trunk/0.3/docs/
    trunk/0.3/schema/
Modified: trunk/0.3/README.txt
===================================================================
--- trunk/0.3/README.txt	2009-08-19 15:28:38 UTC (rev 104)
+++ trunk/0.3/README.txt	2009-08-19 15:35:35 UTC (rev 105)
@@ -1,4 +1,5 @@
 
+=== CS Web DB Logger ===
 Once the appropriate schema has been built, code can be updated easily to start logging:
 
 //Create the class...
Copied: trunk/0.3/cs_webdbupgrade.class.php (from rev 101, import/cs-webdbupgrade/trunk/0.2/cs_webdbupgrade.class.php)
===================================================================
--- trunk/0.3/cs_webdbupgrade.class.php	                        (rev 0)
+++ trunk/0.3/cs_webdbupgrade.class.php	2009-08-19 15:35:35 UTC (rev 105)
@@ -0,0 +1,1096 @@
+<?php
+/*
+ * Created on Jul 2, 2007
+ * 
+ * SVN INFORMATION:::
+ * ------------------
+ * SVN Signature::::::: $Id$
+ * Last Author::::::::: $Author$ 
+ * Current Revision:::: $Revision$ 
+ * Repository Location: $HeadURL$ 
+ * Last Updated:::::::: $Date$
+ * 
+ */
+
+require_once(constant('LIBDIR') .'/cs-versionparse/cs_version.abstract.class.php');
+require_once(constant('LIBDIR') .'/cs_debug.php');
+
+class cs_webdbupgrade extends cs_versionAbstract {
+	
+	/** cs_fileSystem{} object: for filesystem read/write operations. */
+	private $fsObj;
+	
+	/** cs_globalFunctions{} object: debugging, array, and string operations. */
+	protected $gfObj;
+	
+	/** Array of configuration parameters. */
+	private $config = NULL;
+	
+	/** Name of primary key sequence of main table (for handling inserts with PostgreSQL) */
+	private $sequenceName;
+	
+	/** Database object. */
+	protected $db;
+	
+	/** Object used to log activity. */
+	protected $logsObj;
+	
+	/** Name of the project as referenced in the database. */
+	protected $projectName;
+	
+	/** Internal cache of the version string from the VERSION file. */
+	private $versionFileVersion = NULL;
+	
+	/** Stored database version. */
+	private $databaseVersion = NULL;
+	
+	/** Name (absolute location) of *.lock file that indicates an upgrade is running. */
+	private $lockfile;
+	
+	/** Determines if an internal upgrade is happening (avoids some infinite loops) */
+	private $internalUpgradeInProgress = false;
+	
+	/**  */
+	private $allowNoDBVersion=true;
+	
+	/** Log messages to store during an internal upgrade (to avoid problems) */
+	private $storedLogs = array();
+	private $debugLogs=array();
+	
+	/** List of acceptable suffixes; example "1.0.0-BETA3" -- NOTE: these MUST be in 
+	 * an order that reflects newest -> oldest; "ALPHA happens before BETA, etc. */
+	private $suffixList = array(
+		'ALPHA', 	//very unstable
+		'BETA', 	//kinda unstable, but probably useable
+		'RC'		//all known bugs fixed, searching for unknown ones
+	);
+	
+	//=========================================================================
+	public function __construct($versionFileLocation, $upgradeConfigFile, $lockFile='upgrade.lock') {
+		
+		//setup configuration parameters for database connectivity.
+		$dbParams = array(
+			'host'		=> constant(__CLASS__ .'-DB_CONNECT_HOST'),
+			'port'		=> constant(__CLASS__ .'-DB_CONNECT_PORT'),
+			'dbname'	=> constant(__CLASS__ .'-DB_CONNECT_DBNAME'),
+			'user'		=> constant(__CLASS__ .'-DB_CONNECT_USER'),
+			'password'	=> constant(__CLASS__ .'-DB_CONNECT_PASSWORD')
+		);
+		$this->config['DBPARAMS'] = $dbParams;
+		//Check for some required constants.
+		$requisiteConstants = array('LIBDIR');
+		if(!defined('LIBDIR')) {
+			throw new exception(__METHOD__ .": required constant 'LIBDIR' not set");
+		}
+		if(!defined('SITE_ROOT')) {
+			throw new exception(__METHOD__ .": required constant 'SITE_ROOT' not set");
+		}
+		
+		require_once(constant('LIBDIR') .'/cs-content/cs_globalFunctions.class.php');
+		require_once(constant('LIBDIR') .'/cs-content/cs_fileSystem.class.php');
+		require_once(constant('LIBDIR') .'/cs-content/cs_phpDB.class.php');
+		require_once(constant('LIBDIR') .'/cs-webdblogger/cs_webdblogger.class.php');
+		require_once(constant('LIBDIR') .'/cs-phpxml/cs_phpxmlParser.class.php');
+		require_once(constant('LIBDIR') .'/cs-phpxml/cs_phpxmlCreator.class.php');
+		require_once(constant('LIBDIR') .'/cs-phpxml/cs_arrayToPath.class.php');
+		
+		$this->fsObj =  new cs_fileSystem(constant('SITE_ROOT'));
+		$this->gfObj = new cs_globalFunctions;
+		if(defined('DEBUGPRINTOPT')) {
+			$this->gfObj->debugPrintOpt = constant('DEBUGPRINTOPT');
+		}
+		
+		if(!defined(__CLASS__ .'-DB_PRIMARYKEY') || !defined(__CLASS__ .'-DB_TABLE')) {
+			throw new exception(__METHOD__ .": no setting for DB_TABLE or DB_PRIMARYKEY, cannot continue");
+		}
+		else {
+			$this->config['DB_TABLE'] = constant(__CLASS__ .'-DB_TABLE');
+			$this->config['DB_PRIMARYKEY'] = constant(__CLASS__ .'-DB_PRIMARYKEY');
+		}
+		$this->sequenceName = $this->config['DB_TABLE'] .'_'. $this->config['DB_PRIMARYKEY'] .'_seq';
+		
+		if(!defined('DBTYPE')) {
+			throw new exception(__METHOD__ .": required constant 'DBTYPE' not set");
+		}
+		
+		if(!file_exists($upgradeConfigFile) || !is_readable($upgradeConfigFile)) {
+			throw new exception(__METHOD__ .": required upgrade config file location (". $upgradeConfigFile .") not set or unreadable");
+		}
+		else {
+			$this->config['UPGRADE_CONFIG_FILE'] = $upgradeConfigFile;
+		}
+		if(!strlen($versionFileLocation) || !file_exists($versionFileLocation)) {
+			throw new exception(__METHOD__ .": unable to locate version file (". $versionFileLocation .")");
+		}
+		$this->set_version_file_location($versionFileLocation);
+		
+		if(!defined(__CLASS__ .'-RWDIR') || !is_dir(constant(__CLASS__ .'-RWDIR')) || !is_readable(constant(__CLASS__ .'-RWDIR')) || !is_writable(constant(__CLASS__ .'-RWDIR'))) {
+			throw new exception(__METHOD__ .": missing RWDIR (". constant(__CLASS__ .'-RWDIR') .") or isn't readable/writable");
+		}
+		else {
+			$this->config['RWDIR'] = constant(__CLASS__ .'-RWDIR');
+		}
+		if(is_null($lockFile) || !strlen($lockFile)) {
+			$lockFile = 'upgrade.lock';
+		}
+		$this->lockfile = $this->config['RWDIR'] .'/'. $lockFile;
+		
+		$this->db = new cs_phpDB(constant('DBTYPE'));
+		try {
+			$this->db->connect($this->config['DBPARAMS']);
+		}
+		catch(exception $e) {
+			throw new exception(__METHOD__ .": failed to connect to database or logger error: ". $e->getMessage());
+		}
+		
+		if($this->check_lockfile()) {
+			//there is an existing lockfile...
+			throw new exception(__METHOD__ .": upgrade in progress: ". $this->fsObj->read($this->lockfile));
+		}
+		
+		$this->check_internal_upgrades();
+		
+		try {
+			$loggerDb = new cs_phpDB(constant('DBTYPE'));
+			$loggerDb->connect($this->config['DBPARAMS'], true);
+			$this->logsObj = new cs_webdblogger($loggerDb, "Upgrade ". $this->projectName, false);
+		}
+		catch(exception $e) {
+			throw new exception(__METHOD__ .": failed to create logger::: ". $e->getMessage());
+		}
+		
+		$this->check_versions(false);
+	}//end __construct()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	/**
+	 * Determine if there are any upgrades that need to be performed...
+	 */
+	private function check_internal_upgrades() {
+		$oldVersionFileLocation = $this->versionFileLocation;
+		$oldUpgradeConfigFile = $this->config['UPGRADE_CONFIG_FILE'];
+		$this->config['UPGRADE_CONFIG_FILE'] = dirname(__FILE__) .'/upgrades/upgrade.xml';
+		
+		
+		//set a status flag so we can store log messages (for now).
+		$this->internalUpgradeInProgress = true;
+		
+		
+		//do stuff here...
+		$this->set_version_file_location(dirname(__FILE__) .'/VERSION');
+		$this->read_version_file();
+		
+		//if there is an error, then... uh... yeah.
+		try {
+			$this->get_database_version();
+		}
+		catch(exception $e) {
+			throw new exception(__METHOD__ .": error while retrieving database version: ". $e->getMessage());
+			
+			//try creating the version.
+			$this->load_initial_version();
+		}
+		
+		//do upgrades here...
+		$this->check_versions(true);
+		$this->internalUpgradeInProgress = false;
+		
+		
+		
+		
+		//reset internal vars.
+		$this->set_version_file_location($oldVersionFileLocation);
+		$this->config['UPGRADE_CONFIG_FILE'] = $oldUpgradeConfigFile;
+		$this->read_version_file();
+		
+	}//end check_internal_upgrades()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	/**
+	 * Where everything begins: checks if the version held in config.xml lines-up 
+	 * with the one in the VERSION file; if it does, then it checks the version 
+	 * listed in the database.
+	 */
+	public function check_versions($performUpgrade=TRUE) {
+		if(!is_bool($performUpgrade) || is_null($performUpgrade)) {
+			$performUpgrade = true;
+		}
+		
+		//first, check that all files exist.
+		$retval = NULL;
+		
+		//check to see if the lock files for upgrading exist.
+		if($this->upgrade_in_progress()) {
+			$this->do_log("Upgrade in progress", 'notice');
+			throw new exception(__METHOD__ .": upgrade in progress");
+		}
+		else {
+			//okay, all files present: check the version in the VERSION file.
+			$versionFileVersion = $this->read_version_file();
+			$dbVersion = $this->get_database_version();
+			if(!is_array($dbVersion)) {
+				$this->load_initial_version();
+			}
+			
+			$versionsDiffer = TRUE;
+			$retval = FALSE;
+			
+			if($this->check_for_version_conflict() == false) {
+				$versionsDiffer = false;
+				$performUpgrade = false;
+			}
+			else {
+				$versionsDiffer = true;
+			}
+			
+			if($versionsDiffer == TRUE && $performUpgrade === TRUE) {
+				//reset the return value, so it'll default to failure until we say otherwise.
+				$retval = NULL;
+				
+				//Perform the upgrade!
+				$this->perform_upgrade();
+			}
+		}
+		
+		return($retval);
+	}//end check_versions()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	protected function read_version_file() {
+		$retval = NULL;
+		
+		//okay, all files present: check the version in the VERSION file.
+		$versionFileContents = $this->fsObj->read($this->versionFileLocation);
+		
+		
+		$versionMatches = array();
+		preg_match_all('/\nVERSION: (.*)\n/', $versionFileContents, $versionMatches);
+		if(count($versionMatches) == 2 && count($versionMatches[1]) == 1) {
+			$retval = trim($versionMatches[1][0]);
+			$this->versionFileVersion = $this->get_full_version_string($retval);
+			
+			//now retrieve the PROJECT name.
+			$projectMatches = array();
+			preg_match_all('/\nPROJECT: (.*)\n/', $versionFileContents, $projectMatches);
+			if(count($projectMatches) == 2 && count($projectMatches[1]) == 1) {
+				$this->projectName = trim($projectMatches[1][0]);
+			}
+			else {
+				$this->error_handler(__METHOD__ .": failed to find PROJECT name");
+			}
+		}
+		else {
+			$this->error_handler(__METHOD__ .": could not find VERSION data");
+		}
+		
+		return($retval);
+	}//end read_version_file()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	/**
+	 * Read information from our config file, so we know what to expect.
+	 */
+	private function read_upgrade_config_file() {
+		try {
+			$xmlString = $this->fsObj->read($this->config['UPGRADE_CONFIG_FILE']);
+		}
+		catch(exception $e) {
+			throw new exception(__METHOD__ .": failed to read upgrade config file::: ". $e->getMessage());
+		}
+		
+		//parse the file.
+		$xmlParser = new cs_phpxmlParser($xmlString);
+		
+		$config = $xmlParser->get_tree(TRUE);
+		
+		if(is_array($config['UPGRADE']) && count($config['UPGRADE'])) {
+			$this->config['UPGRADELIST'] = $config['UPGRADE'];
+		}
+		else {
+			$this->error_handler(__METHOD__ .": failed to retrieve 'UPGRADE' section; " .
+					"make sure upgrade.xml's ROOT element is 'UPGRADE'");
+		}
+	}//end read_upgrade_config_file()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	private function perform_upgrade() {
+		$this->logsObj->suspendLogging=true;
+		//make sure there's not already a lockfile.
+		if($this->upgrade_in_progress()) {
+			//ew.  Can't upgrade.
+			$this->error_handler(__METHOD__ .": upgrade already in progress...????");
+		}
+		else {
+			$lockConfig = $this->upgrade_in_progress(TRUE);
+			$this->fsObj->cd("/");
+			
+			$this->do_log("Starting upgrade process...", 'begin');
+			
+			//TODO: not only should the "create_file()" method be run, but also do a sanity check by calling lock_file_exists().
+			if($lockConfig === 0) {
+				//can't create the lockfile.  Die.
+				$this->error_handler(__METHOD__ .": failed to set 'upgrade in progress'");
+			}
+			else {
+				$this->do_log(__METHOD__ .": result of creating lockfile: (". $lockConfig .")", 'debug');
+				
+				//push data into our internal "config" array.
+				$this->read_upgrade_config_file();
+				$this->get_database_version();
+				
+				//check for version conflicts.
+				$versionConflictInfo = $this->check_for_version_conflict();
+				
+				
+				if($versionConflictInfo !== false) {
+					$this->do_log("Upgrading ". $versionConflictInfo ." versions, from " .
+							"(". $this->databaseVersion .") to (". $this->versionFileVersion .")", 'info');
+				}
+				
+				$upgradeList = $this->get_upgrade_list();
+				try {
+					$i=0;
+					$this->do_log(__METHOD__ .": starting to run through the upgrade list, starting at (". $this->databaseVersion ."), " .
+							"total number of upgrades to perform: ". count($upgradeList), 'debug');
+					$this->db->beginTrans(__METHOD__);
+					foreach($upgradeList as $fromVersion=>$toVersion) {
+						
+						$details = __METHOD__ .": upgrading from ". $fromVersion ." to ". $toVersion ."... ";
+						$this->do_log($details, 'system');
+						$this->do_single_upgrade($fromVersion, $toVersion);
+						$this->get_database_version();
+						$i++;
+						
+						$details = __METHOD__ .": finished upgrade #". $i .", now at version (". $this->databaseVersion .")";
+						$this->do_log($details, 'system');
+					}
+					
+					if($i < count($upgradeList)) {
+						$this->do_log(__METHOD__ .": completed upgrade ". $i ." of ". count($upgradeList), 'debug');
+					}
+					else {
+						if($this->databaseVersion == $this->versionFileVersion) {
+							$this->do_log(__METHOD__ .": finished upgrading after performing (". $i .") upgrades", 'debug');
+							$this->newVersion = $this->databaseVersion;
+						}
+						else {
+							$this->do_log(__METHOD__ .": upgradeList::: ". $this->gfObj->debug_print($upgradeList,0), 'debug');
+							$this->error_handler(__METHOD__ .": finished upgrade, but version wasn't updated (expecting '". $this->versionFileVersion ."', got '". $this->databaseVersion ."')!!!");
+						}
+					}
+					$this->remove_lockfile();
+					
+					$this->db->commitTrans();
+				}
+				catch(exception $e) {
+					$transactionStatus = $this->db->get_transaction_status(false);
+					$this->error_handler(__METHOD__ .": transaction status=(". $transactionStatus ."), upgrade aborted:::". $e->getMessage());
+					$this->db->rollbackTrans();
+				}
+				$this->logsObj->suspendLogging=false;
+				$this->do_log("Upgrade process complete", 'end');
+			}
+		}
+		$this->logsObj->suspendLogging=false;
+		$logsHandled = $this->logsObj->handle_suspended_logs();
+	}//end perform_upgrade()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	public function upgrade_in_progress($makeItSo=FALSE) {
+		if($makeItSo === TRUE) {
+			if(strlen($this->databaseVersion)) {
+				$details = $this->projectName .': Upgrade from '. $this->databaseVersion .' started at '. date('Y-m-d H:i:s');
+				$this->create_lockfile($details);
+				$retval = TRUE;
+			}
+			else {
+				$this->error_handler(__METHOD__ .": missing internal databaseVersion (". $this->databaseVersion .")");
+			}
+		}
+		$retval = $this->check_lockfile();
+		
+		return($retval);
+	}//end upgrade_in_progress()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	public function parse_version_string($versionString) {
+		if(is_null($versionString) || !strlen($versionString)) {
+			$this->error_handler(__METHOD__ .": invalid version string ($versionString)");
+		}
+		
+		$suffix = "";
+		$explodeThis = $versionString;
+		if(preg_match('/-[A-Z]{2,5}[0-9]{1,}/', $versionString)) {
+			$bits = explode('-', $versionString);
+			$suffix = $bits[1];
+			$explodeThis = $bits[0];
+		}
+		$tmp = explode('.', $explodeThis);
+		
+		
+		if(is_numeric($tmp[0]) && is_numeric($tmp[1])) {
+			$retval = array(
+				'version_string'	=> $versionString,
+				'version_major'		=> $tmp[0],
+				'version_minor'		=> $tmp[1],
+			);
+			if(isset($tmp[2])) {
+				$retval['version_maintenance'] = $tmp[2];
+			}
+			else {
+				$retval['version_maintenance'] = 0;
+			}
+			
+			$retval['version_suffix'] = $suffix;
+		}
+		else {
+			$this->error_handler(__METHOD__ .": invalid version string format, requires MAJOR.MINOR syntax (". $versionString .")");
+		}
+		
+		return($retval);
+	}//end parse_version_string()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	/**
+	 * Checks for issues with versions.
+	 * 0		== No problems.
+	 * (string)	== upgrade applicable (indicates "major"/"minor"/"maintenance").
+	 * NULL		== encountered error
+	 */
+	private function check_for_version_conflict() {
+		//set a default return...
+		$retval = NULL;
+		
+		$this->read_version_file();
+		
+		//parse the version strings.
+		$dbVersion = $this->get_database_version();
+		$versionFileData = $this->parse_version_string($this->versionFileVersion);
+		
+		if($versionFileData['version_string'] == $dbVersion['version_string']) {
+			//good to go: no upgrade needed.
+			$retval = false;
+		}
+		else {
+			//NOTE: this seems very convoluted, but it works.
+			if($versionFileData['version_major'] == $dbVersion['version_major']) {
+				if($versionFileData['version_minor'] == $dbVersion['version_minor']) {
+					if($versionFileData['version_maintenance'] == $dbVersion['version_maintenance']) {
+						if($versionFileData['version_suffix'] == $dbVersion['version_suffix']) {
+							$this->error_handler(__METHOD__ .": no version upgrade detected, but version strings don't match (versionFile=". $versionFileData['version_string'] .", dbVersion=". $dbVersion['version_string'] .")");
+						}
+						else {
+							$retval = "suffix";
+						}
+					}
+					elseif($versionFileData['version_maintenance'] > $dbVersion['version_maintenance']) {
+						$retval = "maintenance";
+					}
+					else {
+						$this->error_handler(__METHOD__ .": downgrading from maintenance versions is unsupported");
+					}
+				}
+				elseif($versionFileData['version_minor'] > $dbVersion['version_minor']) {
+					$retval = "minor";
+				}
+				else {
+					$this->error_handler(__METHOD__ .": downgrading minor versions is unsupported");
+				}
+			}
+			elseif($versionFileData['version_major'] > $dbVersion['version_major']) {
+				$retval = "major";
+			}
+			else {
+				$this->error_handler(__METHOD__ .": downgrading major versions is unsupported");
+			}
+		}
+		
+		return($retval);
+	}//end check_for_version_conflict()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	private function get_database_version() {
+		$this->gfObj->debugPrintOpt=1;
+		//create a database object & attempt to read the database version.
+		
+		$sql = "SELECT * FROM ". $this->config['DB_TABLE'] ." WHERE project_name='" .
+				$this->gfObj->cleanString($this->projectName, 'sql') ."'";
+		
+		$numrows = $this->db->exec($sql);
+		$dberror = $this->db->errorMsg();
+		
+		if(strlen($dberror) || $numrows != 1) {
+			//
+			if(preg_match('/doesn\'t exist/', $dberror) || preg_match('/does not exist/', $dberror)) {
+				//add the table...
+				$loadTableResult = $this->load_table();
+				if($loadTableResult === TRUE) {
+					//now try the SQL...
+					$numrows = $this->db->exec($sql);
+					$dberror = $this->db->errorMsg();
+				}
+				else {
+					$this->error_handler(__METHOD__ .": no table in database, failed to create one... ORIGINAL " .
+						"ERROR: ". $dberror .", SCHEMA LOAD ERROR::: ". $loadTableResult);
+				}
+			}
+			elseif(!strlen($dberror) && $numrows == 0) {
+				if($this->allowNoDBVersion) {
+					$retval = false;
+				}
+				else {
+					$this->error_handler(__METHOD__ .": no version data found for (". $this->projectName .")");
+				}
+			}
+			else {
+				$this->error_handler(__METHOD__ .": failed to retrieve version... numrows=(". $numrows ."), DBERROR::: ". $dberror);
+			}
+		}
+		else {
+			$data = $this->db->farray_fieldnames();
+			$this->databaseVersion = $data['version_string'];
+			$retval = $this->parse_version_string($data['version_string']);
+		}
+		
+		return($retval);
+	}//end get_database_version()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	private function do_single_upgrade($fromVersion, $toVersion=null) {
+		//Use the "matching_syntax" data in the upgrade.xml file to determine the filename.
+		$versionIndex = "V". $this->get_full_version_string($fromVersion);
+		if(!isset($this->config['UPGRADELIST']['MATCHING'][$versionIndex])) {
+			//version-only upgrade.
+			$this->newVersion = $toVersion;
+			$this->update_database_version($toVersion);
+		}
+		else {
+			//scripted upgrade...
+			$scriptIndex = $versionIndex;
+			
+			$upgradeData = $this->config['UPGRADELIST']['MATCHING'][$versionIndex];
+			
+			if(isset($upgradeData['TARGET_VERSION']) && count($upgradeData) > 1) {
+				$this->newVersion = $upgradeData['TARGET_VERSION'];
+				if(isset($upgradeData['SCRIPT_NAME']) && isset($upgradeData['CLASS_NAME']) && isset($upgradeData['CALL_METHOD'])) {
+					//good to go; it's a scripted upgrade.
+					$this->do_scripted_upgrade($upgradeData);
+					$this->update_database_version($upgradeData['TARGET_VERSION']);
+				}
+				else {
+					$this->error_handler(__METHOD__ .": not enough information to run scripted upgrade for ". $versionIndex);
+				}
+			}
+			else {
+				$this->error_handler(__METHOD__ .": target version not specified, unable to proceed with upgrade for ". $versionIndex);
+			}
+		}
+		$this->do_log("Finished upgrade to ". $this->newVersion, 'system');
+	}//end do_single_upgrade()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	/**
+	 * Updates information that's stored in the database, internal to cs-project, 
+	 * so the version there is consistent with all the others.
+	 */
+	protected function update_database_version($newVersionString) {
+		$versionInfo = $this->parse_version_string($newVersionString);
+		
+		$sql = "UPDATE ". $this->config['DB_TABLE'] ." SET version_string='". 
+				$this->gfObj->cleanString($versionInfo['version_string'], 'sql') 
+				."' WHERE project_name='". 
+				$this->gfObj->cleanString($this->projectName, 'sql') ."'";
+		
+		
+		$updateRes = $this->db->run_update($sql,false);
+		if($updateRes == 1) {
+			$retval = $updateRes;
+		}
+		else {
+			$this->error_handler(__METHOD__ .": invalid result (". $updateRes .")	");
+		}
+		
+		//okay, now check that the version string matches the updated bits.
+		if(!$this->check_database_version($this->newVersion)) {
+			$this->error_handler(__METHOD__ .": database version information is invalid: (". $this->newVersion .")");
+		}
+		
+		return($retval);
+		
+	}//end update_database_version()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	/**
+	 * Checks consistency of version information in the database, and optionally 
+	 * against a given version string.
+	 */
+	private function check_database_version() {
+		//retrieve the internal version information.
+		if(!is_null($this->newVersion)) {
+			$data = $this->get_database_version();
+			$versionString = $data['version_string'];
+			
+			if($versionString == $this->newVersion) {
+				$retval = TRUE; 
+			}
+			else {
+				$retval = FALSE;
+			}
+			
+			if(!$retval) {
+				$this->do_log("Version check failed, versionString=(". $versionString ."), checkVersion=(". $this->newVersion .")", 'FATAL');
+			}
+			
+		}
+		else {
+			$this->error_handler(__METHOD__ .": no version string given (". $this->newVersion .")");
+		}
+		
+		return($retval);
+	}//end check_database_version()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	private function do_scripted_upgrade(array $upgradeData) {
+		$myConfigFile = $upgradeData['SCRIPT_NAME'];
+		
+		$this->do_log("Preparing to run script '". $myConfigFile ."'", 'debug');
+		
+		//we've got the filename, see if it exists.
+		
+		$scriptsDir = dirname($this->config['UPGRADE_CONFIG_FILE']);
+		$fileName = $scriptsDir .'/'. $myConfigFile;
+		
+		if(file_exists($fileName)) {
+			
+			$this->do_log("(". __CLASS__ .") Performing scripted upgrade (". $myConfigFile .") from file '". $fileName ."'", 'DEBUG');
+			$createClassName = $upgradeData['CLASS_NAME'];
+			$classUpgradeMethod = $upgradeData['CALL_METHOD'];
+			require_once($fileName);
+			
+			//now check to see that the class we need actually exists.
+			if(class_exists($createClassName)) {
+				$upgradeObj = new $createClassName($this->db);
+				if(method_exists($upgradeObj, $classUpgradeMethod)) {
+					$upgradeResult = $upgradeObj->$classUpgradeMethod();
+					
+					if($upgradeResult === true) {
+						//yay, it worked!
+						$this->do_log("Upgrade succeeded (". $upgradeResult .")", 'success');
+					}
+					else {
+						$this->error_handler(__METHOD__ .": upgrade failed (". $upgradeResult .")");
+					}
+					$this->do_log("Finished running ". $createClassName ."::". $classUpgradeMethod ."(), result was (". $upgradeResult .")", 'debug');
+				}
+				else {
+					$this->error_handler(__METHOD__ .": upgrade method doesn't exist (". $createClassName ."::". $classUpgradeMethod 
+						."), unable to perform upgrade ");
+				}
+			}
+			else {
+				$this->error_handler(__METHOD__ .": unable to locate upgrade class name (". $createClassName .")");
+			}
+		}
+		else {
+			$this->error_handler(__METHOD__ .": upgrade filename (". $fileName .") does not exist");
+		}
+	}//end do_scripted_upgrade()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	public function is_higher_version($version, $checkIfHigher) {
+		try {
+			$retval = parent::is_higher_version($version, $checkIfHigher);
+		}
+		catch(exception $e) {
+			$this->error_handler($e->getMessage());
+		}
+		
+		return($retval);
+		
+	}//end is_higher_version()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	/**
+	 * Determines list of upgrades to perform.
+	 * 
+	 * If the current version is 1.0.1, the version file is 1.0.5, and there's a 
+	 * scripted upgrade at 1.0.4, this will update the database version to 1.0.3, 
+	 * run the scripted upgrade at 1.0.4, then update the database version to 
+	 * 1.0.5 (keeps from skipping the upgrade at 1.0.4)
+	 */
+	private function get_upgrade_list() {
+		$this->get_database_version();
+		$dbVersion = $this->databaseVersion;
+		$newVersion = $this->versionFileVersion;
+		
+		$retval = array();
+		if(!$this->is_higher_version($dbVersion, $newVersion)) {
+			$this->error_handler(__METHOD__ .": version (". $newVersion .") isn't higher than (". $dbVersion .")... something is broken");
+		}
+		elseif(is_array($this->config['UPGRADELIST']['MATCHING'])) {
+			$lastVersion = $dbVersion;
+			foreach($this->config['UPGRADELIST']['MATCHING'] as $matchVersion=>$data) {
+				
+				$matchVersion = preg_replace('/^V/', '', $matchVersion);
+				if($matchVersion == $data['TARGET_VERSION']) {
+					$this->error_handler(__METHOD__ .": detected invalid TARGET_VERSION in (". $matchVersion ."): make sure TARGET_VERSION is higher than matching!");
+				}
+				elseif($this->databaseVersion == $matchVersion || $this->is_higher_version($this->databaseVersion, $matchVersion)) {
+					//the version in MATCHING is equal to or HIGHER than our database version... make sure it is NOT
+					//	higher than the version in our versionFile.
+					if(!$this->is_higher_version($this->versionFileVersion, $matchVersion)) {
+						if(!count($retval) && $matchVersion != $this->databaseVersion) {
+							$retval[$this->databaseVersion] = $matchVersion;
+						}
+						//the MATCHING version is NOT higher than the version file's version, looks ok.
+						$lastVersion = $data['TARGET_VERSION'];
+						$retval[$matchVersion] = $data['TARGET_VERSION'];
+					}
+					else {
+						$this->do_log(__METHOD__ .": entry in upgrade.xml (". $matchVersion .") is higher than the VERSION file (". $this->versionFileVersion .")", 'warning');
+					}
+				}
+				else {
+					$this->do_log(__METHOD__ .": SKIPPING (". $matchVersion .")", 'debug');
+				}
+			}
+			
+			if($lastVersion !== $newVersion && (!isset($retval[$lastVersion]) || $retval[$lastVersion] != $newVersion)) {
+				$retval[$lastVersion] = $newVersion;
+			}
+		}
+		else {
+			//no intermediary upgrades: just pass back the latest version.
+			$this->do_log(__METHOD__ .": no intermediary upgrades", 'debug');
+			$retval[$dbVersion] = $this->versionFileVersion;
+		}
+		
+		return($retval);
+		
+	}//end get_upgrade_list()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	protected function parse_suffix($suffix) {
+		$retval = NULL;
+		if(strlen($suffix)) {
+			//determine what kind it is.
+			foreach($this->suffixList as $type) {
+				if(preg_match('/^'. $type .'/', $suffix)) {
+					$checkThis = preg_replace('/^'. $type .'/', '', $suffix);
+					if(strlen($checkThis) && is_numeric($checkThis)) {
+						//oooh... it's something like "BETA3"
+						$retval = array(
+							'type'		=> $type,
+							'number'	=> $checkThis
+						);
+					}
+					else {
+						$this->error_handler(__METHOD__ .": invalid suffix (". $suffix .")");
+					}
+					break;
+				}
+			}
+		}
+		else {
+			$this->error_handler(__METHOD__ .": invalid suffix (". $suffix .")");
+		}
+		
+		return($retval);
+	}//end parse_suffix()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	private function fix_xml_config($config, $path=null) {
+		$this->xmlLoops++;
+		if($this->xmlLoops > 1000) {
+			$this->error_handler(__METHOD__ .": infinite loop detected...");
+		}
+		
+		try {
+			$a2p = new cs_arrayToPath($config);
+		}
+		catch(exception $e) {
+			$this->do_log(__METHOD__ .': encountered exception: '. $e->getMessage());
+			$this->error_handler($e->getMessage());
+		}
+		if(!is_array($this->tempXmlConfig)) {	
+			$this->tempXmlConfig = array();
+		}
+		try {
+			$myA2p = new cs_arrayToPath(&$this->tempXmlConfig);
+		}
+		catch(exception $e) {
+			$this->do_log(__METHOD__ .': encountered exception: '. $e->getMessage());
+			$this->error_handler($e->getMessage());
+		}
+		
+		$myData = $a2p->get_data($path);
+		
+		if(is_array($myData)) {
+			if(isset($myData['type']) && $myData['type'] != 'open') {
+				if($myData['type'] == 'complete') {
+					$val = null;
+					if(isset($myData['value'])) {
+						$val = $myData['value'];
+					}
+					$oldData = $myA2p->get_data();
+					$myA2p->set_data($path, $val);
+					$this->tempXmlConfig = $myA2p->get_data();
+				}
+				else {
+					$this->error_handler(__METHOD__ .": invalid type (". $myData['type'] .")");
+				}
+			}
+			else {
+				foreach($myData as $i=>$d) {
+					if(!in_array($i, array('type', 'attributes', 'value'))) {
+						$this->fix_xml_config($config, $path .'/'. $i);
+					}
+				}
+			}
+		}
+		else {
+			$this->error_handler(__METHOD__ .": unable to fix data on path=(". $path .")::: ". $this->gfObj->debug_print($myData,0));
+		}
+	}//end fix_xml_config()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	public function load_table() {
+		$schemaFileLocation = dirname(__FILE__) .'/schema/schema.sql';
+		$schema = file_get_contents($schemaFileLocation);
+		$schema = str_replace('{tableName}', $this->config['DB_TABLE'], $schema);
+		$schema = str_replace('{primaryKey}', $this->config['DB_PRIMARYKEY'], $schema);
+		$this->db->exec($schema);
+		
+		$loadTableResult = $this->db->errorMsg();
+		if(!strlen($loadTableResult)) {
+			$loadTableResult = true;
+			$logRes = 'Successfully loaded';
+			$logType = 'initialize';
+			
+			//now set the initial version information...
+			if(strlen($this->projectName) && strlen($this->versionFileVersion)) {
+				$this->load_initial_version();
+			}
+			else {
+				throw new exception(__METHOD__ .": missing projectName (". $this->projectName .") " .
+						"or versionFileVersion (". $this->versionFileVersion ."), cannot load data");
+			}
+		}
+		else {
+			$logRes = 'Failed to load';
+			$logType = 'error';
+		}
+		$this->do_log($logRes .' table ('. $this->config['DB_TABLE'] .') into ' .
+				'database::: '. $loadTableResult, $logType);
+		
+		return($loadTableResult);
+	}//end load_table()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	public function check_lockfile() {
+		$status = false;
+		if(file_exists($this->lockfile)) {
+			$status = true;
+		}
+		
+		return($status);
+	}//end check_lockfile()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	/**
+	 * Create a *.lock file that indicates the system is in the process of 
+	 * performing an upgrade (safer than always updating the site's configuration 
+	 * file).
+	 */
+	public function create_lockfile($contents) {
+		if(!$this->check_lockfile()) {
+			if($this->fsObj->create_file($this->lockfile)) {
+				if(!preg_match('/\n$/', $contents)) {
+					$contents .= "\n";
+				}
+				$writeRes = $this->fsObj->write($contents);
+				if(is_numeric($writeRes) && $writeRes > 0) {
+					$this->fsObj->closeFile();
+				}
+				else {
+					$this->error_handler(__METHOD__ .": failed to write contents (". $contents .") to lockfile");
+				}
+			}
+			else {
+				$this->error_handler(__METHOD__ .": failed to create lockfile (". $this->lockfile .")");
+			}
+		}
+		else {
+			$this->error_handler(__METHOD__ .": failed to create lockfile, one already exists (". $this->lockfile .")");
+		}
+	}//end create_lockfile()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	/**
+	 * Destroy the *.lock file that indicates an upgrade is underway.
+	 */
+	private function remove_lockfile() {
+		if($this->check_lockfile()) {
+			if(!$this->fsObj->rm($this->lockfile)) {
+				$this->error_handler(__METHOD__ .": failed to remove lockfile (". $this->lockfile .")");
+			}
+		}
+		else {
+			$this->error_handler(__METHOD__ .": no lockfile (". $this->lockfile .")");
+		}
+	}//end remove_lockfile()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	private function get_full_version_string($versionString) {
+		if(strlen($versionString)) {
+			$bits = $this->parse_version_string($versionString);
+			
+			$fullVersion = $bits['version_major'] .'.'. $bits['version_minor'] .'.'.
+					$bits['version_maintenance'];
+			if(strlen($bits['version_suffix'])) {
+				$fullVersion .= '-'. $bits['version_suffix'];
+			}
+		}
+		else {
+			$this->error_handler(__METHOD__ .": no version string given");
+		}
+		
+		return($fullVersion);
+	}//end get_full_version_string()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	public function error_handler($details) {
+		//log the error.
+		if(!is_object($this->logsObj)) {
+			throw new exception(__METHOD__ .": error while running an internal upgrade::: ". $details);
+		}
+		if($this->internalUpgradeInProgress === false) {
+			$this->do_log($details, 'exception in code');
+		}
+		
+		//now throw an exception so other code can catch it.
+		throw new exception($details);
+	}//end error_handler()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	public function load_initial_version() {
+		//if there's an INITIAL_VERSION in the upgrade config file, use that.
+		$this->read_upgrade_config_file();
+		$insertData = array();
+		if(isset($this->config['UPGRADELIST']['INITIALVERSION'])) {
+			$parseThis = $this->config['UPGRADELIST']['INITIALVERSION'];
+		}
+		else {
+			$parseThis = $this->versionFileVersion;
+		}
+		$versionInfo = $this->parse_version_string($parseThis);
+		$insertData = array(
+			'project_name'		=> $this->projectName,
+			'version_string'	=> $versionInfo['version_string']
+		);
+		
+		$sql = 'INSERT INTO '. $this->config['DB_TABLE'] . $this->gfObj->string_from_array($insertData, 'insert');
+		
+		if($this->db->run_insert($sql, $this->sequenceName)) {
+			$loadRes = true;
+			$this->do_log("Created data for '". $this->projectName ."' with version '". $insertData['version_string'] ."'", 'initialize');
+		}
+		else {
+			$this->error_handler(__METHOD__ .": failed to load initial version::: ". $e->getMessage());
+		}
+		
+		return($loadRes);
+	}//end load_initial_version()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	protected function do_log($message, $type) {
+		$this->debugLogs[] = array('project'=>$this->projectName,'upgradeFile'=>$this->config['UPGRADE_CONFIG_FILE'],'message'=>$message,'type'=>$type);
+		if($this->internalUpgradeInProgress === true) {
+			$this->storedLogs[] = func_get_args();
+		}
+		else {
+			$this->logsObj->log_by_class($message, $type);
+		}
+	}//end do_log()
+	//=========================================================================
+	
+	
+}//end upgrade{}
+
+
+?>
\ No newline at end of file
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
 | 
| 
      
      
      From: <cra...@us...> - 2009-08-19 15:28:47
      
     | 
| Revision: 104
          http://cs-webapplibs.svn.sourceforge.net/cs-webapplibs/?rev=104&view=rev
Author:   crazedsanity
Date:     2009-08-19 15:28:38 +0000 (Wed, 19 Aug 2009)
Log Message:
-----------
Copied folders from cs-webdblogger...
Added Paths:
-----------
    trunk/0.3/README.txt
    trunk/0.3/cs_webdblogger.class.php
    trunk/0.3/setup/
    trunk/0.3/upgrades/
Copied: trunk/0.3/README.txt (from rev 101, import/cs-webdblogger/trunk/0.2/README.txt)
===================================================================
--- trunk/0.3/README.txt	                        (rev 0)
+++ trunk/0.3/README.txt	2009-08-19 15:28:38 UTC (rev 104)
@@ -0,0 +1,29 @@
+
+Once the appropriate schema has been built, code can be updated easily to start logging:
+
+//Create the class...
+$this->log = new cs_webdblogger($dbObj, 'Generic Activity');
+
+//Now call the logger.
+$this->log->log_by_class('User viewed page', 'info', $this->userId);
+
+
+
+UNDERSTANDING THE DATABASE SCHEMA:::
+I understand things best from real data, so here goes::::
+
+live_cs_project=# select rca.name as category, rcl.name as class, le.description from log_event_table AS le INNER JOIN log_class_table AS rcl USING (log_class_id) INNER JOIN log_category_table AS rca USING (log_category_id) limit 5;
+ category | class  |       description
+----------+--------+--------------------------
+ Project  | Create | Project: created record
+ Project  | Delete | Project: deleted record
+ Project  | Update | Project: updated record
+ Project  | Error  | Project: ERROR
+ Helpdesk | Create | Helpdesk: Created record
+(5 rows)
+
+The category indicates what system it is attached to, and class is a more 
+generic way of indicating what type of action it is. 
+
+
+$Id$ 
\ No newline at end of file
Copied: trunk/0.3/cs_webdblogger.class.php (from rev 101, import/cs-webdblogger/trunk/0.2/cs_webdblogger.class.php)
===================================================================
--- trunk/0.3/cs_webdblogger.class.php	                        (rev 0)
+++ trunk/0.3/cs_webdblogger.class.php	2009-08-19 15:28:38 UTC (rev 104)
@@ -0,0 +1,864 @@
+<?php
+/*
+ * Created on Mar 8, 2007
+ * 
+ * NOTICE::: this class was derived from the logsClass.php found in cs-projet v1.2, found
+ * at URL: https://cs-project.svn.sourceforge.net/svnroot/cs-project/trunk/1.2/lib/logsClass.php
+ * Last SVN Signature (from cs-project v1.2): "logsClass.php 819 2008-02-09 10:01:10Z crazedsanity"
+ * 
+ * SVN INFORMATION:::
+ * ------------------
+ * SVN Signature::::::: $Id$
+ * Last Author::::::::: $Author$ 
+ * Current Revision:::: $Revision$ 
+ * Repository Location: $HeadURL$ 
+ * Last Updated:::::::: $Date$
+ * 
+ * 
+ * Each class that's trying to log should have an internal var statically set to indicates what category 
+ * it is: this allows them to call a method within this and tell it ONLY what "class" the log should be
+ * under, so this class can determine the appropriate log_event_id.  This avoids having to hard-code 
+ * too many id's that might need to be changed later.  Yay, dynamic code!
+ * 
+ * QUERY TO GET LAST COUPLE OF LOGS::::
+ SELECT l.log_id as id, l.creation, l.event_id as lid, le.description AS event, l.details 
+ FROM cswdbl_log_table AS l INNER JOIN cswdbl_event_table AS le USING (event_id) ORDER BY log_id DESC LIMIT 25;
+ */
+
+//NOTE::: this class **REQUIRES** cs-content for its "cs_phpDB" class.
+
+require_once(constant('LIBDIR') .'/cs-versionparse/cs_version.abstract.class.php');
+
+class cs_webdblogger extends cs_versionAbstract {
+	/** Database handle */
+	public $db;
+	
+	/** Cache of all records in the class table */
+	private $logClassCache = array();
+	
+	/** Cache of all records in the attribute table */
+	private $attributeCache=array();
+	
+	/** The category_id value to use, set on class creation. */
+	private $logCategoryId = null;
+	
+	/** Default uid (users.id) to log under when no uid is available */
+	private $defaultUid = 0;
+	
+	/** Category to use when logging a database error */
+	//TODO: make SURE this category is correct...
+	private $databaseCategory = 1;
+	
+	/** Check to see if setup has been performed (avoids running it multiple times) **/
+	private $setupComplete=false;
+	
+	/** Last SQL file handled */
+	protected $lastSQLFile=null;
+	
+	/** Global functions class from cs-content */
+	protected $gfObj;
+	
+	protected $pendingLogs;
+	private $suspendLogging=false;
+	
+	/** List of tables keyed off an internal reference name. */
+	protected $tables = array(
+		'category'	=> 'cswdbl_category_table',
+		'class'		=> 'cswdbl_class_table',
+		'event'		=> 'cswdbl_event_table',
+		'log'		=> 'cswdbl_log_table',
+		'attrib'	=> 'cswdbl_attribute_table',
+		'logAttrib'	=> 'cswdbl_log_attribute_table'
+	);
+	
+	/** List of sequences keyed off an internal reference name (MUST match references above) */
+	protected $seqs = array(
+		'category'		=> "cswdbl_category_table_category_id_seq",
+		'class'			=> "cswdbl_class_table_class_id_seq",
+		'event'			=> "cswdbl_event_table_event_id_seq",
+		'log'			=> "cswdbl_log_table_log_id_seq",
+		'attrib'		=> "cswdbl_attribute_table_attribute_id_seq",
+		'logAttrib'		=> "cswdbl_log_attribute_table_log_attribute_id_seq"
+	);
+	
+	//=========================================================================
+	/**
+	 * The constructor.
+	 */
+	public function __construct(cs_phpDB &$db, $logCategory=null, $checkForUpgrades=true) {
+		//assign the database object.
+		$this->db = $db;
+		
+		$this->set_version_file_location(dirname(__FILE__) . '/VERSION');
+		
+		//Make sure the version of cs_phpDB is HIGHER THAN (not equal to) 1.0.0-ALPHA8, 
+		//	which added some methods that are required.
+		$mustBeHigherThan = '1.2-ALPHA8';
+		if(!$this->is_higher_version($mustBeHigherThan, $this->db->get_version())) {
+			throw new exception(__METHOD__ .": requires cs_phpDB of higher than v". $mustBeHigherThan,1);
+		}
+		
+		$this->gfObj = new cs_globalFunctions;
+		
+		//see if there's an upgrade to perform...
+		if($checkForUpgrades === true) {
+			$this->suspendLogging = true;
+			$upgObj = new cs_webdbupgrade(dirname(__FILE__) . '/VERSION', dirname(__FILE__) .'/upgrades/upgrade.xml');
+			$upgObj->check_versions(true);
+			$this->suspendLogging = false;
+			$this->handle_suspended_logs();
+		}
+		
+		//assign the category_id.
+		if(strlen($logCategory)) {
+			if(!is_numeric($logCategory)) {
+				//attempt to retreive the logCategoryId (assuming they passed a name)
+				$this->logCategoryId = $this->get_category_id($logCategory);
+			}
+			else {
+				//it was numeric: set it!
+				$this->logCategoryId = $logCategory;
+			}
+		}
+		
+		//check for a uid in the session.
+		$this->defaultUid = $this->get_uid();
+		
+		
+		//build our cache.
+		$this->build_cache();
+	}//end __construct()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	/**
+	 * Execute the entire contents of the given file (with absolute path) as SQL.
+	 */
+	final public function run_sql_file($filename) {
+		if(!is_object($this->fsObj)) {
+			if(class_exists('cs_fileSystem')) {
+				$fsObj = new cs_fileSystem;
+			}
+			else {
+				throw new exception(__METHOD__ .": required library (cs_fileSystem) not found");
+			}
+		}
+		
+		$this->lastSQLFile = $filename;
+		
+		$fileContents = $fsObj->read($filename);
+		try {
+			$this->db->run_update($fileContents, true);
+			$this->build_cache();
+			$retval = TRUE;
+		}
+		catch(exception $e) {
+			$retval = FALSE;
+		}
+		
+		return($retval);
+	}//end run_sql_file()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	/**
+	 * Build internal cache to avoid extra queries.
+	 */
+	private function build_cache() {
+		//build query, run it, check for errors.
+		$sql = "SELECT class_id, lower(class_name) as name FROM ". $this->tables['class'];
+		
+		try {
+			$data = $this->db->run_query($sql, 'name', 'class_id');
+			
+			if(is_array($data)) {
+				$this->logClassCache = $data;
+			}
+			elseif($data == false) {
+				$this->logClassCache = array();
+			}
+			else {
+				throw new exception(__METHOD__ .": unknown data returned: ". $this->gfObj->debug_var_dump($data,0));
+			}
+		}
+		catch(exception $e) {
+			throw new exception(__METHOD__ .": failed to build internal class cache::: ". $e->getMessage());
+		}
+		
+		//now build cache for attributes.
+		$sql = "SELECT attribute_id, lower(attribute_name) AS attribute_name FROM ". $this->tables['attrib'];
+		
+		try {
+			$data = $this->db->run_query($sql, 'attribute_name', 'attribute_id');
+			
+			if(is_array($data)) {
+				$this->attributeCache = $data;
+			}
+			elseif($data == false) {
+				$this->attributeCache = array();
+			}
+			else {
+				throw new exception(__METHOD__ .": unknown data returned: ". $this->gfObj->debug_var_dump($data,0));
+			}
+		}
+		catch(exception $e) {
+			throw new exception(__METHOD__ .": error occurred while retrieving attribute cache::: ". $e->getMessage());
+		}
+	}//end build_cache()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	/**
+	 * Retrieve log_class_id value from the given name, or insert a new one.
+	 */
+	private function get_class_id($name) {
+		$name = strtolower($name);
+		
+		//get the id.
+		if(isset($this->logClassCache[$name])) {
+			//set the id.
+			$retval = $this->logClassCache[$name];
+		}
+		else {
+			//create the class & then rebuild cache.
+			$retval = $this->create_class($name);
+			$this->build_cache();
+		}
+		
+		return($retval);
+	}//end get_class_id()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	/**
+	 * Retrieve log_event_id based on the given class name & the internal 
+	 * logCategoryId value.
+	 */
+	function get_event_id($logClassName) {
+		$sqlArr = array(
+			'class_id'		=> $this->get_class_id($logClassName),
+			'category_id'	=> $this->logCategoryId
+		);
+		$sql = "SELECT event_id FROM ". $this->tables['event'] ." WHERE " .
+			$this->gfObj->string_from_array($sqlArr, 'select', NULL, 'numeric');
+		
+		try {
+			$data = $this->db->run_query($sql);
+			
+			
+			if($data === false) {
+				//no records & no error: create one.
+				$retval = $this->auto_insert_record($sqlArr['class_id']);
+			}
+			elseif(is_array($data) && isset($data['event_id'])) {
+				$retval = $data['event_id'];
+			}
+			else {
+				throw new exception(__METHOD__ .": invalid data returned::: ". $this->gfObj->debug_var_dump($data,0));
+			}
+		}
+		catch(exception $e) {
+			throw new exception(__METHOD__ .": failed to retrieve event_id::: ". $e->getMessage());
+		}
+		
+		return($retval);
+	}//end get_event_id()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	/**
+	 * The primary means of building log entries: use log_dberror() to log an 
+	 * error with a bit more capabilities; throws the details of the error as  
+	 * an exception.
+	 */
+	public function log_by_class($details, $className="error", $uid=NULL, array $logAttribs=NULL) {
+		
+		if($this->suspendLogging === true) {
+			$this->pendingLogs[] = func_get_args();
+			$retval = count($this->pendingLogs) -1;
+		}
+		else {
+			if(count($this->pendingLogs)) {
+				$this->handle_suspended_logs();
+			}
+			
+			//make sure there's a valid class name.
+			if(!strlen($className) || is_null($className)) {
+				$className = 'error';
+			}
+			
+			//make sure we've got a uid to log under.
+			if(is_null($uid) || !is_numeric($uid)) {
+				//set it.
+				$uid = $this->defaultUid;
+			}
+			
+			//determine the log_event_id.
+			try {
+				$logEventId = $this->get_event_id($className);
+			}
+			catch(Exception $e) {
+				throw new exception(__METHOD__ .": while attempting to retrieve logEventId, encountered an " .
+				"exception:::\n". $e->getMessage() ."\n\nCLASS: $className\nDETAILS: $details");
+			}
+			
+			//check to see what uid to use.
+			$myUid = $this->get_uid();
+			
+			//okay, setup an array of all the data we need.
+			$cleanStringArr = array(
+				'event_id'		=> 'numeric',
+				'uid'			=> 'numeric',
+				'affected_uid'	=> 'numeric',
+				'details'		=> 'sql'
+			);
+			$sqlArr = array (
+				'event_id'	=> $this->gfObj->cleanString($logEventId, 'numeric'),
+				'uid'			=> $myUid,
+				'affected_uid'	=> $uid,
+				'details'		=> $details
+			);
+			
+			//build, run, error-checking.
+			$sql = "INSERT INTO ". $this->tables['log'] ." ". $this->gfObj->string_from_array($sqlArr, 'insert', NULL, $cleanStringArr, TRUE);
+			
+			try {
+				$newId = $this->db->run_insert($sql, $this->seqs['log']);
+				
+				if(is_numeric($newId) && $newId > 0) {
+					$retval = $newId;
+					
+					if(is_array($logAttribs) && count($logAttribs)) {
+						$this->create_log_attributes($newId, $logAttribs);
+					}
+				}
+				else {
+					throw new exception(__METHOD__ .": failed to insert id or invalid return (". $this->gfObj->debug_var_dump($newId,0) .")");
+				}
+			}
+			catch(exception $e) {
+				throw new exception(__METHOD__ .": error while creating log::: ". $e->getMessage());
+			}
+		}
+		
+		return($retval);
+	}//end log_by_class()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	/**
+	 * Logs an error like log_by_class(), but also throws an exception.
+	 */
+	public function log_dberror($details, $uid=NULL, $skipCurrentCatLog=FALSE) {
+		//set the error for the current category.
+		if(!$skipCurrentCatLog && ($this->logCategoryId !== $this->databaseCategory)) {
+			//yep, log it!
+			$this->log_by_class($details, 'error', $uid);
+		}
+		
+		//now log the database error.
+		$originalCategoryId = $this->logCategoryId;
+		$this->logCategoryId = $this->databaseCategory;
+		$retval = $this->log_by_class($details, 'error', $uid);
+		$this->logCategoryId = $originalCategoryId;
+		
+		throw new exception(__METHOD__ .": encountered error::: $details");
+	}//end log_dberror()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	/**
+	 * Attempts to auto-recover if a class was requested that doesn't exist.
+	 */
+	private function auto_insert_record($logClassId) {
+		//generate a default name
+		
+		$className = $this->get_class_name($logClassId);
+		$categoryName = $this->get_category_name($this->logCategoryId);
+		
+		$details = ucwords($categoryName) .": ". ucwords($className);
+		
+		if(strlen($details) <= 4) {
+			//something bad happened (i.e. details="0: 0")
+			throw new exception(__METHOD__ .": failed to recover with class_id=(". $logClassId .") " .
+					"AND category_id=(". $this->logCategoryId ."), details=(". $details .")");
+		}
+		else {
+			//create the sql array.
+			$sqlArr = array (
+				'class_id'		=> $logClassId,
+				'category_id'	=> $this->logCategoryId,
+				'description'		=> "'". $this->gfObj->cleanString($details, 'sql') ."'"
+			);
+			
+			//now run the insert.
+			$sql = 'INSERT INTO '. $this->tables['event'] .' '. $this->gfObj->string_from_array($sqlArr, 'insert');
+			
+			try {
+				$newId = $this->db->run_insert($sql, $this->seqs['event']);
+				
+				if(is_numeric($newId) && $newId > 0) {
+					$retval = $newId;
+				}
+				else {
+					throw new exception(__METHOD__ .": unable to insert id or bad return::: ". $this->gfObj->debug_var_dump($newId,0));
+				}
+			}
+			catch(exception $e) {
+				throw new exception(__METHOD__ .": failed to create record::: ". $e->getMessage());
+			}
+		}
+		
+		return($retval);
+	}//end auto_insert_record()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	/**
+	 * Retrieves logs with the given criteria.
+	 */
+	public function get_logs(array $criteria, array $orderBy=NULL, $limit=20) {
+		//set a default for the limit.
+		if(!is_numeric($limit) || $limit < 1) {
+			//set it again.
+			$limit = 20;
+		}
+		
+		if(is_null($orderBy) || count($orderBy) < 1) {
+			//set it.
+			$orderBy = array(
+				'log_id DESC'
+			);
+		}
+		
+		//set the fields that can be used, along with what alias for the table & cleaning type to use on the data.
+		$allowedCritFields = array(
+			'class_id'			=> array('cl',	'numeric'),
+			'category_id'		=> array('ca',	'numeric'),
+			'uid'				=> array('l',	'numeric'),
+			'affected_uid'		=> array('l',	'numeric'),
+			'creation'			=> array('l',	'sql')
+		);
+		
+		//loop through the data to create our cleaned, prefixed array of criteria.
+		$sqlArr = array();
+		foreach($criteria AS $field => $value) {
+			//is this field in the allowed list?
+			if(isset($allowedCritFields[$field])) {
+				//grab data for this field.
+				$myFieldData = $allowedCritFields[$field];
+				$cleanStringArg = $myFieldData[1];
+				
+				//clean the data.
+				if($field == 'creation' && is_numeric($value)) {
+					$value = $this->gfObj->cleanString($value, 'numeric');
+					$cleanedData = ">= (NOW() - interval '". $value ." hours')";
+				}
+				else {
+					$cleanedData = $this->gfObj->cleanString($value, $cleanStringArg);
+				}
+				
+				//set the prefixed column name.
+				$prefixedName = $myFieldData[0] .'.'. $field;
+				
+				//now add it to our array.
+				$sqlArr[$prefixedName] = $cleanedData;
+			}
+		}
+		
+		
+		//build the criteria.
+		$sqlArr['ca.category_id'] = '>0';
+		$critString = $this->gfObj->string_from_array($sqlArr, 'select');
+		
+		//check if "timeperiod" is in there (it's special)
+		if(isset($criteria['timeperiod']) && isset($criteria['timeperiod']['start']) && isset($criteria['timeperiod']['end'])) {
+			//add it in!
+			$myTime = $criteria['timeperiod'];
+			$addThis = "(l.creation >= '". $myTime['start'] ."'::date AND l.creation <= '". $myTime['end'] ."'::date + interval '1 day')";
+			$critString = create_list($critString, $addThis, ' AND ');
+		}
+		
+		$orderString = $this->gfObj->string_from_array($orderBy, 'limit');
+		$sql = "select " .
+				"l.creation, " .
+				"l.log_id, " .
+				"l.uid, " .
+				"cl.class_name, " .
+				"ca.category_name, " .
+				"ev.description, " .
+				"l.details " .
+			"FROM ". $this->tables['log'] ." AS l " .
+				"INNER JOIN ". $this->tables['event'] ." AS ev ON (l.event_id=ev.event_id) " .
+				"INNER JOIN ". $this->tables['class'] ." AS cl ON (cl.class_id=ev.class_id) " .
+				"INNER JOIN ". $this->tables['category'] ." AS ca ON (ca.category_id=ev.category_id) " .
+			"WHERE " . $critString . " " .
+			"ORDER BY " .
+				"log_id DESC " .
+			"LIMIT ". $limit;
+		
+		try {
+			//run it.
+			$data = $this->db->run_query($sql, 'log_id');
+			
+			$retval = array();
+			if(is_array($data)) {
+				$retval = $data;
+			}
+		}
+		catch(exception $e) {
+			throw new exception(__METHOD__ .": failed to retrieve logs::: ". $e->getMessage());
+		}
+		
+		return($retval);
+	}//end get_logs()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	/**
+	 * Uses arbitrary criteria to retrieve the last X log entries.
+	 */
+	public function get_recent_logs($numEntries=null) {
+		if(!is_numeric($numEntries) || $numEntries < 1) {
+			$numEntries = 20;
+		}
+		
+		//set the criteria so we only get the last few entries.
+		$retval = $this->get_logs(array(), NULL, $numEntries);
+		return($retval);
+	}//end get_recent_logs()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	/**
+	 * Retrieve category_id from the given name.
+	 */
+	private function get_category_id($catName) {
+		if(strlen($catName) && is_string($catName)) {
+			$catName = trim($catName);
+			$sql = "SELECT category_id FROM ". $this->tables['category'] ." WHERE lower(category_name) = '". strtolower($catName) ."'";
+			
+			try {
+				
+				$data = $this->db->run_query($sql);
+				
+				$numrows = $this->db->numRows();
+				if($numrows == 1 && is_array($data) && isset($data['category_id']) && is_numeric($data['category_id'])) {
+					$retval = $data['category_id'];
+				}
+				elseif($data === false) {
+					$retval = $this->create_log_category($catName);
+				}
+				elseif($numrows > 1) {
+					throw new exception(__METHOD__ .": found too many records (". $numrows .")");
+				}
+				else {
+					throw new exception(__METHOD__ .": unknown error (bad data in array?)");
+				}
+			}
+			catch(exception $e) {
+				if($this->setupComplete === true) {
+					throw new exception(__METHOD__ .": encountered error::: ". $e->getMessage());
+				}
+				else {
+					$mySchemaFile = dirname(__FILE__) .'/setup/schema.'. $this->db->get_dbtype() .'.sql';
+					if(file_exists($mySchemaFile)) {
+						$this->setupComplete = true;
+						$this->run_sql_file($mySchemaFile);
+						
+						//Create the default category.
+						$this->create_log_category('Database');
+						
+						$retval = $this->create_log_category($catName);
+					}
+					else {
+						throw new exception(__METHOD__ .": missing schema file (". $mySchemaFile ."), can't run setup");
+					}
+				}
+			}
+		}
+		else {
+			throw new exception(__METHOD__ .": category name (". $catName .") is invalid");
+		}
+		
+		return($retval);
+	}//end get_category_id()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	/**
+	 * Create a category_id based on the given name.
+	 */
+	private function create_log_category($catName) {
+		$sql = "INSERT INTO ". $this->tables['category'] ." (category_name) VALUES ('". 
+			$this->gfObj->cleanString($catName, 'sql') ."')";
+		
+		try {
+			$newId = $this->db->run_insert($sql, $this->seqs['category']);
+			
+			if(is_numeric($newId) && $newId > 0) {
+				$retval = $newId;
+			}
+			else {
+				throw new exception(__METHOD__ .": invalid data returned for " .
+						"category::: ". $this->gfObj->debug_var_dump($newId,0));
+			}
+		}
+		catch(exception $e) {
+			throw new exception(__METHOD__ .": error encountered while trying to " .
+					"create category::: ". $e->getMessage());
+		}
+		
+		return($retval);
+	}//end create_log_category()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	/**
+	 * Create a log_class_id based on the given name.
+	 */
+	private function create_class($className) {
+		$sql = "INSERT INTO ". $this->tables['class'] ." (class_name) VALUES ('". 
+			$this->gfObj->cleanString($className, 'sql') ."')";
+		
+		
+		try {
+			$newId = $this->db->run_insert($sql, $this->seqs['class']);
+			
+			if(is_numeric($newId) && $newId > 0) {
+				$retval = $newId;
+			}
+			else {
+				throw new exception(__METHOD__ .": failed to insert class or invalid " .
+						"id::: ". $this->gfObj->debug_var_dump($newId,0));
+			}
+		}
+		catch(exception $e) {
+			throw new exception(__METHOD__ .": error encountered while creating log " .
+					"class::: ". $e->getMessage());
+		}
+		
+		return($retval);
+	}//end create_class()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	/**
+	 * Retrieve class name from the given id.
+	 */
+	private function get_class_name($classId) {
+		if(is_numeric($classId)) {
+			$sql = "SELECT class_name FROM ". $this->tables['class'] ." WHERE class_id=". $classId;
+			
+			try {
+				$data = $this->db->run_query($sql);
+				
+				if(is_array($data) && isset($data['class_name']) && $this->db->numRows() == 1) {
+					$className = $data['class_name'];
+				}
+				else {
+					throw new exception(__METHOD__ .": failed to retrieve class " .
+							"name, or invalid return data::: ". $this->gfObj->debug_print($data,0));
+				}
+			}
+			catch(exception $e) {
+				throw new exception(__METHOD__ .": error encountered while " .
+						"retrieving class name::: ". $e->getMessage());
+			}
+			
+		}
+		else {
+			throw new exception(__METHOD__ .": invalid class ID (". $classId .")");
+		}
+		
+		return($className);
+	}//end get_class_name()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	/**
+	 * Retrieve category name from the given ID.
+	 */
+	private function get_category_name($categoryId) {
+		if(is_numeric($categoryId)) {
+			$sql = "SELECT category_name FROM ". $this->tables['category'] ." WHERE category_id=". $categoryId;
+			
+			try {
+				$data = $this->db->run_query($sql);
+				
+				if(is_array($data) && isset($data['category_name']) && $this->db->numRows() == 1) {
+					$categoryName = $data['category_name'];
+				}
+				else {
+					throw new exception(__METHOD__ .": failed to retrieve " .
+							"category name::: ". $this->gfObj->debug_var_dump($data,0));
+				}
+			}
+			catch(exception $e) {
+				throw new exception(__METHOD__ .": error encountered while " .
+						"retrieving category name::: ". $e->getMessage());
+			}
+		}
+		else {
+			throw new exception(__METHOD__ .": invalid category ID (". $categoryId .")");
+		}
+		
+		return($categoryName);
+	}//end get_category_name()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	public function __get($var) {
+		return($this->$var);
+	}//end __get()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	public function __set($var, $newVal) {
+		$res = false;
+		switch($var) {
+			case 'suspendLogging':
+				$this->$var = $newVal;
+				if($newVal === false) {
+					#$this->handle_suspended_logs();
+				}
+				$res = true;
+				break;
+			
+			case 'logCategory':
+			case 'logCategoryId':
+				$this->logCategoryId = $this->get_category_id($newVal);
+				$res = true;
+				break;
+		}
+		return($res);
+	}//end __set()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	public function handle_suspended_logs() {
+		$retval = 0;
+		$debugThis = array();
+		if($this->suspendLogging === false && count($this->pendingLogs)) {
+			$myLogs = $this->pendingLogs;
+			$this->build_cache();
+			$this->pendingLogs = array();
+			foreach($myLogs as $i=>$args) {
+				//this is potentially deadly: call self recursively to log the items prevously suspended.
+				$newId = call_user_func_array(array($this, 'log_by_class'), $args);
+				
+				$debugThis[$newId] = $args;
+				$retval++;
+			}
+		}
+		return($retval);
+	}//end handle_suspended_logs()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	public function get_uid() {
+		$myUid = $this->defaultUid;
+		//check for a uid in the session.
+		if(is_array($_SESSION) && isset($_SESSION['uid']) && is_numeric($_SESSION['uid'])) {
+			//got an ID in the session.
+			$myUid = $_SESSION['uid'];
+		}
+		return($myUid);
+	}//end get_uid()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	private function create_attribute($attribName, $buildCache=true) {
+		
+		$myId = null;
+		if(isset($this->attributeCache[strtolower($attribName)])) {
+			$myId = $this->attributeCache[strtolower($attribName)];
+		}
+		else {
+			$sql = "INSERT INTO ". $this->tables['attrib'] ." (attribute_name) " .
+					"VALUES ('". $this->gfObj->cleanString($attribName, 'sql_insert') ."')";
+			
+			try {
+				$myId = $this->db->run_insert($sql, $this->seqs['attrib']);
+			}
+			catch(exception $e) {
+				throw new exception(__METHOD__ .": fatal error while creating attribute (". $attribName .")::: ". $e->getMessage());
+			}
+		}
+		
+		if($buildCache) {
+			$this->build_cache();
+		}
+		
+		return($myId);
+	}//end create_attribute()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	private function create_log_attributes($logId, array $attribs) {
+		$myIds = array();
+		foreach($attribs as $name=>$val) {
+			$insertData = array(
+				'log_id'		=> $logId,
+				'attribute_id'	=> $this->create_attribute($name, false),
+				'value_text'	=> $val
+			);
+			$sql = "INSERT INTO ". $this->tables['logAttrib'] ." ". 
+					$this->gfObj->string_from_array($insertData, 'insert');
+			
+			try {
+				$myIds[$name][] = $this->db->run_insert($sql, $this->seqs['logAttrib']);
+			}
+			catch(exception $e) {
+				throw new exception(__METHOD__ .": fatal error while creating log attribute " .
+						"(". $name .")::: ". $e->getMessage());
+			}
+		}
+		$this->build_cache();
+		
+	}//end create_log_attributes()
+	//=========================================================================
+	
+	
+}//end logsClass{}
+?>
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
 | 
| 
      
      
      From: <cra...@us...> - 2009-08-19 15:27:47
      
     | 
| Revision: 103
          http://cs-webapplibs.svn.sourceforge.net/cs-webapplibs/?rev=103&view=rev
Author:   crazedsanity
Date:     2009-08-19 15:27:38 +0000 (Wed, 19 Aug 2009)
Log Message:
-----------
Import of cs-versionparse.
Added Paths:
-----------
    trunk/0.3/CREDITS
    trunk/0.3/LICENSE
    trunk/0.3/VERSION
    trunk/0.3/cs_version.abstract.class.php
Copied: trunk/0.3/CREDITS (from rev 101, import/cs-versionparse/trunk/0.1/CREDITS)
===================================================================
--- trunk/0.3/CREDITS	                        (rev 0)
+++ trunk/0.3/CREDITS	2009-08-19 15:27:38 UTC (rev 103)
@@ -0,0 +1,3 @@
+
+Lead Developer: Dan Falconer (cra...@us...)
+
Copied: trunk/0.3/LICENSE (from rev 101, import/cs-versionparse/trunk/0.1/LICENSE)
===================================================================
--- trunk/0.3/LICENSE	                        (rev 0)
+++ trunk/0.3/LICENSE	2009-08-19 15:27:38 UTC (rev 103)
@@ -0,0 +1,291 @@
+NOTE: a full HTML version of this license can be found at: 
+http://www.opensource.org/licenses/gpl-license.php
+
+It has been reproduced below without any HTML.
+==========================================================================
+
+The GNU General Public License (GPL)
+
+Version 2, June 1991
+
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+		Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
Copied: trunk/0.3/VERSION (from rev 101, import/cs-versionparse/trunk/0.1/VERSION)
===================================================================
--- trunk/0.3/VERSION	                        (rev 0)
+++ trunk/0.3/VERSION	2009-08-19 15:27:38 UTC (rev 103)
@@ -0,0 +1,6 @@
+## Stores the current version of the cs-versionparse system, and it's source.  
+## Please do NOT modify this file.
+
+VERSION: 0.3.0
+PROJECT: cs-webapplibs
+$HeadURL$
\ No newline at end of file
Copied: trunk/0.3/cs_version.abstract.class.php (from rev 101, import/cs-versionparse/trunk/0.1/cs_version.abstract.class.php)
===================================================================
--- trunk/0.3/cs_version.abstract.class.php	                        (rev 0)
+++ trunk/0.3/cs_version.abstract.class.php	2009-08-19 15:27:38 UTC (rev 103)
@@ -0,0 +1,398 @@
+<?php
+/*
+ * Created on January 01, 2009 by Dan Falconer
+ * 
+ * SVN INFORMATION:::
+ * -------------------
+ * Last Author::::::::: $Author$ 
+ * Current Revision:::: $Revision$ 
+ * Repository Location: $HeadURL$ 
+ * Last Updated:::::::: $Date$
+ */
+
+abstract class cs_versionAbstract {
+	
+	public $isTest = FALSE;
+	
+	
+	
+	private $versionFileLocation=null;
+	private $fullVersionString;
+	private $suffixList = array(
+		'ALPHA', 	//very unstable
+		'BETA', 	//kinda unstable, but probably useable
+		'RC'		//all known bugs fixed, searching for unknown ones
+	);
+	
+	
+	
+	abstract public function __construct();
+	
+	
+	
+	//=========================================================================
+	/**
+	 * Retrieve our version string from the VERSION file.
+	 */
+	final public function get_version($asArray=false) {
+		$retval = NULL;
+		
+		$this->auto_set_version_file();
+		
+		if(file_exists($this->versionFileLocation)) {
+			$myMatches = array();
+			$findIt = preg_match('/VERSION: (.+)/', file_get_contents($this->versionFileLocation), $matches);
+			
+			if($findIt == 1 && count($matches) == 2) {
+				$fullVersionString = $matches[1];
+				$versionInfo = $this->parse_version_string($fullVersionString);
+				$this->fullVersionString = $this->build_full_version_string($versionInfo);
+				
+				
+				if($asArray) {
+					$retval = $versionInfo;
+					$retval['version_string'] = $this->fullVersionString;
+				}
+				else {
+					$retval = $this->build_full_version_string($versionInfo);
+				}
+			}
+			else {
+				throw new exception(__METHOD__ .": failed to retrieve version string in file " .
+						"(". $this->versionFileLocation .")");
+			}
+		}
+		else {
+			throw new exception(__METHOD__ .": failed to retrieve version information, file " .
+					"(". $this->versionFileLocation .") does not exist or was not set");
+		}
+		
+		return($retval);
+	}//end get_version()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	public function __get($var) {
+		return($this->$var);
+	}//end __get()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	final public function get_project() {
+		$retval = NULL;
+		$this->auto_set_version_file();
+		if(file_exists($this->versionFileLocation)) {
+			$myMatches = array();
+			$findIt = preg_match('/PROJECT: (.+)/', file_get_contents($this->versionFileLocation), $matches);
+			
+			if($findIt == 1 && count($matches) == 2 && strlen($matches[1])) {
+				$retval = $matches[1];
+			}
+			else {
+				throw new exception(__METHOD__ .": failed to retrieve project string");
+			}
+		}
+		else {
+			throw new exception(__METHOD__ .": failed to retrieve project information");
+		}
+		
+		return($retval);
+	}//end get_project()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	public function set_version_file_location($location) {
+		if(file_exists($location)) {
+			$this->versionFileLocation = $location;
+		}
+		else {
+			throw new exception(__METHOD__ .": invalid location of VERSION file (". $location .")");
+		}
+	}//end set_version_file_location()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	protected function auto_set_version_file() {
+		if(!strlen($this->versionFileLocation)) {
+			$bt = debug_backtrace();
+			foreach($bt as $callNum=>$data) {
+				if(strlen($data['class'])) {
+					if($data['class'] != __CLASS__) {
+						$dir = dirname($data['file']);
+						if(preg_match('/tests$/', $dir)) {
+							$dir = preg_replace('/\/tests$/', '', $dir);
+						}
+						elseif(preg_match('/test$/', $dir)) {
+							$dir = preg_replace('/\/test$/', '', $dir);
+						}
+						break;
+					}
+				}
+				else {
+					throw new exception(__METHOD__ .": failed to locate the calling class in backtrace");
+				}
+			}
+			
+			if(file_exists($dir .'/VERSION')) {
+				$this->set_version_file_location($dir .'/VERSION');
+			}
+			else {
+				throw new exception(__METHOD__ .": failed to automatically set version file (tried ". $dir ."/VERSION)");
+			}
+		}
+	}//end auto_set_version_file()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	/**
+	 * 
+	 * TODO: add logic to split apart the suffix (i.e. "-ALPHA5" broken into "ALPHA" and "5").
+	 */
+	public function parse_version_string($version) {
+		if(is_string($version) && strlen($version) && preg_match('/\./', $version)) {
+			$version = preg_replace('/ /', '', $version);
+			
+			$pieces = explode('.', $version);
+			$retval = array(
+				'version_major'			=> $pieces[0],
+				'version_minor'			=> $pieces[1]
+			);
+			if(isset($pieces[2]) && strlen($pieces[2])) {
+				$retval['version_maintenance'] = $pieces[2];
+			}
+			else {
+				$retval['version_maintenance'] = 0;
+			}
+			
+			if(preg_match('/-/', $retval['version_maintenance'])) {
+				$bits = explode('-', $retval['version_maintenance']);
+				$retval['version_maintenance'] = $bits[0];
+				$suffix = $bits[1];
+			}
+			elseif(preg_match('/-/', $retval['version_minor'])) {
+				$bits = explode('-', $retval['version_minor']);
+				$retval['version_minor'] = $bits[0];
+				$suffix = $bits[1];
+			}
+			else {
+				$suffix = "";
+			}
+			$retval['version_suffix'] = $suffix;
+		}
+		else {
+			throw new exception(__METHOD__ .": invalid version string passed (". $version .")");
+		}
+		
+		return($retval);
+	}//end parse_version_string()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	public function build_full_version_string(array $versionInfo) {
+		$requiredIndexes = array(
+			'version_major', 'version_minor', 'version_maintenance', 'version_suffix'
+		);
+		
+		$missing="";
+		$count=0;
+		foreach($requiredIndexes as $indexName) {
+			if(isset($versionInfo[$indexName])) {
+				$count++;
+			}
+			else {
+				if(strlen($missing)) {
+					$missing .= ", ". $indexName;
+				}
+				else {
+					$missing = $indexName;
+				}
+			}
+		}
+		
+		if($count == count($requiredIndexes) && !strlen($missing)) {
+			$suffix = $versionInfo['version_suffix'];
+			unset($versionInfo['version_suffix']);
+			
+			$retval = "";
+			foreach($versionInfo as $name=>$value) {
+				if(strlen($retval)) {
+					$retval .= ".". $value;
+				}
+				else {
+					$retval = $value;
+				}
+			}
+			if(strlen($suffix)) {
+				$retval .= "-". $suffix;
+			}
+		}
+		else {
+			throw new exception(__METHOD__ .": missing indexes in given array (". $missing .")");
+		}
+		
+		return($retval);
+		
+	}//end build_full_version_string()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	public function is_higher_version($version, $checkIfHigher) {
+		$retval = FALSE;
+		$this->gfObj = new cs_globalFunctions;
+		if(!is_string($version) || !is_string($checkIfHigher)) {
+			throw new exception(__METHOD__ .": no valid version strings, version=(". $version ."), checkIfHigher=(". $checkIfHigher .")");
+		}
+		elseif($version == $checkIfHigher) {
+			$retval = FALSE;
+		}
+		else {
+			$curVersionArr = $this->parse_version_string($version);
+			$checkVersionArr = $this->parse_version_string($checkIfHigher);
+			
+			unset($curVersionArr['version_string'], $checkVersionArr['version_string']);
+			
+			
+			$curVersionSuffix = $curVersionArr['version_suffix'];
+			$checkVersionSuffix = $checkVersionArr['version_suffix'];
+			
+			
+			unset($curVersionArr['version_suffix']);
+			
+			foreach($curVersionArr as $index=>$versionNumber) {
+				$checkThis = $checkVersionArr[$index];
+				
+				if(is_numeric($checkThis) && is_numeric($versionNumber)) {
+					//set them as integers.
+					settype($versionNumber, 'int');
+					settype($checkThis, 'int');
+					
+					if($checkThis > $versionNumber) {
+						$retval = TRUE;
+						break;
+					}
+					elseif($checkThis == $versionNumber) {
+						//they're equal...
+					}
+					else {
+						//TODO: should there maybe be an option to throw an exception (freak out) here?
+					}
+				}
+				else {
+					throw new exception(__METHOD__ .": ". $index ." is not numeric in one of the strings " .
+						"(versionNumber=". $versionNumber .", checkThis=". $checkThis .")");
+				}
+			}
+			
+			//now deal with those damnable suffixes, but only if the versions are so far identical: if 
+			//	the "$checkIfHigher" is actually higher, don't bother (i.e. suffixes don't matter when
+			//	we already know there's a major, minor, or maintenance version that's also higher.
+			if($retval === FALSE) {
+				//EXAMPLE: $version="1.0.0-BETA3", $checkIfHigher="1.1.0"
+				// Moving from a non-suffixed version to a suffixed version isn't supported, but the inverse is:
+				//		i.e. (1.0.0-BETA3 to 1.0.0) is okay, but (1.0.0 to 1.0.0-BETA3) is NOT.
+				//		Also: (1.0.0-BETA3 to 1.0.0-BETA4) is okay, but (1.0.0-BETA4 to 1.0.0-BETA3) is NOT.
+				if(strlen($curVersionSuffix) && strlen($checkVersionSuffix) && $curVersionSuffix == $checkVersionSuffix) {
+					//matching suffixes.
+				}
+				elseif(strlen($curVersionSuffix) || strlen($checkVersionSuffix)) {
+					//we know the suffixes are there and DO match.
+					if(strlen($curVersionSuffix) && strlen($checkVersionSuffix)) {
+						//okay, here's where we do some crazy things...
+						$curVersionData = $this->parse_suffix($curVersionSuffix);
+						$checkVersionData = $this->parse_suffix($checkVersionSuffix);
+						
+						if($curVersionData['type'] == $checkVersionData['type']) {
+							//got the same suffix type (like "BETA"), check the number.
+							if($checkVersionData['number'] > $curVersionData['number']) {
+								//new version's suffix number higher than current...
+								$retval = TRUE;
+							}
+							elseif($checkVersionData['number'] == $curVersionData['number']) {
+								//new version's suffix number is EQUAL TO current...
+								$retval = FALSE;
+							}
+							else {
+								//new version's suffix number is LESS THAN current...
+								$retval = FALSE;
+							}
+						}
+						else {
+							//not the same suffix... see if the new one is higher.
+							$suffixValues = array_flip($this->suffixList);
+							if($suffixValues[$checkVersionData['type']] > $suffixValues[$curVersionData['type']]) {
+								$retval = TRUE;
+							}
+							else {
+								//current suffix type is higher...
+							}
+						}
+						
+					}
+					elseif(strlen($curVersionSuffix) && !strlen($checkVersionSuffix)) {
+						//i.e. "1.0.0-BETA1" to "1.0.0" --->>> OKAY!
+						$retval = TRUE;
+					}
+					elseif(!strlen($curVersionSuffix) && strlen($checkVersionSuffix)) {
+						//i.e. "1.0.0" to "1.0.0-BETA1" --->>> NOT ACCEPTABLE!
+					}
+				}
+				else {
+					//no suffix to care about
+				}
+			}
+		}
+		
+		return($retval);
+		
+	}//end is_higher_version()
+	//=========================================================================
+	
+	
+	
+	//=========================================================================
+	protected function parse_suffix($suffix) {
+		$retval = NULL;
+		if(strlen($suffix)) {
+			//determine what kind it is.
+			foreach($this->suffixList as $type) {
+				if(preg_match('/^'. $type .'/', $suffix)) {
+					$checkThis = preg_replace('/^'. $type .'/', '', $suffix);
+					if(strlen($checkThis) && is_numeric($checkThis)) {
+						//oooh... it's something like "BETA3"
+						$retval = array(
+							'type'		=> $type,
+							'number'	=> $checkThis
+						);
+					}
+					else {
+						throw new exception(__METHOD__ .": invalid suffix (". $suffix .")");
+					}
+					break;
+				}
+			}
+		}
+		else {
+			throw new exception(__METHOD__ .": invalid suffix (". $suffix .")");
+		}
+		
+		return($retval);
+	}//end parse_suffix()
+	//=========================================================================
+	
+	
+}
+?>
\ No newline at end of file
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
 | 
| 
      
      
      From: <cra...@us...> - 2009-08-19 15:25:44
      
     | 
| Revision: 102
          http://cs-webapplibs.svn.sourceforge.net/cs-webapplibs/?rev=102&view=rev
Author:   crazedsanity
Date:     2009-08-19 15:25:32 +0000 (Wed, 19 Aug 2009)
Log Message:
-----------
Trunk + 0.3 folder, for bleeding-edge development.
NOTE::: starting @ 0.3 instead of 0.1 to avoid overlap from the 3 existing libs, which were at max of 0.2.x...
Added Paths:
-----------
    trunk/
    trunk/0.3/
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
 | 
| 
      
      
      From: <cra...@us...> - 2009-08-19 15:18:52
      
     | 
| Revision: 2
          http://cs-webapplibs.svn.sourceforge.net/cs-webapplibs/?rev=2&view=rev
Author:   crazedsanity
Date:     2009-08-19 15:18:36 +0000 (Wed, 19 Aug 2009)
Log Message:
-----------
Initial layout for importing the other libraries.
Added Paths:
-----------
    import/cs-versionparse/
    import/cs-webdblogger/
    import/cs-webdbupgrade/
Property Changed:
----------------
    import/
Property changes on: import
___________________________________________________________________
Added: svn:ignore
   + .project
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
 | 
| 
      
      
      From: <cra...@us...> - 2009-08-19 15:08:57
      
     | 
| Revision: 1
          http://cs-webapplibs.svn.sourceforge.net/cs-webapplibs/?rev=1&view=rev
Author:   crazedsanity
Date:     2009-08-19 15:08:50 +0000 (Wed, 19 Aug 2009)
Log Message:
-----------
Added Paths:
-----------
    import/
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
 |