public function install() { if(CdiSlave::install()) { if(CdiUtil::hasDumpDBInstalled()) { CdiDumpDB::install(); } } else { return false; } Symphony::Configuration()->set('enabled', 'yes', 'cdi'); Symphony::Configuration()->set('cdi-mode', 'cdi', 'cdi'); Symphony::Configuration()->set('mode', 'CdiSlave', 'cdi'); Symphony::Configuration()->write(); return true; }
public static function restore() { // We should only backup the database when the extension is enabled and version 1.09 of the Dump_DB extension is installed. if((!class_exists('Administration')) || !CdiUtil::isEnabled()) { throw new Exception("You can only restore the Symphony database from the Preferences page"); } if(!CdiUtil::hasDumpDBInstalled()) { throw new Exception('No valid version of <a href="http://symphony-cms.com/download/extensions/view/40986/">Dump DB</a> found. Please make sure it is installed.'); } else { require_once(EXTENSIONS . '/dump_db/lib/class.mysqlrestore.php'); // Prevent the CdiLogQuery::log() from persisting queries that are executed by CDI itself CdiLogQuery::isUpdating(true); // COPIED FROM Dump_DB version 1.09 // Adjust to only support FULL database dump $restore = new MySQLRestore(Symphony::Database()); $filename = CDI_BACKUP_ROOT . '/' . $_POST["ref"]; if(isset($_FILES['dumpdb_restore_file'])) { $filename = self::getFileName('manual'); rename($_FILES['dumpdb_restore_file']['tmp_name'],$filename); } if(file_exists($filename)) { $data = file_get_contents($filename); $data = str_replace('tbl_', Symphony::Configuration()->get('tbl_prefix', 'database'), $data); $restore->import($data); } else { throw new Exception("The provided restore file '" . $filename . "' could not be found."); } // Re-enable CdiLogQuery::log() to persist queries CdiLogQuery::isUpdating(false); } }
public static function appendDBExport() { $div = new XMLElement('div', NULL, array('class' => 'cdiExport')); $div->appendChild(new XMLElement('h3','Export current Symphony database',array('style' => 'margin-bottom: 5px;'))); if(!CdiUtil::hasDumpDBInstalled()) { $div->appendChild(new XMLElement('p', 'To enable database export functionality you need to install the <a href="http://symphony-cms.com/download/extensions/view/40986/">Dump DB</a> extension (version 1.10 or higher)')); } else { $button = new XMLElement('div',NULL,array('style' => 'margin: 10px 0;')); $button->appendChild(new XMLElement('input',null,array('value' => 'Export', 'name' => 'action[cdi_export]', 'type' => 'button', 'class' => 'cdi_export_action'))); $button->appendChild(new XMLElement('span',' Press "Export" to create a full backup of the Symphony Database.')); $div->appendChild($button); $label = Widget::Label(); $label->setAttribute('style','margin: -12px 0 12px 62px;position:relative;padding-left:18px;'); $input = Widget::Input('settings[cdi][manual-backup-overwrite]', 'yes', 'checkbox'); $input->setAttribute('style','position:absolute;left:0px;'); $input->setAttribute('class','manual-backup-overwrite'); if(Symphony::Configuration()->get('manual-backup-overwrite', 'cdi') == 'yes') { $input->setAttribute('checked', 'checked'); } $label->setValue($input->generate() . ' Overwrite existing backup file'); $div->appendChild($label); $div->appendChild(new XMLElement('p', 'You can use the export to synchronise your databases between environments. Be advised: this will copy all data. If your production environment has user-generated content you need to be carefull for data loss.', array('class' => 'help'))); } return $div; }
/** * The executeQueries() function will try to execute all SQL statements that are available in the manifest folder. * It will check the CDI log to see if the statement has already been executed based on the MD5 hash. * This function can only be called by SLAVE instances */ public static function update() { // We should not be processing any queries when the extension is disabled or when we are the Master instance // Check also exists on content page, but just to be sure! if((!class_exists('Administration')) || !CdiUtil::isEnabled() || !CdiUtil::isCdiSlave()) { echo "WARNING: CDI is disabled or you are running the queryies on the Master instance. No queries have been executed."; return; } // Prevent the CdiLogQuery::log() from persisting queries that are executed by CDI itself // Technically this should not be possible because it will not log queries on a SLAVE instance // and you can only run the executeQueries when in SLAVE mode. This is just to be sure. CdiLogQuery::isUpdating(true); // Switch to maintenance mode if(Symphony::Configuration()->get('maintenance-enabled', 'cdi') == 'yes') { Symphony::Configuration()->set('enabled', 'yes', 'maintenance_mode'); Symphony::Configuration()->write(); } // Implement automatic backup before processing structural changes if(Symphony::Configuration()->get('backup-enabled', 'cdi') == 'yes') { try { if(CdiUtil::hasDumpDBInstalled()) { require_once(EXTENSIONS . '/cdi/lib/class.cdidumpdb.php'); $currentBackup = CdiDumpDB::backup("automatic"); } else { throw new Exception('You can only enable automatic backup files when the "Dump DB" extension (version 1.10 or higher) is installed and enabled.'); } }catch (Exception $e) { echo "ERROR: " . $e->getMessage() , ". Failed to backup database before update, aborting."; die(); } } try { $skipped = 0; $executed = 0; $entries = CdiLogQuery::getCdiLogEntries(); foreach($entries as $entry) { $ts = $entry[0]; $order = $entry[1]; $hash = $entry[2]; $query = $entry[3]; $date = date('Y-m-d H:i:s', $ts); // Replace the table prefix in the query // Rename the generic table prefix to the prefix used by this instance $tbl_prefix = Symphony::Configuration()->get('tbl_prefix', 'database'); $query = str_replace('tbl_', $tbl_prefix, $query); try { // Look for available CDI log entries based on the provided MD5 hash $cdiLogEntry = Symphony::Database()->fetchRow(0,"SELECT * FROM tbl_cdi_log WHERE `query_hash` LIKE '" . $hash . "'"); if(empty($cdiLogEntry)) { // The query has not been found in the log, thus it has not been executed // So let's execute the query and add it to the log! Symphony::Database()->query("INSERT INTO `tbl_cdi_log` (`query_hash`,`author`,`url`,`date`,`order`) VALUES ('" . $hash . "','" . CdiUtil::getAuthor() . "','" . CdiUtil::getURL() . "','" . $date . "'," . $order . ")"); Symphony::Database()->query($query); $executed++; } else { // The query has already been executed, let's do nothing; $skipped++; } } catch (Exception $e) { //TODO: think of some smart way of dealing with errors, perhaps through the preference screen or a CDI Status content page? //Due to the error we need to perform a rollback to allow this query to be executed at a later stage. CdiLogQuery::rollback($hash,$ts,$order); // Implement automatic restore on error if((Symphony::Configuration()->get('backup-enabled', 'cdi') == 'yes') && (Symphony::Configuration()->get('restore-enabled', 'cdi') == 'yes')) { try { CdiDumpDB::restore($currentBackup); }catch (Exception $r) { echo "ERROR: " . $r->getMessage() , ". Failed to restore latest database backup. This instance needs immediate attention!"; die(); } // Restore was succesful, at least we can jump out of maintenance mode if(Symphony::Configuration()->get('maintenance-enabled', 'cdi') == 'yes') { Symphony::Configuration()->set('enabled', 'no', 'maintenance_mode'); Symphony::Configuration()->write(); } echo "ERROR: " . $e->getMessage() , ". Rollback & Restore have been executed."; } else { if(Symphony::Configuration()->get('maintenance-enabled', 'cdi') == 'yes') { echo "ERROR: " . $e->getMessage() , ". Rollback has been executed. This instance is now in maintenance mode and needs immediate attention!"; } else { echo "ERROR: " . $e->getMessage() , ". Rollback has been executed."; } } // Stop processing die(); } } echo "OK: " . $executed . " queries executed, " . $skipped . " skipped."; } catch (Exception $e) { //TODO: think of some smart way of dealing with errors, perhaps through the preference screen or a CDI Status content page? echo "ERROR: " . $e->getMessage(); } // Save the last update date to configuration Symphony::Configuration()->set('last-update', time(), 'cdi'); // No more maintenance mode if(Symphony::Configuration()->get('maintenance-enabled', 'cdi') == 'yes') { Symphony::Configuration()->set('enabled', 'no', 'maintenance_mode'); } Symphony::Configuration()->write(); // Re-enable CdiLogQuery::log() to persist queries CdiLogQuery::isUpdating(false); }