diff --git a/include/ajax.upgrader.php b/include/ajax.upgrader.php index 8b263ba0ced03b51cacc0341b20f0fc7b8dc01b1..331d23d26bef97b89eddbf2e80170947a794b72f 100644 --- a/include/ajax.upgrader.php +++ b/include/ajax.upgrader.php @@ -25,14 +25,7 @@ class UpgraderAjaxAPI extends AjaxController { if(!$thisstaff or !$thisstaff->isAdmin() or !$ost) Http::response(403, 'Access Denied'); - $upgrader = new Upgrader($ost->getDBSignature(), TABLE_PREFIX, SQL_DIR); - - //Just report the next action on the first call. - if(!$_SESSION['ost_upgrader'] || !$_SESSION['ost_upgrader'][$upgrader->getShash()]['progress']) { - $_SESSION['ost_upgrader'][$upgrader->getShash()]['progress'] = $upgrader->getNextAction(); - Http::response(200, $upgrader->getNextAction()); - exit; - } + $upgrader = new Upgrader(TABLE_PREFIX, UPGRADE_DIR.'streams/'); if($upgrader->isAborted()) { Http::response(416, "We have a problem ... wait a sec."); @@ -51,7 +44,7 @@ class UpgraderAjaxAPI extends AjaxController { Http::response(200, "Upgraded to $version ... post-upgrade checks!"); exit; } - } else { + } else { //Abort: Upgrade pending but NOT upgradable - invalid or wrong hash. $upgrader->abort(sprintf('Upgrade Failed: Invalid or wrong hash [%s]',$ost->getDBSignature())); } diff --git a/include/class.migrater.php b/include/class.migrater.php index ba26d091e31defa230ab4da3f7420d3e044dbaf2..f3d6c775dc50240deaf460b7586d961770422aed 100644 --- a/include/class.migrater.php +++ b/include/class.migrater.php @@ -70,6 +70,36 @@ class DatabaseMigrater { return array_filter($patches); } + + /** + * Reads update stream information from UPGRADE_DIR/<streams>/streams.cfg. + * Each declared stream folder should contain a file under the name of the + * stream with an 'sig' extension. The file will be the hash of the + * signature of the tip of the stream. A corresponding config variable + * 'schema_signature' should exist in the namespace of the stream itself. + * If the hash file doesn't match the schema_signature on record, then an + * update is triggered and the patches in the stream folder are used to + * upgrade the database. + */ + /* static */ + function getUpgradeStreams($basedir) { + static $streams = array(); + if ($streams) return $streams; + + // TODO: Make the hash algo configurable in the streams + // configuration ( core : md5 ) + $config = @file_get_contents($basedir.'/streams.cfg'); + if (!$config) $config = 'core'; + foreach (explode("\n", $config) as $line) { + $line = trim(preg_replace('/#.*$/', '', $line)); + if (!$line) + continue; + else if (file_exists($basedir."$line.sig") && is_dir($basedir.$line)) + $streams[$line] = + trim(file_get_contents($basedir."$line.sig")); + } + return $streams; + } } diff --git a/include/class.osticket.php b/include/class.osticket.php index 1a1945218d05ba79ba906d41ddaf8eb4ce942b2a..671bdca4ba7f34ab99698dba009e79a73e06e61c 100644 --- a/include/class.osticket.php +++ b/include/class.osticket.php @@ -20,6 +20,7 @@ require_once(INCLUDE_DIR.'class.config.php'); //Config helper require_once(INCLUDE_DIR.'class.csrf.php'); //CSRF token class. +require_once(INCLUDE_DIR.'class.migrater.php'); define('LOG_WARN',LOG_WARNING); @@ -60,8 +61,11 @@ class osTicket { } function isUpgradePending() { - return strcasecmp(SCHEMA_SIGNATURE, - $this->getConfig()->getSchemaSignature()); + foreach (DatabaseMigrater::getUpgradeStreams(UPGRADE_DIR.'streams/') as $stream=>$hash) + if (strcasecmp($hash, + $this->getConfig()->getSchemaSignature($stream))) + return true; + return false; } function getSession() { @@ -72,8 +76,8 @@ class osTicket { return $this->config; } - function getDBSignature() { - return $this->getConfig()->getSchemaSignature(); + function getDBSignature($namespace='core') { + return $this->getConfig()->getSchemaSignature($namespace); } function getVersion() { diff --git a/include/class.upgrader.php b/include/class.upgrader.php index 8f6fea22342aacb2c2a23da4547f7c9d935a7e47..e17b0a4b2626bb21cc3e7b2103c48b5a3a9c4568 100644 --- a/include/class.upgrader.php +++ b/include/class.upgrader.php @@ -17,7 +17,130 @@ require_once INCLUDE_DIR.'class.setup.php'; require_once INCLUDE_DIR.'class.migrater.php'; -class Upgrader extends SetupWizard { +class Upgrader { + function Upgrader($prefix, $basedir) { + global $ost; + + $this->streams = array(); + foreach (DatabaseMigrater::getUpgradeStreams($basedir) as $stream=>$hash) { + $signature = $ost->getConfig()->getSchemaSignature($stream); + $this->streams[$stream] = new StreamUpgrader($signature, $hash, $stream, + $prefix, $basedir.$stream.'/', $this); + } + + //Init persistent state of upgrade. + $this->state = &$_SESSION['ost_upgrader']['state']; + + $this->mode = &$_SESSION['ost_upgrader']['mode']; + + $this->current = &$_SESSION['ost_upgrader']['stream']; + if (!$this->current || $this->getCurrentStream()->isFinished()) { + $streams = array_keys($this->streams); + do { + $this->current = array_shift($streams); + } while ($this->current && $this->getCurrentStream()->isFinished()); + } + } + + function getCurrentStream() { + return $this->streams[$this->current]; + } + + function isUpgradable() { + if ($this->isAborted()) + return false; + + foreach ($this->streams as $s) + if (!$s->isUpgradable()) + return false; + + return true; + } + + function isAborted() { + return !strcasecmp($this->getState(), 'aborted'); + } + + function getState() { + return $this->state; + } + + function setState($state) { + $this->state = $state; + } + + function getMode() { + return $this->mode; + } + + function setMode($mode) { + $this->mode = $mode; + } + + function upgrade() { + if (!$this->current) + return true; + + return $this->getCurrentStream()->upgrade(); + } + + function check_prereq() { + if ($this->getCurrentStream()) + return $this->getCurrentStream()->check_prereq(); + } + function check_php() { + if ($this->getCurrentStream()) + return $this->getCurrentStream()->check_php(); + } + function check_mysql() { + if ($this->getCurrentStream()) + return $this->getCurrentStream()->check_mysql(); + } + + function getNumPendingTasks() { + if ($this->getCurrentStream()) + return $this->getCurrentStream()->getNumPendingTasks(); + } + + function doTasks() { + if ($this->getNumPendingTasks()) + return $this->getCurrentStream()->doTasks(); + } + + function getErrors() { + return $this->getCurrentStream()->getError(); + } + + function getNextAction() { + if ($this->getCurrentStream()) + return $this->getCurrentStream()->getNextAction(); + } + + function getNextVersion() { + return $this->getCurrentStream()->getNextVersion(); + } + + function getSchemaSignature() { + if ($this->getCurrentStream()) + return $this->getCurrentStream()->getSchemaSignature(); + } + + function getSHash() { + if ($this->getCurrentStream()) + return $this->getCurrentStream()->getSHash(); + } +} + +/** + * Updates a single database stream. In the classical sense, osTicket only + * maintained a single database update stream. In that model, this + * represents upgrading that single stream. In multi-stream mode, + * customizations and plugins are supported to have their own respective + * database update streams. The Upgrader class is used to coordinate updates + * for all the streams, whereas the work to upgrade each stream is done in + * this class + */ +class StreamUpgrader extends SetupWizard { var $prefix; var $sqldir; @@ -26,22 +149,32 @@ class Upgrader extends SetupWizard { var $state; var $mode; - function Upgrader($signature, $prefix, $sqldir) { - - $this->signature = $signature; + /** + * Parameters: + * schema_signature - (string<hash-hex>) Current database-reflected (via + * config table) version of the stream + * target - (stream<hash-hex>) Current stream tip, as reflected by + * streams/<stream>.sig + * stream - (string) Name of the stream (folder) + * prefix - (string) Database table prefix + * sqldir - (string<path>) Path of sql patches + * upgrader - (Upgrader) Parent coordinator of parallel stream updates + */ + function StreamUpgrader($schema_signature, $target, $stream, $prefix, $sqldir, $upgrader) { + + $this->signature = $schema_signature; + $this->target = $target; $this->prefix = $prefix; $this->sqldir = $sqldir; $this->errors = array(); $this->mode = 'ajax'; // + $this->upgrader = $upgrader; + $this->name = $stream; //Disable time limit if - safe mode is set. if(!ini_get('safe_mode')) set_time_limit(0); - //Init persistent state of upgrade. - $this->state = &$_SESSION['ost_upgrader']['state']; - - $this->mode = &$_SESSION['ost_upgrader']['mode']; //Init the task Manager. if(!isset($_SESSION['ost_upgrader'][$this->getShash()])) @@ -57,9 +190,10 @@ class Upgrader extends SetupWizard { function onError($error) { global $ost, $thisstaff; - $ost->logError('Upgrader Error', $error); + $subject = '['.$this->name.']: Upgrader Error'; + $ost->logError($subject, $error); $this->setError($error); - $this->setState('aborted'); + $this->upgrader->setState('aborted'); //Alert staff upgrading the system - if the email is not same as admin's // admin gets alerted on error log (above) @@ -70,7 +204,6 @@ class Upgrader extends SetupWizard { if(!($email=$ost->getConfig()->getAlertEmail())) $email=$ost->getConfig()->getDefaultEmail(); //will take the default email. - $subject = 'Upgrader Error'; if($email) { $email->sendAlert($thisstaff->getEmail(), $subject, $error); } else {//no luck - try the system mail. @@ -80,11 +213,7 @@ class Upgrader extends SetupWizard { } function isUpgradable() { - return (!$this->isAborted() && $this->getNextPatch()); - } - - function isAborted() { - return !strcasecmp($this->getState(), 'aborted'); + return $this->getNextPatch(); } function getSchemaSignature() { @@ -103,25 +232,9 @@ class Upgrader extends SetupWizard { return $this->sqldir; } - function getState() { - return $this->state; - } - - function setState($state) { - $this->state = $state; - } - - function getMode() { - return $this->mode; - } - - function setMode($mode) { - $this->mode = $mode; - } - function getMigrater() { if(!$this->migrater) - $this->migrater = new DatabaseMigrater($this->signature, SCHEMA_SIGNATURE, $this->sqldir); + $this->migrater = new DatabaseMigrater($this->signature, $this->target, $this->sqldir); return $this->migrater; } @@ -146,6 +259,12 @@ class Upgrader extends SetupWizard { return $info['version']; } + function isFinished() { + # TODO: 1. Check if current and target hashes match, + # 2. Any pending tasks + return !($this->getNextPatch() || $this->getPendingTasks()); + } + function readPatchInfo($patch) { $info = array(); if (preg_match(':/\*\*(.*)\*/:s', file_get_contents($patch), $matches)) { @@ -170,7 +289,7 @@ class Upgrader extends SetupWizard { $action = "Upgrade to $nextversion"; } - return $action; + return '['.$this->name.'] '.$action; } function getNumPendingTasks() { diff --git a/setup/inc/sql/osTicket-mysql.sql.md5 b/include/upgrader/streams/core.sig similarity index 100% rename from setup/inc/sql/osTicket-mysql.sql.md5 rename to include/upgrader/streams/core.sig diff --git a/include/upgrader/sql/00ff231f-9f3b454c.patch.sql b/include/upgrader/streams/core/00ff231f-9f3b454c.patch.sql similarity index 100% rename from include/upgrader/sql/00ff231f-9f3b454c.patch.sql rename to include/upgrader/streams/core/00ff231f-9f3b454c.patch.sql diff --git a/include/upgrader/sql/02decaa2-60fcbee1.patch.sql b/include/upgrader/streams/core/02decaa2-60fcbee1.patch.sql similarity index 100% rename from include/upgrader/sql/02decaa2-60fcbee1.patch.sql rename to include/upgrader/streams/core/02decaa2-60fcbee1.patch.sql diff --git a/include/upgrader/sql/15719536-dd0022fb.patch.sql b/include/upgrader/streams/core/15719536-dd0022fb.patch.sql similarity index 100% rename from include/upgrader/sql/15719536-dd0022fb.patch.sql rename to include/upgrader/streams/core/15719536-dd0022fb.patch.sql diff --git a/include/upgrader/sql/15af7cd3-98ae1ed2.patch.sql b/include/upgrader/streams/core/15af7cd3-98ae1ed2.patch.sql similarity index 100% rename from include/upgrader/sql/15af7cd3-98ae1ed2.patch.sql rename to include/upgrader/streams/core/15af7cd3-98ae1ed2.patch.sql diff --git a/include/upgrader/sql/15b30765-dd0022fb.cleanup.sql b/include/upgrader/streams/core/15b30765-dd0022fb.cleanup.sql similarity index 100% rename from include/upgrader/sql/15b30765-dd0022fb.cleanup.sql rename to include/upgrader/streams/core/15b30765-dd0022fb.cleanup.sql diff --git a/include/upgrader/sql/15b30765-dd0022fb.patch.sql b/include/upgrader/streams/core/15b30765-dd0022fb.patch.sql similarity index 100% rename from include/upgrader/sql/15b30765-dd0022fb.patch.sql rename to include/upgrader/streams/core/15b30765-dd0022fb.patch.sql diff --git a/include/upgrader/sql/1da1bcba-15b30765.patch.sql b/include/upgrader/streams/core/1da1bcba-15b30765.patch.sql similarity index 100% rename from include/upgrader/sql/1da1bcba-15b30765.patch.sql rename to include/upgrader/streams/core/1da1bcba-15b30765.patch.sql diff --git a/include/upgrader/sql/2e20a0eb-98ae1ed2.patch.sql b/include/upgrader/streams/core/2e20a0eb-98ae1ed2.patch.sql similarity index 100% rename from include/upgrader/sql/2e20a0eb-98ae1ed2.patch.sql rename to include/upgrader/streams/core/2e20a0eb-98ae1ed2.patch.sql diff --git a/include/upgrader/sql/2e7531a2-d0e37dca.patch.sql b/include/upgrader/streams/core/2e7531a2-d0e37dca.patch.sql similarity index 100% rename from include/upgrader/sql/2e7531a2-d0e37dca.patch.sql rename to include/upgrader/streams/core/2e7531a2-d0e37dca.patch.sql diff --git a/include/upgrader/sql/32de1766-852ca89e.patch.sql b/include/upgrader/streams/core/32de1766-852ca89e.patch.sql similarity index 100% rename from include/upgrader/sql/32de1766-852ca89e.patch.sql rename to include/upgrader/streams/core/32de1766-852ca89e.patch.sql diff --git a/include/upgrader/sql/435c62c3-2e7531a2.cleanup.sql b/include/upgrader/streams/core/435c62c3-2e7531a2.cleanup.sql similarity index 100% rename from include/upgrader/sql/435c62c3-2e7531a2.cleanup.sql rename to include/upgrader/streams/core/435c62c3-2e7531a2.cleanup.sql diff --git a/include/upgrader/sql/435c62c3-2e7531a2.patch.sql b/include/upgrader/streams/core/435c62c3-2e7531a2.patch.sql similarity index 100% rename from include/upgrader/sql/435c62c3-2e7531a2.patch.sql rename to include/upgrader/streams/core/435c62c3-2e7531a2.patch.sql diff --git a/include/upgrader/sql/49478749-c2d2fabf.patch.sql b/include/upgrader/streams/core/49478749-c2d2fabf.patch.sql similarity index 100% rename from include/upgrader/sql/49478749-c2d2fabf.patch.sql rename to include/upgrader/streams/core/49478749-c2d2fabf.patch.sql diff --git a/include/upgrader/sql/522e5b78-02decaa2.patch.sql b/include/upgrader/streams/core/522e5b78-02decaa2.patch.sql similarity index 100% rename from include/upgrader/sql/522e5b78-02decaa2.patch.sql rename to include/upgrader/streams/core/522e5b78-02decaa2.patch.sql diff --git a/include/upgrader/sql/60fcbee1-f8856d56.patch.sql b/include/upgrader/streams/core/60fcbee1-f8856d56.patch.sql similarity index 100% rename from include/upgrader/sql/60fcbee1-f8856d56.patch.sql rename to include/upgrader/streams/core/60fcbee1-f8856d56.patch.sql diff --git a/include/upgrader/sql/7be60a84-522e5b78.patch.sql b/include/upgrader/streams/core/7be60a84-522e5b78.patch.sql similarity index 100% rename from include/upgrader/sql/7be60a84-522e5b78.patch.sql rename to include/upgrader/streams/core/7be60a84-522e5b78.patch.sql diff --git a/include/upgrader/sql/852ca89e-740428f9.patch.sql b/include/upgrader/streams/core/852ca89e-740428f9.patch.sql similarity index 100% rename from include/upgrader/sql/852ca89e-740428f9.patch.sql rename to include/upgrader/streams/core/852ca89e-740428f9.patch.sql diff --git a/include/upgrader/sql/98ae1ed2-e342f869.cleanup.sql b/include/upgrader/streams/core/98ae1ed2-e342f869.cleanup.sql similarity index 100% rename from include/upgrader/sql/98ae1ed2-e342f869.cleanup.sql rename to include/upgrader/streams/core/98ae1ed2-e342f869.cleanup.sql diff --git a/include/upgrader/sql/98ae1ed2-e342f869.patch.sql b/include/upgrader/streams/core/98ae1ed2-e342f869.patch.sql similarity index 100% rename from include/upgrader/sql/98ae1ed2-e342f869.patch.sql rename to include/upgrader/streams/core/98ae1ed2-e342f869.patch.sql diff --git a/include/upgrader/sql/9f3b454c-c0fd16f4.patch.sql b/include/upgrader/streams/core/9f3b454c-c0fd16f4.patch.sql similarity index 100% rename from include/upgrader/sql/9f3b454c-c0fd16f4.patch.sql rename to include/upgrader/streams/core/9f3b454c-c0fd16f4.patch.sql diff --git a/include/upgrader/sql/a67ba35e-98ae1ed2.patch.sql b/include/upgrader/streams/core/a67ba35e-98ae1ed2.patch.sql similarity index 100% rename from include/upgrader/sql/a67ba35e-98ae1ed2.patch.sql rename to include/upgrader/streams/core/a67ba35e-98ae1ed2.patch.sql diff --git a/include/upgrader/sql/aa4664af-b19dc97d.patch.sql b/include/upgrader/streams/core/aa4664af-b19dc97d.patch.sql similarity index 100% rename from include/upgrader/sql/aa4664af-b19dc97d.patch.sql rename to include/upgrader/streams/core/aa4664af-b19dc97d.patch.sql diff --git a/include/upgrader/sql/abe9c0cb-bbb021fb.patch.sql b/include/upgrader/streams/core/abe9c0cb-bbb021fb.patch.sql similarity index 100% rename from include/upgrader/sql/abe9c0cb-bbb021fb.patch.sql rename to include/upgrader/streams/core/abe9c0cb-bbb021fb.patch.sql diff --git a/include/upgrader/sql/aee589ab-98ae1ed2.patch.sql b/include/upgrader/streams/core/aee589ab-98ae1ed2.patch.sql similarity index 100% rename from include/upgrader/sql/aee589ab-98ae1ed2.patch.sql rename to include/upgrader/streams/core/aee589ab-98ae1ed2.patch.sql diff --git a/include/upgrader/sql/b19dc97d-435c62c3.patch.sql b/include/upgrader/streams/core/b19dc97d-435c62c3.patch.sql similarity index 100% rename from include/upgrader/sql/b19dc97d-435c62c3.patch.sql rename to include/upgrader/streams/core/b19dc97d-435c62c3.patch.sql diff --git a/include/upgrader/sql/bbb021fb-49478749.patch.sql b/include/upgrader/streams/core/bbb021fb-49478749.patch.sql similarity index 100% rename from include/upgrader/sql/bbb021fb-49478749.patch.sql rename to include/upgrader/streams/core/bbb021fb-49478749.patch.sql diff --git a/include/upgrader/sql/c00511c7-7be60a84.cleanup.sql b/include/upgrader/streams/core/c00511c7-7be60a84.cleanup.sql similarity index 100% rename from include/upgrader/sql/c00511c7-7be60a84.cleanup.sql rename to include/upgrader/streams/core/c00511c7-7be60a84.cleanup.sql diff --git a/include/upgrader/sql/c00511c7-7be60a84.patch.sql b/include/upgrader/streams/core/c00511c7-7be60a84.patch.sql similarity index 100% rename from include/upgrader/sql/c00511c7-7be60a84.patch.sql rename to include/upgrader/streams/core/c00511c7-7be60a84.patch.sql diff --git a/include/upgrader/sql/c0fd16f4-d959a00e.patch.sql b/include/upgrader/streams/core/c0fd16f4-d959a00e.patch.sql similarity index 100% rename from include/upgrader/sql/c0fd16f4-d959a00e.patch.sql rename to include/upgrader/streams/core/c0fd16f4-d959a00e.patch.sql diff --git a/include/upgrader/sql/c2d2fabf-aa4664af.patch.sql b/include/upgrader/streams/core/c2d2fabf-aa4664af.patch.sql similarity index 100% rename from include/upgrader/sql/c2d2fabf-aa4664af.patch.sql rename to include/upgrader/streams/core/c2d2fabf-aa4664af.patch.sql diff --git a/include/upgrader/sql/d0e37dca-1da1bcba.patch.sql b/include/upgrader/streams/core/d0e37dca-1da1bcba.patch.sql similarity index 100% rename from include/upgrader/sql/d0e37dca-1da1bcba.patch.sql rename to include/upgrader/streams/core/d0e37dca-1da1bcba.patch.sql diff --git a/include/upgrader/sql/d959a00e-32de1766.patch.sql b/include/upgrader/streams/core/d959a00e-32de1766.patch.sql similarity index 100% rename from include/upgrader/sql/d959a00e-32de1766.patch.sql rename to include/upgrader/streams/core/d959a00e-32de1766.patch.sql diff --git a/include/upgrader/sql/dd0022fb-f4da0c9b.patch.sql b/include/upgrader/streams/core/dd0022fb-f4da0c9b.patch.sql similarity index 100% rename from include/upgrader/sql/dd0022fb-f4da0c9b.patch.sql rename to include/upgrader/streams/core/dd0022fb-f4da0c9b.patch.sql diff --git a/include/upgrader/sql/e342f869-c00511c7.patch.sql b/include/upgrader/streams/core/e342f869-c00511c7.patch.sql similarity index 100% rename from include/upgrader/sql/e342f869-c00511c7.patch.sql rename to include/upgrader/streams/core/e342f869-c00511c7.patch.sql diff --git a/include/upgrader/sql/f4da0c9b-00ff231f.patch.sql b/include/upgrader/streams/core/f4da0c9b-00ff231f.patch.sql similarity index 100% rename from include/upgrader/sql/f4da0c9b-00ff231f.patch.sql rename to include/upgrader/streams/core/f4da0c9b-00ff231f.patch.sql diff --git a/include/upgrader/sql/f8856d56-abe9c0cb.patch.sql b/include/upgrader/streams/core/f8856d56-abe9c0cb.patch.sql similarity index 100% rename from include/upgrader/sql/f8856d56-abe9c0cb.patch.sql rename to include/upgrader/streams/core/f8856d56-abe9c0cb.patch.sql diff --git a/main.inc.php b/main.inc.php index 6f7c932045fa46099de9d707b69dc105bdfecfba..e5d19c0a417ce78d71ccfdb5b3a1772bff9ec042 100644 --- a/main.inc.php +++ b/main.inc.php @@ -70,13 +70,11 @@ define('SETUP_DIR',INCLUDE_DIR.'setup/'); define('UPGRADE_DIR', INCLUDE_DIR.'upgrader/'); - define('SQL_DIR', UPGRADE_DIR.'sql/'); /*############## Do NOT monkey with anything else beyond this point UNLESS you really know what you are doing ##############*/ #Current version && schema signature (Changes from version to version) define('THIS_VERSION','1.7.0+'); //Shown on admin panel - define('SCHEMA_SIGNATURE', '740428f9986da6ad85f88ec841b57bfe'); //MD5 signature of the db schema. (used to trigger upgrades) #load config info $configfile=''; if(file_exists(ROOT_DIR.'ostconfig.php')) //Old installs prior to v 1.6 RC5 diff --git a/scp/upgrade.php b/scp/upgrade.php index c66230a22288d131b3666a1a789df065397880f1..4be64dc3b851e490e40a97911fb018bea52215d3 100644 --- a/scp/upgrade.php +++ b/scp/upgrade.php @@ -17,7 +17,7 @@ require_once 'admin.inc.php'; require_once INCLUDE_DIR.'class.upgrader.php'; //$_SESSION['ost_upgrader']=null; -$upgrader = new Upgrader($cfg->getSchemaSignature(), TABLE_PREFIX, SQL_DIR); +$upgrader = new Upgrader(TABLE_PREFIX, UPGRADE_DIR.'streams/'); $errors=array(); if($_POST && $_POST['s'] && !$upgrader->isAborted()) { switch(strtolower($_POST['s'])) { diff --git a/setup/doc/streams.md b/setup/doc/streams.md new file mode 100644 index 0000000000000000000000000000000000000000..823e7ee5c3921f7c3b226161884dc66312b4b04c --- /dev/null +++ b/setup/doc/streams.md @@ -0,0 +1,114 @@ +osTicket Database Migration +=========================== + +Database Upgrade Streams +------------------------ +Database upgrade streams are registered in the +`INCLUDE_DIR/upgrader/streams/streams.cfg`. This file contains the names of +the upgrade streams in the order the streams should be applied in. The stock +osTicket install does not ship with a `streams.cfg`, so that updates to the +source code base will not destroy your `streams.cfg` file. If the file does +not exist, only the osTicket `core` stream will be updated. + +Streams folders +--------------- +Stream folders are created in the `INCLUDE_DIR/upgrader/streams` folder. The +name of the stream will be the name of the folder. For instance, the core +osTicket stream exists in the `core` folder, and so is called `core`. Each +stream folder should also have an accompanying hash file which gives the md5 +hash of the tip of the stream. How you generate the hashes is up to you. For +the core stream, we use the md5 hash of the install SQL file. Changes made +to the main install file result in changes to the md5 hash of the file. +Then, the update file placed in the upgrade stream will have the changes +between to two hashes. + +Upgrade Streams +--------------- +Upgrade patches are used to migrate the database from snapshot to snapshot. +The system will start upgrading by consulting the `schema_signature` +configuration setting in the `config` table in the namespace of your stream +name. It will look for an upgrade patch file that starts with the first +eight characters of the current signature. + +Each upgrade in a stream should set the `schema_signature` configuration +option in the `config` when completed. The plan is to make this automatic, +but currently, it is still a manual process. Whatever the hash of then last +executed patch is, it should be reflected in the config table at the +completion of the upgrade patch. + +The migration process will continue until the hash reflected in the +`schema_signature` setting is the same as the value given in the stream +signature file. If no patch files are given to migrate from the current +`schema_signature` value to the value listed in the md5 file, then the +migrater will fail and complain that the system is not upgradeable. + +Patch Files +----------- +Patch files should live in your stream folder and should have the name of + + 12345678-00abcdef.patch.sql + +and should contain only SQL text. Double-dash comments are only supported if +started at the beginning of a line. For instance, do not write then inline +as part of a long running SQL statement. The filename format is the first +eight chars of the starting and ending database `schema_signature` values. + +Your patch process can be separated into two parts if you like. A cleanup file can be used to cleanup database objects after the completion of the patch process. Cleanup files must have the name of + + 12345678-00abcdef.cleanup.sql + +Where the starting and ending hashes are listed with a hyphen in between. +The idea is that PHP code can be run between the two SQL patch files. +//Currently, support for this is hardcoded, but will hopefully be redesigned +to include a `patch.php` file at some point in the future.// + +If you want to use numeric serial numbers, make sure the first eight digits +change for every upgrade. For instance, use 00000001-00000002. Technically, +there is no current requirement for the hash file to be an actual md5 or even +have 32 hex chars in it. + +Patch files should contain a header with some common information about the +patch. The header should be formatted similar to + + /* osTicket database migration patch + * + * @version 0.0.0 + * @signature 0123456789abcdef0000000000000000 + * + * Details about the migration patch are listed here + */ + +Eventually the `@signature` line will be automatically inspected and forced +into the config table for the `schema_signature` setting in the namespace of +your stream at the completion of the patch process. Please add it to your +patches to keep them future-minded. The `@version` is a string that will be +shown to clients during the upgrade process indicating the version numbers +as each patch is applied. + +Customizing osTicket +==================== +osTicket now supports database customizations using a separation technique +called *streams*. Separating the database upgrade path into streams allows +the upstream osTicket database upgrades to be kept separate from your own, +custom upgrade streams. Streams are registered in `streams.cfg` located in +the `UPGRADE_DIR/streams`. + +Example streams.cfg +------------------- + # Write the names of the stream folders to be enabled in this file. + # The order is significant. The upgrade process will run updates for the + # respective streams in the order they are listed in this file. + + core # The upstream osTicket upgrade stream + + # Add custom upgrade streams here, which will be applied in the order + # listed in this file + +Database Customization Rules +--------------------------- +1. Leave the upstream osTicket tables unchanged. If your customization makes + changes to the main osTicket tables, you will likely get merge conflicts + when the *core* stream is updated. If you need to add columns to an + upstream table, add another table with the extra columns and link the + data to the upstream table using the primary key of the upstream table. + This will keep your data model separate from the upstream data model. diff --git a/setup/inc/class.installer.php b/setup/inc/class.installer.php index 40d9137dc6ea723ea5c4cd9d357b4523bc234a43..5562f33ab120196550aff8690984b4d610d27309 100644 --- a/setup/inc/class.installer.php +++ b/setup/inc/class.installer.php @@ -13,6 +13,7 @@ vim: expandtab sw=4 ts=4 sts=4: **********************************************************************/ +require_once INCLUDE_DIR.'class.migrater.php'; require_once INCLUDE_DIR.'class.setup.php'; class Installer extends SetupWizard { @@ -109,24 +110,34 @@ class Installer extends SetupWizard { define('ADMIN_EMAIL',$vars['admin_email']); //Needed to report SQL errors during install. define('PREFIX',$vars['prefix']); //Table prefix - $schemaFile =INC_DIR.'sql/osTicket-mysql.sql'; //DB dump. - $debug = true; //XXX:Change it to true to show SQL errors. + $debug = true; // Change it to false to squelch SQL errors. //Last minute checks. - if(!file_exists($schemaFile) || !($fp = fopen($schemaFile, 'rb'))) - $this->errors['err']='Internal Error - please make sure your download is the latest (#1)'; - elseif( - !($signature=trim(file_get_contents("$schemaFile.md5"))) - || !($hash=md5(fread($fp, filesize($schemaFile)))) - || strcasecmp($signature, $hash)) - $this->errors['err']='Unknown or invalid schema signature (' - .$signature.' .. '.$hash.')'; - elseif(!file_exists($this->getConfigFile()) || !($configFile=file_get_contents($this->getConfigFile()))) + if(!file_exists($this->getConfigFile()) || !($configFile=file_get_contents($this->getConfigFile()))) $this->errors['err']='Unable to read config file. Permission denied! (#2)'; elseif(!($fp = @fopen($this->getConfigFile(),'r+'))) $this->errors['err']='Unable to open config file for writing. Permission denied! (#3)'; - elseif(!$this->load_sql_file($schemaFile,$vars['prefix'], true, $debug)) - $this->errors['err']='Error parsing SQL schema! Get help from developers (#4)'; + + else { + foreach (DatabaseMigrater::getUpgradeStreams(INCLUDE_DIR.'upgrader/streams/') + as $stream=>$signature) { + $schemaFile = INC_DIR."streams/$stream/install-mysql.sql"; + if (!file_exists($schemaFile) || !($fp2 = fopen($schemaFile, 'rb'))) + $this->errors['err'] = $stream + . ': Internal Error - please make sure your download is the latest (#1)'; + elseif ( + // TODO: Make the hash algo configurable in the streams + // configuration ( core : md5 ) + !($hash = md5(fread($fp2, filesize($schemaFile)))) + || strcasecmp($signature, $hash)) + $this->errors['err'] = $stream + .': Unknown or invalid schema signature (' + .$signature.' .. '.$hash.')'; + elseif (!$this->load_sql_file($schemaFile, $vars['prefix'], true, $debug)) + $this->errors['err'] = $stream + .': Error parsing SQL schema! Get help from developers (#4)'; + } + } $sql='SELECT `id` FROM '.PREFIX.'sla ORDER BY `id` LIMIT 1'; $sla_id_1 = db_result(db_query($sql, false), 0); @@ -173,6 +184,7 @@ class Installer extends SetupWizard { //Create config settings---default settings! //XXX: rename ostversion helpdesk_* ?? + // XXX: Some of this can go to the core install file $defaults = array('isonline'=>'0', 'default_email_id'=>$support_email_id, 'alert_email_id'=>$alert_email_id, 'default_dept_id'=>$dept_id_1, 'default_sla_id'=>$sla_id_1, 'default_timezone_id'=>$eastern_timezone, 'default_template_id'=>$template_id_1, diff --git a/setup/inc/sql/osTicket-mysql.sql b/setup/inc/streams/core/install-mysql.sql similarity index 100% rename from setup/inc/sql/osTicket-mysql.sql rename to setup/inc/streams/core/install-mysql.sql