예제 #1
0
		/**
		 * Imports the SQL statements from the db_sync.sql file, or from the file provided through file upload.
		 * This function can only be called from the Symphony backend, the CDI extension must be enabled and running in Database Synchronisation mode.
		 */
		public static function import() {
			// We should not be processing any queries when the extension is disabled or when it is in 'Continuous Database Integration' mode
			if((!class_exists('Administration'))  || !CdiUtil::isEnabled() || !CdiUtil::isCdiDBSyncSlave()) {
			   	throw new Exception("You can only import the Database Synchroniser file from the Preferences page. The CDI extension must be enabled and should be a 'Slave' instance in 'Database Synchronisation' mode.");
			}
			
			// Prevent the CdiLogQuery::log() from persisting queries that are executed by CDI itself
			// This should not be possible anyway because we can only import in "Slave" mode, but just to be sure!
			CdiLogQuery::isUpdating(true);

			// Handle file upload
			$syncFile = CDI_DB_SYNC_FILE;
			if(!empty($_FILES['cdi_import_file']['tmp_name'])) {
				$syncFile = $_FILES['cdi_import_file']['tmp_name'];
			}
			
			//Execute the queries from file
			try {
				if(file_exists($syncFile)) {
					$contents = file_get_contents($syncFile);
					$queries = explode(';',$contents);
					foreach($queries as $query) {
						$query = trim($query);
						// ommit comments and empty statements
						if(!preg_match('/^--/i', $query) && !$query=='') {
							Symphony::Database()->query($query);
						}
					}
					
					if(isset($_POST['settings']['cdi']['deleteSyncFile'])) {
						unlink($syncFile);
					}
				}
			} catch (Exception $e) {
				// Re-enable CdiLogQuery::log() to persist queries
				CdiLogQuery::isUpdating(false);
				throw $e;
			}

			// Save the last update date to configuration
			Symphony::Configuration()->set('last-update', time(), 'cdi');
			Symphony::Configuration()->write();
			
			// Re-enable CdiLogQuery::log() to persist queries
			CdiLogQuery::isUpdating(false);
		}
예제 #2
0
		/**
		 * The CdiQueryLog::log() function is called from the Database implementation responsible for executing Symphony SQL queries
		 * If in MASTER mode, CDI will save the query to disk allowing it to be committed to the VCS. From there it will be available
		 * for automatic query exection by CDI slave instances (see also CdiLogQuery::executeQueries()).
		 * @param String $query
		 */
		public static function log($query) {
			// Prevent execution on the frontend and check configuration conditions
			// Do not log the query when CDI is disabled, in SLAVE mode or busy executing queries.
			// Additionally if the logger is not installed, you should not be able to call this function
			if((!class_exists('Administration')) || !CdiUtil::isEnabled() || self::$isUpdating) {
				return true;
			}
			
			$query = trim($query);
			$tbl_prefix = Symphony::Configuration()->get('tbl_prefix', 'database');
	
			/* FILTERS */
			// do not register changes to tbl_cdi_log
			if (preg_match("/{$tbl_prefix}cdi_log/i", $query)) return true;
			// only structural changes, no SELECT statements
			if (!preg_match('/^(insert|update|delete|create|drop|alter|rename)/i', $query)) return true;
			// un-tracked tables (sessions, cache, authors)
			if (preg_match("/{$tbl_prefix}(authors|cache|forgotpass|sessions)/i", $query)) return true;
			// content updates in tbl_entries (includes tbl_entries_fields_*)
			if (preg_match('/^(insert|delete|update)/i', $query) && preg_match("/({$config->tbl_prefix}entries)/i", $query)) return true;
			// append query delimeter if it doesn't exist
			if (!preg_match('/;$/', $query)) $query .= ";";

			// Replace the table prefix in the query
			// This allows query execution on slave instances with different table prefix.
			$query = str_replace($tbl_prefix,'tbl_',$query);
			
			// We've come far enough... let's try to save it to disk!
			if(CdiUtil::isCdiMaster()) {
				return CdiMaster::persistQuery($query);
			} else if(CdiUtil::isCdiDBSyncMaster()) {
				return CdiDBSync::persistQuery($query);
			} else {
				//TODO: error handling for the unusual event that we are dealing with here.
				return true;
			}
		}
예제 #3
0
		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);
			}
		}
예제 #4
0
<?php 

	$result = array();
	require_once(EXTENSIONS . '/cdi/lib/class.cdiutil.php');
	require_once(EXTENSIONS . '/cdi/lib/class.cdimaster.php');
	require_once(EXTENSIONS . '/cdi/lib/class.cdislave.php');
	require_once(EXTENSIONS . '/cdi/lib/class.cdidbsync.php');
	require_once(EXTENSIONS . '/cdi/lib/class.cdidumpdb.php');
	require_once(EXTENSIONS . '/cdi/lib/class.cdilogquery.php');
	require_once(EXTENSIONS . '/cdi/lib/class.cdipreferences.php');
	
	// We should not be processing any queries when the extension is disabled or when we are the Master instance
	if((!class_exists('Administration')) || !CdiUtil::isEnabled()) {
	   	$result["status"] = "error";
	   	$result["message"] = "You can only execute actions from within Symphony and when the CDI extension is enabled";
		Symphony::Log()->pushToLog('[CDI] You can only execute actions from within Symphony and when the CDI extension is enabled', E_NOTICE, true);
	} 
	
	// Clean the database and log files when the cdi_clear action is called
	if(isset($_POST["action"]["cdi_clear"])) {
		try {
			if(CdiUtil::isCdiMaster()) {
				CdiMaster::uninstall();
				CdiMaster::install();
			} else if (CdiUtil::isCdiSlave()) {
				CdiSlave::uninstall();
				CdiSlave::install();
			} else if(CdiUtil::isCdiDBSync()) {
				CdiDBSync::uninstall();
				CdiDBSync::install();
			}
예제 #5
0
<?php


	// We should only allow download of the database from the administration interface when the extension is enabled.
	if((!class_exists('Administration'))  || !Administration::instance()->isLoggedIn() || !CdiUtil::isEnabled()) {
	   	throw new Exception("You can only download CDI content from the Preferences page");
	}

	require_once(EXTENSIONS . '/cdi/lib/class.cdiutil.php');
	
	$filename = $_REQUEST["ref"];
	if($filename == CDI_FILENAME || $filename == CDI_DB_SYNC_FILENAME) {
		$file = CDIROOT . '/' . $filename;
	} else {
		$file = CDI_BACKUP_ROOT . '/' . $filename;
	}
	
	if(file_exists($file)) {
		$data = file_get_contents($file);

		header("Pragma: public");
		header("Expires: 0");
		header("Cache-Control: must-revalidate, post-check=0, pre-check=0");

		header("Content-Type: application/octet-stream");
		header("Content-Transfer-Encoding: binary");
		header("Content-Disposition: attachment; filename=" . $filename);
		echo $data;
		die();
	} else {
		throw new Exception("The provided backup file '" . $file . "' could not be found.");
예제 #6
0
<?php 
	require_once(EXTENSIONS . '/cdi/lib/class.cdiutil.php');
	require_once(EXTENSIONS . '/cdi/lib/class.cdislave.php');

	// We should not be processing any queries when the extension is disabled or when we are the Master instance
	if((!class_exists('Administration')) || !CdiUtil::isEnabled() || (CdiUtil::isCdiMaster() || CdiUtil::isCdiDBSync())) {
		echo "WARNING: You are not calling this page from Symphony, the CDI extension is disabled or you are running the queryies on the Master instance. No queries have been executed.";
	} else {
		$callback = Administration::getPageCallback();
		if(Symphony::Configuration()->get('api_key','cdi') !== $callback['context'][0]){
			echo "WARNING: Invalid API key. The correct key can be found in the configuration page.";
			die();
		}
		else{
			CdiSlave::update();
		}
	}
	
	die();
예제 #7
0
		/**
		 * 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);
		}