using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Data;
using System.Threading;
using System.Text.RegularExpressions;
namespace Bookings_And_Loans.Database
{
/// <summary>
/// This file will handle the backup aspect of the system
/// </summary>
class csBackupSystem
{
//Declare the variables I will be using within this file
private static bool isJournalEnabled = true;
private static string backupLocation_;
public static string backupLocation
{
//When accessed, return the value of backupLocation_
get { return backupLocation_; }
set
{
//When attempting to set the value of this variable, move all existing backups to the new folder
if (backupLocation_!= null) { MoveAllExistingBackups(backupLocation_, value); }
//Set the journal file to the new location
journalFile = value + "\\journal.txt";
//Save the settings
Properties.Settings.Default.BackupLocation = value;
Properties.Settings.Default.Save();
backupLocation_ = value;
}
}
private static string journalFile;
public static void Initialize()
{
//A method to start all aspects directly related to the backups system
backupLocation = Properties.Settings.Default.BackupLocation;
if (!Directory.Exists(backupLocation))
{ Directory.CreateDirectory(backupLocation); }
//Add the eventhandlers for each of the tables to be covered by the backup system
for (int repeat = 0; repeat < csDatabase.dbBookings.Tables.Count; repeat++)
{
csDatabase.dbBookings.Tables[repeat].RowChanged += new System.Data.DataRowChangeEventHandler(csBackupSystem_RowChanged);
csDatabase.dbBookings.Tables[repeat].RowDeleting += new System.Data.DataRowChangeEventHandler(csBackupSystem_RowChanged);
csDatabase.dbBookings.Tables[repeat].TableCleared += new DataTableClearEventHandler(csBackupSystem_TableCleared);
}
//Call the procedure which will check if a new backup is required
CheckBackup();
}
static void csBackupSystem_TableCleared(object sender, DataTableClearEventArgs e)
{
//Create a string to reflect that a table was cleared within the database
string lineToLog;
lineToLog = e.Table.TableName + "><" + "Clear" + "><" + "%%";
LogChange(lineToLog);
}
private static void CheckBackup()
{
//Start a new thread, to avoid making the program unresponsive while checking/backing up
new Thread(delegate()
{
//Create an unending loop, which will pause for 12hours after each run through
while (true)
{
//Get a list of backups and find the latest one
string[] backupFolders = Directory.GetDirectories(backupLocation);
string latestBackup = DateTime.Today.AddDays(-8).ToString("yyyy-MM-dd");
if (backupFolders.Length > 0)
{ latestBackup = backupFolders[backupFolders.Length - 1].Split('\\')[backupFolders[backupFolders.Length - 1].Split('\\').Length-1]; }
DateTime dateOfLastBackup = DateTime.Parse(latestBackup);
//Compare todays date to the last backup, and if the last backup was seven days ago, make a new backup
if (DateTime.Compare(dateOfLastBackup.AddDays(7), DateTime.Today).Equals(-1))
{
CreateBackup();
}
int nextCheckInterval = (((60 * 1000) * 60) * 12);
Thread.Sleep(nextCheckInterval);
}
}) { IsBackground = true }.Start();
}
private static void CreateBackup()
{
//Create the directory to backup to and get a list of files to backup
Directory.CreateDirectory(backupLocation + "\\" + DateTime.Today.ToString("yyyy-MM-dd"));
string[] filesToBackup = new string[] { "bookings.txt", "loans.txt", "archived_loans,txt",
"archived_bookings.txt", "users.xml", "journal.txt" };
//Foreach file to backup, copy it to the backup folder
for (int repeat = 0; repeat < filesToBackup.Length; repeat++)
{
string fileToCopy = filesToBackup[repeat];
if (File.Exists(fileToCopy))
{
File.Copy(fileToCopy, backupLocation + "\\" + DateTime.Today.ToString("yyyy-MM-dd") + "\\" + fileToCopy);
}
}
//Delete the journal file as all recorded data is no longer needed
File.Delete(journalFile);
}
private static void csBackupSystem_RowChanged(object sender, System.Data.DataRowChangeEventArgs e)
{
//If the table being added to is an archive table, set the archive date of the record
if (e.Action.ToString().Equals("Add") && e.Row.Table.TableName.StartsWith("archived"))
{ e.Row["Date Archived"] = DateTime.Now; }
string lineToLog;
//Create the string which is to be recorded, including table, action and all values after the change in the row
lineToLog = e.Row.Table.TableName + "><" + e.Action + "><";
for (int repeat = 0; repeat < e.Row.ItemArray.Length; repeat++)
{
lineToLog += "%%" + e.Row.ItemArray[repeat];
}
//Log the change
LogChange(lineToLog);
}
private static void LogChange(string changeToLog)
{
//If the journal file is active, write the change to the database
if (isJournalEnabled)
{
StreamWriter journalWriter = File.AppendText(journalFile);
journalWriter.WriteLine(changeToLog);
journalWriter.Flush();
journalWriter.Close();
}
}
public static void RestoreLastBackup()
{
//Find the latest backup and restore it
string[] backupFolders = Directory.GetDirectories(backupLocation);
string latestBackup = DateTime.Today.AddDays(-8).ToString("dd-MM-yyyy");
if (backupFolders.Length > 0)
{ latestBackup = backupFolders[backupFolders.Length - 1].Split('\\')[1]; }
RestoreBackup(latestBackup);
}
public static void RestoreBackup(string folderToUse)
{
//Make sure the journal doesn't log the changes made when restoring the backup
isJournalEnabled = false;
csDatabase.ClearAllTables();
string[] filesToRestore = new string[] { "bookings.txt", "loans.txt", "archived_loans,txt", "archived_bookings.txt", "users.xml" };
for (int repeat = 0; repeat < filesToRestore.Length; repeat++)
{
string fileToRestore = filesToRestore[repeat];
if (File.Exists(backupLocation + "\\" + folderToUse + "\\" + fileToRestore))
{
File.Copy(backupLocation + "\\" + folderToUse + "\\" + fileToRestore, fileToRestore, true);
csDatabase.LoadTable(fileToRestore.Split('.')[0]);
}
}
//Restore all changes recorded in the journal
RestoreJournal();
isJournalEnabled = true;
}
private static void RestoreJournal()
{
//If the journal file exists, continue
if (File.Exists(journalFile))
{
StreamReader journalReader = new StreamReader(journalFile);
string line = "";
while ((line = journalReader.ReadLine()) != null)
{
try
{
//Set the variables which will hold the data required by the system to restore each change
string tableToAffect = Regex.Split(line, "><")[0];
string actionToPerform = Regex.Split(line, "><")[1];
string[] fieldValues = Regex.Split(Regex.Split(line, "><")[2], "%%");
string idColumn = csDatabase.dbBookings.Tables[tableToAffect].Columns[0].ColumnName;
DataRow rowToEdit;
switch (actionToPerform)
{
case "Change":
{
rowToEdit = csDatabase.dbBookings.Tables[tableToAffect].Select("[" + idColumn + "] = " + fieldValues[1])[0];
for (int a = 0; a < rowToEdit.Table.Columns.Count; a++)
{
string columnToEdit = rowToEdit.Table.Columns[a].ColumnName;
rowToEdit[columnToEdit] = fieldValues[a + 1];
}
break;
}
case "Add":
{
rowToEdit = csDatabase.dbBookings.Tables[tableToAffect].NewRow();
for (int a = 0; a < rowToEdit.Table.Columns.Count; a++)
{
string columnToEdit = rowToEdit.Table.Columns[a].ColumnName;
string dataTypeOfColumn = rowToEdit.Table.Columns[a].DataType.ToString();
rowToEdit[columnToEdit] = fieldValues[a + 1];
}
csDatabase.dbBookings.Tables[tableToAffect].Rows.Add(rowToEdit);
break;
}
case "Delete":
{
rowToEdit = csDatabase.dbBookings.Tables[tableToAffect].Select("[" + idColumn + "] = " + fieldValues[1])[0];
csDatabase.dbBookings.Tables[tableToAffect].Rows.Remove(rowToEdit);
break;
}
case "Clear":
{
csDatabase.dbBookings.Tables[tableToAffect].Clear();
break;
}
}
}
catch (Exception e) { }
}
}
}
private static void MoveAllExistingBackups(string existingFolder, string newFolder)
{
//Get a list of existing backups
string[] backupFolders = Directory.GetDirectories(existingFolder);
if (File.Exists(existingFolder + "\\journal.txt")) { File.Copy(existingFolder + "\\journal.txt", newFolder + "\\journal.txt"); }
//For each existing backup
for (int repeat = 0; repeat < backupFolders.Length; repeat++)
{
//Get a list of files and move them to the relevant new directory
string[] backupFiles = Directory.GetFiles(backupFolders[repeat]);
string folderName = Regex.Split(backupFolders[repeat], "\\\\")[Regex.Split(backupFolders[repeat], "\\\\").Length - 1];
Directory.CreateDirectory(newFolder + "\\" + folderName);
for (int repeat_ = 0; repeat_ < backupFiles.Length; repeat_++)
{
string destination = newFolder + "\\" + Regex.Match(backupFiles[repeat_], "([0-9]*(-)?){3}\\\\[a-z]*\\.[a-z]{3}").Value;
File.Copy(backupFiles[repeat_], destination);
File.Delete(backupFiles[repeat_]);
}
//Delete the existing backup
Directory.Delete(backupFolders[repeat]);
}
}
}
}