[Cs-webdbupgrade-commits] SF.net SVN: cs-webdbupgrade:[6] trunk/0.1
Status: Inactive
Brought to you by:
crazedsanity
|
From: <cra...@us...> - 2009-01-07 18:20:20
|
Revision: 6
http://cs-webdbupgrade.svn.sourceforge.net/cs-webdbupgrade/?rev=6&view=rev
Author: crazedsanity
Date: 2009-01-07 18:20:14 +0000 (Wed, 07 Jan 2009)
Log Message:
-----------
Current version of the upgrade class. Example upgrade scripts & config pending.
Added Paths:
-----------
trunk/0.1/CREDITS
trunk/0.1/LICENSE
trunk/0.1/VERSION
trunk/0.1/upgradeClass.php
Added: trunk/0.1/CREDITS
===================================================================
--- trunk/0.1/CREDITS (rev 0)
+++ trunk/0.1/CREDITS 2009-01-07 18:20:14 UTC (rev 6)
@@ -0,0 +1,3 @@
+
+Lead Developer: Dan Falconer (cra...@us...)
+
Added: trunk/0.1/LICENSE
===================================================================
--- trunk/0.1/LICENSE (rev 0)
+++ trunk/0.1/LICENSE 2009-01-07 18:20:14 UTC (rev 6)
@@ -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
Added: trunk/0.1/VERSION
===================================================================
--- trunk/0.1/VERSION (rev 0)
+++ trunk/0.1/VERSION 2009-01-07 18:20:14 UTC (rev 6)
@@ -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.1
+PROJECT: cs-webdbupgrade
+$HeadURL$
\ No newline at end of file
Property changes on: trunk/0.1/VERSION
___________________________________________________________________
Added: svn:keywords
+ HeadURL
Added: trunk/0.1/upgradeClass.php
===================================================================
--- trunk/0.1/upgradeClass.php (rev 0)
+++ trunk/0.1/upgradeClass.php 2009-01-07 18:20:14 UTC (rev 6)
@@ -0,0 +1,902 @@
+<?php
+/*
+ * Created on Jul 2, 2007
+ *
+ * SVN INFORMATION:::
+ * ------------------
+ * SVN Signature::::::: $Id: upgradeClass.php 894 2008-05-16 03:26:15Z crazedsanity $
+ * Last Author::::::::: $Author: crazedsanity $
+ * Current Revision:::: $Revision: 894 $
+ * Repository Location: $HeadURL: https://cs-project.svn.sourceforge.net/svnroot/cs-project/trunk/1.2/lib/upgradeClass.php $
+ * Last Updated:::::::: $Date: 2008-05-15 22:26:15 -0500 (Thu, 15 May 2008) $
+ *
+ */
+
+class upgrade {
+
+ private $fsObj;
+ private $gfObj;
+ private $config = NULL;
+ protected $db;
+ protected $logsObj;
+
+ private $versionFileVersion = NULL;
+ private $configVersion = NULL;
+ private $databaseVersion = NULL;
+
+ private $mainConfig = NULL;
+
+ /** 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() {
+ $this->fsObj = new cs_fileSystemClass(dirname(__FILE__) .'/../');
+ $this->gfObj = new cs_globalFunctions;
+ $this->gfObj->debugPrintOpt = DEBUGPRINTOPT;
+ clearstatcache();
+
+ $this->db = new cs_phpDB;
+ $this->db->connect(get_config_db_params());
+
+ $this->logsObj = new logsClass($this->db, "Upgrade");
+
+ //define some things for upgrades.
+ define("UPGRADE_LOCKFILE", dirname(__FILE__) ."/../UPGRADING_VERSION"); //relative to the directory beneath lib.
+ define("UPGRADE_DIR", dirname(__FILE__) ."/../upgrade");
+ }//end __construct()
+ //=========================================================================
+
+
+
+ //=========================================================================
+ /**
+ * 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) {
+ //first, check that all files exist.
+ $retval = NULL;
+
+ //check to see if the lock files for upgrading exist.
+ if($this->upgrade_in_progress()) {
+ throw new exception(__METHOD__ .": upgrade in progress");
+ }
+ elseif(!file_exists(CONFIG_FILE_LOCATION)) {
+ throw new exception(__METHOD__ .": config.xml file missing");
+ }
+ elseif(!file_exists(dirname(__FILE__) .'/../VERSION')) {
+ throw new exception(__METHOD__ .": VERSION file missing");
+ }
+ elseif(!file_exists(dirname(__FILE__) .'/../upgrade/upgrade.xml')) {
+ throw new exception(__METHOD__ .": upgrade.xml file missing");
+ }
+ else {
+ //okay, all files present: check the version in the VERSION file.
+ $versionFileContents = $this->read_version_file();
+
+ //now read data from the config.
+ $versionFromConfig = $this->read_config_version();
+
+ $versionsDiffer = TRUE;
+ $retval = FALSE;
+ if($versionFileContents == $versionFromConfig) {
+ $versionConflict = $this->check_for_version_conflict();
+ if($versionConflict === 0) {
+ //all is good: no problems detected (all things match-up).
+ $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('VERSION');
+
+ //okay, rip it into bits. NOTE: this *depends* on "VERSION: " being on the third line.
+ $lines = explode("\n", $versionFileContents);
+ $versionLine = $lines[2];
+ if(preg_match('/^VERSION: /', $versionLine)) {
+
+ $retval = trim(preg_replace('/VERSION: /', '', $versionLine));
+ $this->versionFileVersion = $retval;
+ }
+ else {
+ throw new exception(__METHOD__ .": could not find VERSION data");
+ }
+
+ return($retval);
+ }//end read_version_file()
+ //=========================================================================
+
+
+
+ //=========================================================================
+ private function read_config_version() {
+ $configObj = new config();
+ $config = $configObj->read_config_file(FALSE);
+ $this->mainConfig = $config;
+ $retval = NULL;
+
+ if(!is_array($config) || !count($config)) {
+ throw new exception(__METHOD__ .": no configuration data available (missing config file?)");
+ }
+ else {
+ //now, let's see if there's a "version_string" index.
+ if(isset($config['VERSION_STRING']) && strlen($config['VERSION_STRING'])) {
+ $retval = $config['VERSION_STRING'];
+ }
+ else {
+ throw new exception(__METHOD__ .": invalid version string found (". $config['VERSION_STRING'] .")");
+ }
+ }
+
+ $this->configVersion = $retval;
+ return($retval);
+ }//end read_config_version()
+ //=========================================================================
+
+
+
+ //=========================================================================
+ /**
+ * Read information from our config file, so we know what to expect.
+ */
+ private function read_upgrade_config_file() {
+ $xmlString = $this->fsObj->read("upgrade/upgrade.xml");
+
+ //parse the file.
+ $xmlParser = new xmlParser($xmlString);
+
+ $config = $xmlParser->get_tree(TRUE);
+ $this->config = $config['UPGRADE'];
+ }//end read_upgrade_config_file()
+ //=========================================================================
+
+
+
+ //=========================================================================
+ private function perform_upgrade() {
+ //make sure there's not already a lockfile.
+ if($this->upgrade_in_progress()) {
+ //ew. Can't upgrade.
+ throw new exception(__METHOD__ .": upgrade already in progress...????");
+ }
+ else {
+ $lockConfig = $this->upgrade_in_progress(TRUE);
+ $this->fsObj->cd("/");
+
+ //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.
+ throw new exception(__METHOD__ .": failed to set 'upgrade in progress'");
+ }
+ else {
+ $this->gfObj->debug_print(__METHOD__ .": result of setting 'upgrade in progress': (". $lockConfig .")");
+
+ //check to see if our config file is writable.
+ if(!$this->fsObj->is_writable(CONFIG_FILE_LOCATION)) {
+ throw new exception(__METHOD__ .": config file isn't writable!");
+ }
+
+ //push data into our internal "config" array.
+ $this->read_upgrade_config_file();
+ $this->get_database_version();
+
+ //check for version conflicts.
+ $this->check_for_version_conflict();
+
+ $upgradeList = $this->get_upgrade_list();
+
+ $i=0;
+ $this->gfObj->debug_print(__METHOD__ .": starting to run through the upgrade list, starting at (". $this->databaseVersion .")...");
+ $this->db->beginTrans(__METHOD__);
+ foreach($upgradeList as $fromVersion=>$toVersion) {
+
+ $details = __METHOD__ .": upgrading from ". $fromVersion ." to ". $toVersion ."... ";
+ $this->gfObj->debug_print($details);
+ $this->logsObj->log_by_class($details, 'system');
+ $this->do_single_upgrade($fromVersion);
+ $this->get_database_version();
+ $i++;
+
+ $details = __METHOD__ .": finished upgrade #". $i .", now at version (". $this->databaseVersion .")";
+ $this->gfObj->debug_print($details);
+ $this->logsObj->log_by_class($details, 'system');
+ }
+
+ if($this->databaseVersion == $this->versionFileVersion) {
+ $this->gfObj->debug_print(__METHOD__ .": finished upgrading after performing (". $i .") upgrades!!!");
+ $this->newVersion = $this->databaseVersion;
+ }
+ else {
+ throw new exception(__METHOD__ .": finished upgrade, but version wasn't updated (expecting '". $this->versionFileVersion ."', got '". $this->databaseVersion ."')!!!");
+ }
+ $this->update_config_file('version_string', $this->newVersion);
+ $this->update_config_file('workingonit', "0");
+
+ $this->db->commitTrans();
+ }
+ }
+ }//end perform_upgrade()
+ //=========================================================================
+
+
+
+ //=========================================================================
+ public function upgrade_in_progress($makeItSo=FALSE) {
+ $retval = FALSE;
+ if($makeItSo === TRUE) {
+ $this->get_database_version();
+ $details = 'Upgrade from '. $this->databaseVersion .' started at '. date('Y-m-d H:i:s');
+ $this->update_config_file('WORKINGONIT', $details);
+ $retval = TRUE;
+ }
+ elseif(preg_match('/^upgrade/i', $this->mainConfig['WORKINGONIT'])) {
+ $retval = TRUE;
+ }
+
+ return($retval);
+ }//end upgrade_in_progress()
+ //=========================================================================
+
+
+
+ //=========================================================================
+ public function parse_version_string($versionString) {
+ if(is_null($versionString) || !strlen($versionString)) {
+ throw new exception(__METHOD__ .": invalid version string ($versionString)");
+ }
+ $tmp = explode('.', $versionString);
+
+ //NOTE: the order of the array MUST be major, then minor, then maintenance, so is_higher_version() can check it easily.
+ $retval = array(
+ 'version_string' => $versionString,
+ 'version_major' => $tmp[0],
+ 'version_minor' => $tmp[1]
+ );
+ if(count($tmp) == 3) {
+ $retval['version_maintenance'] = $tmp[2];
+ }
+ else {
+ $retval['version_maintenance'] = "0";
+ }
+
+ //check for a prefix or a suffix.
+ if(preg_match('/-/', $versionString)) {
+ //make sure there's only ONE dash.
+ $tmp = explode('-', $versionString);
+ if(count($tmp) == 2) {
+ if(preg_match('/-/', $retval['version_major'])) {
+ //example: BETA-3.3.0
+
+ throw new exception(__METHOD__ .": versions that contain prefixes cannot be upgraded");
+
+ #$tmp = explode('-', $retval['version_major']);
+ #$retval['version_major'] = $tmp[1];
+ #$retval['prefix'] = $tmp[0];
+ }
+ elseif(preg_match('/-/', $retval['version_maintenance'])) {
+ //example: 1.0.0-ALPHA1
+ $tmp = explode('-', $retval['version_maintenance']);
+ $retval['version_maintenance'] = $tmp[0];
+ $retval['version_suffix'] = $tmp[1];
+ }
+ else {
+ throw new exception(__METHOD__ .": invalid location of prefix/suffix in (". $versionString .")");
+ }
+ }
+ else {
+ throw new exception(__METHOD__ .": too many dashes in version string (". $versionString .")");
+ }
+ }
+ else {
+ $retval['version_suffix'] = "";
+ }
+
+ 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;
+
+ //call to ensure files have been processed.
+ #$this->check_versions(FALSE);
+ $this->read_config_version();
+ $this->read_version_file();
+ $configVersion = NULL;
+
+ //parse the version strings.
+ if(strlen($this->configVersion)) {
+ $configVersion = $this->parse_version_string($this->configVersion);
+ }
+ $versionFile = $this->parse_version_string($this->versionFileVersion);
+
+
+ $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 = 0;
+ }
+ 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']) {
+ throw new exception(__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 {
+ throw new exception(__METHOD__ .": downgrading from maintenance versions is unsupported");
+ }
+ }
+ elseif($versionFileData['version_minor'] > $dbVersion['version_minor']) {
+ $retval = "minor";
+ }
+ else {
+ throw new exception(__METHOD__ .": downgrading minor versions is unsupported");
+ }
+ }
+ elseif($versionFileData['version_major'] > $dbVersion['version_major']) {
+ $retval = "major";
+ }
+ else {
+ throw new exception(__METHOD__ .": downgrading major versions is unsupported");
+ }
+ }
+
+ if(!is_null($retval) && $retval !== 0) {
+ $this->gfObj->debug_print(__METHOD__ .": upgrading ". $retval ." versions, from (". $this->databaseVersion .") to (". $this->versionFileVersion .")");
+ }
+
+ return($retval);
+ }//end check_for_version_conflict()
+ //=========================================================================
+
+
+
+ //=========================================================================
+ private function get_database_version() {
+ //create a database object & attempt to read the database version.
+
+ if(!is_object($this->db) || get_class($this->db) != 'cs_phpDB') {
+ $this->db = new cs_phpDB;
+ $this->db->connect(get_config_db_params());
+ }
+
+ $sql = "SELECT " .
+ "internal_data_get_value('version_string') AS version_string, " .
+ "internal_data_get_value('version_major') AS version_major, " .
+ "internal_data_get_value('version_minor') AS version_minor, " .
+ "internal_data_get_value('version_maintenance') AS version_maintenance, " .
+ "internal_data_get_value('version_suffix') AS version_suffix";
+ $numrows = $this->db->exec($sql);
+ $dberror = $this->db->errorMsg();
+
+ if(strlen($dberror) || $numrows != 1) {
+ //fail.
+ throw new exception(__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($targetVersion) {
+ //Use the "matching_syntax" data in the upgrade.xml file to determine the filename.
+ $versionIndex = "V". $targetVersion;
+ $this->gfObj->debug_print(__METHOD__ .": versionIndex=(". $versionIndex ."), config MATCHING::: ". $this->gfObj->debug_print($this->config['MATCHING'],0));
+ if(!isset($this->config['MATCHING'][$versionIndex])) {
+ //version-only upgrade.
+ $this->update_database_version($this->versionFileVersion);
+ $this->newVersion = $this->versionFileVersion;
+ $this->gfObj->debug_print(__METHOD__ .": doing version-only upgrade...");
+ }
+ else {
+ $scriptIndex = $versionIndex;
+
+ $upgradeData = $this->config['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 {
+ throw new exception(__METHOD__ .": not enough information to run scripted upgrade for ". $versionIndex);
+ }
+ }
+ else {
+ throw new exception(__METHOD__ .": target version not specified, unable to proceed with upgrade for ". $versionIndex);
+ }
+ }
+ $this->gfObj->debug_print(__METHOD__ .": done... ");
+ }//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) {
+ $this->gfObj->debug_print(__METHOD__ .": setting (". $newVersionString .")");
+ $versionArr = $this->parse_version_string($newVersionString);
+
+ $queryArr = array();
+ foreach($versionArr as $index=>$value) {
+ $queryArr[$index] = "SELECT internal_data_set_value('". $index ."', '". $value ."');";
+ }
+
+ $retval = NULL;
+ foreach($queryArr as $name=>$sql) {
+ if($this->run_sql($sql, 1)) {
+ $retval++;
+ }
+ }
+
+ //okay, now check that the version string matches the updated bits.
+ if(!$this->check_database_version($this->newVersion)) {
+ throw new exception(__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($checkThisVersion=NULL) {
+ //retrieve the internal version information.
+ $sql = "select internal_data_get_value('version_string') as version_string, (" .
+ "internal_data_get_value('version_major') || '.' || " .
+ "internal_data_get_value('version_minor') || '.' || " .
+ "internal_data_get_value('version_maintenance')) as check_version, " .
+ "internal_data_get_value('version_suffix') AS version_suffix";
+
+ $retval = NULL;
+ if($this->run_sql($sql,1)) {
+ $data = $this->db->farray_fieldnames();
+ $versionString = $data['version_string'];
+ $checkVersion = $data['check_version'];
+
+ if(strlen($data['version_suffix'])) {
+ //the version string already would have this, but the checked version wouldn't.
+ $checkVersion .= "-". $data['version_suffix'];
+ }
+
+ if($versionString == $checkVersion) {
+ $retval = TRUE;
+ }
+ else {
+ $retval = FALSE;
+ }
+ }
+ else {
+ $retval = FALSE;
+ }
+
+ if(!$retval) {
+ $this->gfObj->debug_print($data);
+ $this->gfObj->debug_print(__METHOD__ .": versionString=(". $versionString ."), checkVersion=(". $checkVersion .")");
+ }
+
+ return($retval);
+
+ }//end check_database_version()
+ //=========================================================================
+
+
+
+ //=========================================================================
+ private function do_scripted_upgrade(array $upgradeData) {
+ $myConfigFile = $upgradeData['SCRIPT_NAME'];
+
+ $this->gfObj->debug_print(__METHOD__ .": script name=(". $myConfigFile .")");
+
+ //we've got the filename, see if it exists.
+ $fileName = UPGRADE_DIR .'/'. $myConfigFile;
+ if(file_exists($fileName)) {
+ $this->gfObj->debug_print(__METHOD__ .": file exists... ");
+ $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();
+ $this->gfObj->debug_print(__METHOD__ .": finished running ". $createClassName ."::". $classUpgradeMethod ."(), result was (". $upgradeResult .")");
+ }
+ else {
+ throw new exception(__METHOD__ .": upgrade method doesn't exist (". $createClassName ."::". $classUpgradeMethod
+ ."), unable to perform upgrade ");
+ }
+ }
+ else {
+ throw new exception(__METHOD__ .": unable to locate upgrade class name (". $createClassName .")");
+ }
+ }
+ else {
+ throw new exception(__METHOD__ .": upgrade filename (". $fileName .") does not exist");
+ }
+ }//end do_scripted_upgrade()
+ //=========================================================================
+
+
+
+ //=========================================================================
+ protected function run_sql($sql, $expectedNumrows=1) {
+ if(!$this->db->is_connected()) {
+ $this->db->connect(get_config_db_params());
+ }
+ $numrows = $this->db->exec($sql);
+ $dberror = $this->db->errorMsg();
+
+ if(strlen($dberror)) {
+ $details = "DBERROR::: ". $dberror;
+ throw new exception(__METHOD__ .": SQL FAILED::: ". $sql ."\n\nDETAILS: ". $details);
+ }
+ elseif(!is_null($expectedNumrows) && $numrows != $expectedNumrows) {
+ throw new exception(__METHOD__ .": SQL FAILED::: ". $sql ."\n\nDETAILS: " .
+ "rows affected didn't match expectation (". $numrows ." != ". $expectedNumrows .")");
+ }
+ elseif(is_null($expectedNumrows) && $numrows < 1) {
+ throw new exception(__METHOD__ .": SQL FAILED::: ". $sql ."\n\nDETAILS: " .
+ "invalid number of rows affected (". $numrows .")");
+ }
+ else {
+ $retval = TRUE;
+ }
+
+ return($retval);
+ }//end run_sql()
+ //=========================================================================
+
+
+
+ //=========================================================================
+ private function update_config_file($index, $value) {
+ $gf = new cs_globalFunctions;
+ $myConfigFile = CONFIG_FILE_LOCATION;
+ $fs = new cs_fileSystemClass(dirname(__FILE__) .'/../');
+ $xmlParser = new XMLParser($fs->read($myConfigFile));
+ $xmlCreator = new XMLCreator;
+ $xmlCreator->load_xmlparser_data($xmlParser);
+
+ //update the given index.
+ $xmlCreator->add_tag($index, $value, $xmlParser->get_attribute('/CONFIG/'. strtoupper($index)));
+ $this->mainConfig[strtoupper($index)] = $value;
+
+ $xmlString = $xmlCreator->create_xml_string();
+
+ //truncate the file, to avoid problems with extra data at the end...
+ $fs->closeFile();
+ $fs->create_file($myConfigFile,TRUE);
+ $fs->openFile($myConfigFile);
+
+ //now write the new configuration.
+ $fs->write($xmlString, $myConfigFile);
+ }//end update_config_file()
+ //=========================================================================
+
+
+
+ //=========================================================================
+ protected function get_num_users_to_convert() {
+
+ $retval = 0;
+ try {
+ //if this generates an error, there are no users...
+ $this->run_sql("SELECT internal_data_get_value('users_to_convert')");
+ $data = $this->db->farray();
+ $retval = $data[0];
+ }
+ catch(exception $e) {
+ $this->gfObj->debug_print(__METHOD__ .": failed to retrieve users to convert: ");
+ }
+
+ return($retval);
+
+ }//end get_num_users_to_convert()
+ //=========================================================================
+
+
+
+ //=========================================================================
+ protected function update_num_users_to_convert() {
+ $retval = $this->run_sql("SELECT internal_data_set_value('users_to_convert', (select count(*) FROM user_table WHERE length(password) != 32))");
+ return($retval);
+ }//end update_num_users_to_convert()
+ //=========================================================================
+
+
+
+ //=========================================================================
+ protected function is_higher_version($version, $checkIfHigher) {
+ $retval = FALSE;
+ if(!is_string($version) || !is_string($checkIfHigher)) {
+ throw new exception(__METHOD__ .": didn't get strings... ". debug_print(func_get_args(),0));
+ }
+ 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?
+ debug_print(__METHOD__ .": while checking ". $index .", realized the new version (". $checkIfHigher .") is LOWER than current (". $version .")",1);
+ }
+ }
+ 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) {
+ $this->gfObj->debug_print(__METHOD__ .": checking suffixes... ");
+ //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.
+ $this->gfObj->debug_print(__METHOD__ .": suffixes match");
+ }
+ 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']) {
+ $this->gfObj->debug_print(__METHOD__ .": got the same type...");
+ //got the same suffix type (like "BETA"), check the number.
+ if($checkVersionData['number'] > $curVersionData['number']) {
+ $this->gfObj->debug_print(__METHOD__ .": new version's suffix number higher than current... ");
+ $retval = TRUE;
+ }
+ elseif($checkVersionData['number'] == $curVersionData['number']) {
+ $this->gfObj->debug_print(__METHOD__ .": new version's suffix number is EQUAL TO current... ");
+ $retval = FALSE;
+ }
+ else {
+ //umm... they're identical??? LOGIC HAS FAILED ME ALTOGETHER!!!
+ $this->gfObj->debug_print(__METHOD__ .": 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 {
+ $this->gfObj->debug_print(__METHOD__ .": 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!
+ $this->gfObj->debug_print(__METHOD__ .": from (". $version .") to (". $checkIfHigher .") isn't acceptable...?");
+ }
+ }
+ else {
+ $this->gfObj->debug_print(__METHOD__ .": no suffix to care about");
+ }
+ }
+ }
+
+ $this->gfObj->debug_print(__METHOD__ .": ('". $version ."', '". $checkIfHigher ."') retval=(". $retval .")", 1);
+
+ 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)) {
+ throw new exception(__METHOD__ .": version (". $newVersion .") isn't higher than (". $dbVersion .")... something is broken");
+ }
+ elseif(is_array($this->config['MATCHING'])) {
+ $lastVersion = $dbVersion;
+ foreach($this->config['MATCHING'] as $matchVersion=>$data) {
+
+ $matchVersion = preg_replace('/^V/', '', $matchVersion);
+ if($matchVersion == $data['TARGET_VERSION']) {
+ throw new exception(__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.
+ $this->gfObj->debug_print(__METHOD__ .": adding (". $matchVersion .")");
+ $lastVersion = $data['TARGET_VERSION'];
+ $retval[$matchVersion] = $data['TARGET_VERSION'];
+ }
+ else {
+ $this->gfObj->debug_print(__METHOD__ .": entry in upgrade.xml (". $matchVersion .") is higher than the VERSION file (". $this->versionFileVersion .")");
+ }
+ }
+ else {
+ $this->gfObj->debug_print(__METHOD__ .": SKIPPING (". $matchVersion .")");
+ }
+ }
+
+ if($lastVersion !== $newVersion && (!isset($retval[$lastVersion]) || $retval[$lastVersion] != $newVersion)) {
+ $this->gfObj->debug_print(__METHOD__ .": <b>ALSO (". $lastVersion .") => (". $newVersion .")</b>");
+ $retval[$lastVersion] = $newVersion;
+ }
+ }
+ else {
+ //no intermediary upgrades: just pass back the latest version.
+ $this->gfObj->debug_print(__METHOD__ .": no intermediary upgrades");
+ $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 {
+ throw new exception(__METHOD__ .": invalid suffix (". $suffix .")");
+ }
+ break;
+ }
+ }
+ }
+ else {
+ throw new exception(__METHOD__ .": invalid suffix (". $suffix .")");
+ }
+
+ return($retval);
+ }//end parse_suffix()
+ //=========================================================================
+
+
+}//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.
|