updater = $updater; } /** * Execute a system update * * @return int|bool True if successful, false if not. * Or return integer 0 if SystemUpdate[n].php will handle updating the system version when it is ready. * When false or 0 is returned, updates will stop being applied for that request. * */ abstract public function execute(); public function getName() { $name = get_class($this); $name = str_replace('SystemUpdate', '', $name); $name = "Update #$name"; return $name; } public function message($text, $flags = 0) { $text = $this->getName() . ": $text"; $this->updater->message($text, $flags); return $this; } public function error($text, $flags = 0) { $text = $this->getName() . " ERROR: $text"; $this->updater->error($text, $flags); return $this; } } /** * SystemUpdater module * */ class SystemUpdater extends WireData implements Module, ConfigurableModule { public static function getModuleInfo() { return array( 'title' => __('System Updater', __FILE__), // Module Title 'summary' => __('Manages system versions and upgrades.', __FILE__), // Module Summary 'permanent' => true, 'singular' => true, 'autoload' => false, /** * This version number is important, as this updater keeps the systemVersion up with this version * */ 'version' => 8, ); } protected $configData = array( // systemVersion generally represents the DB schema version, but // can represent anything about the system that's related to the individual installation. // 0 = the first version when this module was created, should remain there. 'systemVersion' => 0, ); /** * Number of updates that were applied during this request * */ protected $numUpdatesApplied = 0; /** * Part of the ConfigurableModule interface, sets config data to the module * */ public function setConfigData(array $data) { $this->configData = array_merge($this->configData, $data); } /** * Perform version checks and update as needed * */ public function init() { $config = wire('config'); $info = self::getModuleInfo(); $moduleVersion = $info['version']; foreach($this->configData as $key => $value) { $config->$key = $value; } while($config->systemVersion < $moduleVersion) { // apply the incremental version update if(!$this->update($config->systemVersion+1)) break; // we increment the config systemVersion so that the version is also available to the updater $config->systemVersion++; // we save the configData for every version in case an update throws an exception // then already applied updates won't be applied again $this->saveSystemVersion($config->systemVersion); $this->numUpdatesApplied++; } if($this->numUpdatesApplied > 0) { // if updates were applied, reset the modules cache $this->modules->resetCache(); } } /** * Save the system version as the given version number * * @param int $version * */ public function saveSystemVersion($version) { $version = (int) $version; $this->wire('config')->systemVersion = $version; $this->configData['systemVersion'] = $version; $this->wire('modules')->saveModuleConfigData($this, $this->configData); $this->message("Update #$version: Completed!"); } /** * Check for an update file in the format: SystemUpdater123 where '123' is the version it upgrades to * * If found, instantiate the class and it's constructor should perform the update or add any hooks necessary to perform the update * */ protected function update($version) { $className = 'SystemUpdate' . $version; $filename = $this->config->paths->SystemUpdater . $className . '.php'; $errorMessage = sprintf('Failed to apply update %d', $version); if(is_file($filename)) { try { include($filename); $update = new $className($this); $update->message('Initializing update'); $success = $update->execute(); } catch(Exception $e) { $msg = $errorMessage . " - " . $e->getMessage(); if($update) $update->error($msg); else $this->error($msg); $success = false; } if(!$success) { if($success === false) $update->error($errorMessage); return false; } } return true; } public function message($text, $flags = 0) { $this->log($text); return parent::message($text, $flags); } public function error($text, $flags = 0) { $text = "ERROR: $text"; $this->log($text); return parent::error($text, $flags); } /** * Log a message to system-updater.txt log file * */ public function log($text) { $options = array('showUser' => false, 'showPage' => false); $this->wire('log')->save('system-updater', $text, $options); } /** * Required for ConfigurableModule interface * */ public static function getModuleConfigInputfields(array $data) { $inputfields = new InputfieldWrapper(); $logfile = wire('config')->paths->logs . 'system-updater.txt'; if(is_file($logfile)) { $f = wire('modules')->get('InputfieldMarkup'); $f->attr('name', '_log'); $f->label = __('System Update Log'); $f->value = '
' . wire('sanitizer')->entities(file_get_contents($logfile)) . '';
$inputfields->add($f);
}
$f = wire('modules')->get('InputfieldInteger');
$f->attr('name', 'systemVersion');
$f->label = __('System Version');
$f->description = __('This lets you re-apply a system version update by reducing the version number.');
$f->attr('value', $data['systemVersion']);
$inputfields->add($f);
return $inputfields;
}
}