191202PX Added namespace dezentrale.core for the code running in background (thus, completing the mvc structure)

This commit is contained in:
phantomix 2019-12-02 17:23:35 +01:00
parent 7a65a110fc
commit 20c32ab7a3
12 changed files with 524 additions and 485 deletions

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
using dezentrale.core;
using dezentrale.model;
using dezentrale.model.money;
using dezentrale.view;
@ -31,7 +32,7 @@ namespace dezentrale
{
public class Program
{
public static uint VersionNumber { get; private set; } = 0x19120100;
public static uint VersionNumber { get; private set; } = 0x19120200;
public static string VersionString { get; private set; } = $"{VersionNumber:x}";
public static string AppData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
@ -184,10 +185,23 @@ namespace dezentrale
if (!config.ImportExport.VerifyExport(config.DbDirectory, DmDirectory)) return 1;
break;
case eMode.Import:
{
ImportProcess import = new ImportProcess()
{
MemberImportSettings = Program.config.ImportExport,
MemberDir = Program.config.DbDirectory,
InputDir = Program.DmDirectory,
};
ConsoleLogger logger = new ConsoleLogger();
System.Threading.Thread t = logger.StartRunProcess(import);
if (t != null) t.Join();
else return 1;
if (!config.ImportExport.Import(config.DbDirectory, DmDirectory))
return 1;
break;
//frmProcessWithLog frmImport = new frmProcessWithLog(import, true);
//if (!config.ImportExport.Import(config.DbDirectory, DmDirectory))
//return 1;
if(logger.DialogResult != DialogResult.OK) return 1;
} break;
case eMode.BankImport:
if (string.IsNullOrEmpty(csvInput))
{

50
core/BackgroundProcess.cs Normal file
View File

@ -0,0 +1,50 @@
using System;
using System.Diagnostics;
using System.Threading;
namespace dezentrale.core
{
public abstract class BackgroundProcess
{
public IProcessController LogTarget { get; set; } = null;
public bool CancelButton { get; protected set; } = false;
public string Caption { get; protected set; } = "BackgroundProcess";
public uint Steps { get; protected set; } = 1;
protected abstract bool Run();
private Thread thread = null;
public Thread StartRun()
{
if (thread != null) return null;
thread = new Thread(RunAbstract) { IsBackground = true };
thread.Start();
return thread;
}
private void RunAbstract()
{
LogTarget.ActionCompleted(Run());
}
}
public class HelloWorldProcess : BackgroundProcess
{
public HelloWorldProcess()
{
Caption = "Hello World";
Steps = 20;
}
protected override bool Run()
{
for (uint i = 0; i < 20; i++)
{
string stepName = ((i & 0x01) == 0) ? "Hello" : "World";
LogTarget.StepStarted(i, stepName);
LogTarget.LogLine(stepName, dezentrale.model.LogEvent.ELogLevel.Info, "HelloWorldProcess");
Thread.Sleep(200);
LogTarget.StepCompleted(i, stepName, (i & 0x01) == 0);
Thread.Sleep(200);
}
return true;
}
}
}

128
core/ExportProcess.cs Normal file
View File

@ -0,0 +1,128 @@
using System;
using System.IO;
using ICSharpCode.SharpZipLib.Zip;
using dezentrale.model;
namespace dezentrale.core
{
public class ExportProcess : ImportExportBase
{
public ExportProcess()
{
Caption = "Database export";
Steps = 4;
}
protected override bool Run()
{
string tmpFile = Path.Combine(OutputDir, MemberImportSettings.ZipFile);
Console.WriteLine($"Export: Packing {tmpFile}");
if (!Directory.Exists(MemberDir))
{
Console.WriteLine($"Cannot find directory '{MemberDir}'");
return false;
}
if (!Directory.Exists(OutputDir))
{
Console.WriteLine($"Cannot find directory '{OutputDir}'");
return false;
}
try
{
// Depending on the directory this could be very large and would require more attention
// in a commercial package.
string[] filenames = Directory.GetFiles(MemberDir, "*.xml");
string outFile = Path.Combine(OutputDir, MemberImportSettings.ZipFile);
if (File.Exists(outFile))
{
string backupName = $"{outFile}.bak";
if (File.Exists(backupName)) File.Delete(backupName);
File.Move(outFile, backupName);
}
// 'using' statements guarantee the stream is closed properly which is a big source
// of problems otherwise. Its exception safe as well which is great.
using (ZipOutputStream s = new ZipOutputStream(File.Create(outFile)))
{
s.SetLevel(9); // 0 - store only to 9 - means best compression
s.Password = MemberImportSettings.ZipPassword; //null is a desired value for "no encryption"
byte[] buffer = new byte[4096];
foreach (string file in filenames)
{
Console.WriteLine(file);
// Using GetFileName makes the result compatible with XP
// as the resulting path is not absolute.
var entry = new ZipEntry(Path.GetFileName(file));
// Setup the entry data as required.
// Crc and size are handled by the library for seakable streams
// so no need to do them here.
// Could also use the last write time or similar for the file.
entry.DateTime = DateTime.Now;
s.PutNextEntry(entry);
using (FileStream fs = File.OpenRead(file))
{
// Using a fixed size buffer here makes no noticeable difference for output
// but keeps a lid on memory usage.
int sourceBytes;
do
{
sourceBytes = fs.Read(buffer, 0, buffer.Length);
s.Write(buffer, 0, sourceBytes);
} while (sourceBytes > 0);
}
}
// Finish/Close arent needed strictly as the using statement does this automatically
// Finish is important to ensure trailing information for a Zip file is appended. Without this
// the created file would be invalid.
s.Finish();
// Close is important to wrap things up and unlock the file.
s.Close();
}
}
catch (Exception ex)
{
Console.WriteLine($"Exception during export: {ex.Message}");
// No need to rethrow the exception as for our purposes its handled.
return false;
}
if (MemberImportSettings.GpgEnabled)
{
Console.WriteLine($"Export: Using GPG to encrypt {Path.Combine(OutputDir, MemberImportSettings.GpgFile)}");
if (!Gpg($"--output \"{Path.Combine(OutputDir, MemberImportSettings.GpgFile)}\" -c \"{tmpFile}\"", MemberImportSettings.GpgPassword)) return false;
tmpFile = Path.Combine(OutputDir, MemberImportSettings.GpgFile);
}
if (MemberImportSettings.HgEnabled)
{
Console.WriteLine($"Export: Using HG to commit / push {tmpFile}");
//this might fail as repo might be existing / file might be already in repo
RunProcess("hg", "init", OutputDir);
RunProcess("hg", $"add {tmpFile}", OutputDir);
//now, committing is more interesting
if (!RunProcess("hg",
"commit"
+ $" --message \"dezentrale-members.exe --mode=export\nProgram version={Program.VersionString}\""
+ $" --user \"{MemberImportSettings.HgUserName}\"", OutputDir))
return false;
if (!RunProcess("hg", $"--config auth.rc.prefix={MemberImportSettings.HgURL} --config auth.rc.username={MemberImportSettings.HgUserName} --config auth.rc.password={MemberImportSettings.HgPassword} push {MemberImportSettings.HgURL}", OutputDir))
return false;
}
return true;
}
}
}

13
core/ILogger.cs Normal file
View File

@ -0,0 +1,13 @@
using System;
using dezentrale.model;
namespace dezentrale.core
{
public interface ILogger
{
void Clear();
void LogRaw(string text);
void LogLine(string text, LogEvent.ELogLevel logLevel = LogEvent.ELogLevel.Info, string module = "");
}
}

View File

@ -0,0 +1,11 @@
using System;
namespace dezentrale.core
{
public interface IProcessController : ILogger
{
void ActionCompleted(bool success);
void StepStarted(uint stepNumber = 0, string stepDescription = "Generic");
void StepCompleted(uint stepNumber = 0, string stepDescription = "Generic", bool success = true);
System.Windows.Forms.DialogResult DialogResult { get; set; }
}
}

78
core/ImportExportBase.cs Normal file
View File

@ -0,0 +1,78 @@
using System;
using System.Diagnostics;
using System.Threading;
namespace dezentrale.core
{
public abstract class ImportExportBase : BackgroundProcess
{
public model.MemberImportExport MemberImportSettings { get; set; } = null;
public string MemberDir { get; set; } = null; //! Directory that holds the application settings
public string InputDir { get; set; } = null; //! Import working directory
public string OutputDir { get; set; } = null; //! Export working directory
public static bool Gpg(string args, string gpgPassword)
{
string argsFull = $"--batch --yes --passphrase \"{gpgPassword}\" {args}";
return RunProcess("gpg", argsFull);
}
public static bool RunProcess(string cmd, string args, string workingDir = ".", core.ILogger log = null)
{
try
{
if (log != null)
{
log.LogLine($"Running {cmd}", model.LogEvent.ELogLevel.Info, "RunProcess");
log.LogRaw($"args: {cmd} {args}");
log.LogRaw($"dir: {workingDir}");
}
else
{
Console.WriteLine($"cmd: {cmd} {args}");
Console.WriteLine($"dir: {workingDir}");
}
Process p = new Process();
p.StartInfo.FileName = cmd;
p.StartInfo.Arguments = args;
p.StartInfo.WorkingDirectory = workingDir;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.CreateNoWindow = true;
p.Start();
string stdout = p.StandardOutput.ReadToEnd();
string stderr = p.StandardError.ReadToEnd();
p.WaitForExit();
if (stdout.Length > 0)
{
if (log != null) log.LogRaw(stdout);
else
{
Console.WriteLine("stdout:");
Console.WriteLine(stdout);
Console.WriteLine();
}
}
if (stderr.Length > 0)
{
if (log != null) log.LogRaw(stderr);
else
{
Console.WriteLine("stderr:");
Console.WriteLine(stderr);
Console.WriteLine();
}
}
return p.ExitCode == 0;
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
return false;
}
}
}

149
core/ImportProcess.cs Normal file
View File

@ -0,0 +1,149 @@
using System;
using System.IO;
using ICSharpCode.SharpZipLib.Zip;
using dezentrale.model;
namespace dezentrale.core
{
public class ImportProcess : ImportExportBase
{
public ImportProcess()
{
Caption = "Database import";
Steps = 4;
}
protected override bool Run()
{
LogTarget.StepStarted(0, "Preparing import");
if (MemberImportSettings == null)
{
LogTarget.LogLine("MemberImportExport data class not set.", LogEvent.ELogLevel.Error, "ImportProcess");
return false;
}
if (MemberDir == null)
{
LogTarget.LogLine("Member directory not set.", LogEvent.ELogLevel.Error, "ImportProcess");
return false;
}
if (InputDir == null)
{
LogTarget.LogLine("Input directory not set.", LogEvent.ELogLevel.Error, "ImportProcess");
return false;
}
LogTarget.StepStarted(1, "Fetching repository");
if (MemberImportSettings.HgEnabled)
{
RunProcess("hg", "init", InputDir, LogTarget);
RunProcess("hg", "update null", InputDir, LogTarget);
if (!RunProcess("hg", $"--config auth.rc.prefix={MemberImportSettings.HgURL} --config auth.rc.username={MemberImportSettings.HgUserName} --config auth.rc.password={MemberImportSettings.HgPassword} pull {MemberImportSettings.HgURL}", InputDir, LogTarget))
return false;
if (!RunProcess("hg", $"update", InputDir, LogTarget))
return false;
}
if (MemberImportSettings.GpgEnabled)
{
Console.WriteLine($"Import: Using GPG to decrypt {Path.Combine(InputDir, MemberImportSettings.GpgFile)}");
if (!Gpg($"--output {Path.Combine(InputDir, MemberImportSettings.ZipFile)} --decrypt {Path.Combine(InputDir, MemberImportSettings.GpgFile)}", MemberImportSettings.GpgPassword)) return false;
}
if (!Directory.Exists(MemberDir))
{
LogTarget.LogLine($"Cannot find directory '{MemberDir}'", LogEvent.ELogLevel.Error, "ImportProcess");
return false;
}
if (!Directory.Exists(InputDir))
{
LogTarget.LogLine($"Cannot find directory '{InputDir}'", LogEvent.ELogLevel.Error, "ImportProcess");
return false;
}
try
{
LogTarget.StepStarted(2, $"Backupping Contents of {MemberDir}");
string[] filenames = Directory.GetFiles(MemberDir, "*.xml");
LogTarget.LogLine($"Renaming {filenames.Length} xml files to xml.bak in {MemberDir}", LogEvent.ELogLevel.Info, "ImportProcess");
foreach (string f in filenames)
{
string fileName = Path.GetFileName(f);
string backupName = $"{fileName}.bak";
if (File.Exists(backupName))
{
LogTarget.LogLine($"Deleting previous {backupName}", LogEvent.ELogLevel.Trace, "ImportProcess");
File.Delete(backupName);
}
LogTarget.LogLine($"Renaming {fileName} ({f}) to {backupName}", LogEvent.ELogLevel.Trace, "ImportProcess");
File.Move(fileName, backupName);
LogTarget.LogLine($"Deleting backupped {fileName}", LogEvent.ELogLevel.Trace, "ImportProcess");
File.Delete(fileName);
}
LogTarget.StepStarted(3, $"Unpacking {MemberImportSettings.ZipFile}");
LogTarget.LogLine($"Extracting {Path.Combine(InputDir, MemberImportSettings.ZipFile)}", LogEvent.ELogLevel.Info, "ImportProcess");
string inFile = Path.Combine(InputDir, MemberImportSettings.ZipFile);
using (ZipInputStream s = new ZipInputStream(File.OpenRead(inFile)))
{
s.Password = MemberImportSettings.ZipPassword;
ZipEntry theEntry;
while ((theEntry = s.GetNextEntry()) != null)
{
Console.WriteLine(theEntry.Name);
string directoryName = Path.GetDirectoryName(theEntry.Name);
string fileName = Path.GetFileName(theEntry.Name);
// create directory
if (directoryName.Length > 0)
{
Directory.CreateDirectory(directoryName);
}
if (fileName != String.Empty)
{
using (FileStream streamWriter = File.Create(Path.Combine(MemberDir, theEntry.Name)))
{
int size = 2048;
byte[] data = new byte[2048];
while (true)
{
size = s.Read(data, 0, data.Length);
if (size > 0)
{
streamWriter.Write(data, 0, size);
}
else
{
break;
}
}
}
}
}
}
LogTarget.StepCompleted(3, $"Unpacking {MemberImportSettings.ZipFile}", true);
}
catch (Exception ex)
{
LogTarget.LogLine($"Exception during import: {ex.Message}", LogEvent.ELogLevel.Error, "ImportProcess");
return false;
}
return true;
}
}
}

View File

@ -183,6 +183,13 @@
<SubType>Component</SubType>
</Compile>
<Compile Include="view\frmProcessWithLog.cs" />
<Compile Include="core\ImportProcess.cs" />
<Compile Include="core\ILogger.cs" />
<Compile Include="core\IProcessController.cs" />
<Compile Include="core\BackgroundProcess.cs" />
<Compile Include="view\ConsoleLogger.cs" />
<Compile Include="core\ExportProcess.cs" />
<Compile Include="core\ImportExportBase.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
@ -196,5 +203,8 @@
</ItemGroup>
<ItemGroup />
<ItemGroup />
<ItemGroup>
<Folder Include="core\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -6,159 +6,6 @@ using System.Diagnostics;
namespace dezentrale.model
{
public class ImportProcess : view.BackgroundProcess
{
public MemberImportExport MemberImportSettings { get; set; } = null;
public string MemberDir { get; set; } = null;
public string InputDir { get; set; } = null;
public ImportProcess()
{
Caption = "Database import";
Steps = 4;
}
protected override bool Run()
{
LogTarget.StepStarted(0, "Preparing import");
if (MemberImportSettings == null)
{
LogTarget.LogLine("MemberImportExport data class not set.", LogEvent.ELogLevel.Error, "ImportProcess");
return false;
}
if (MemberDir == null)
{
LogTarget.LogLine("Member directory not set.", LogEvent.ELogLevel.Error, "ImportProcess");
return false;
}
if (InputDir == null)
{
LogTarget.LogLine("Input directory not set.", LogEvent.ELogLevel.Error, "ImportProcess");
return false;
}
LogTarget.StepStarted(1, "Fetching repository");
if (MemberImportSettings.HgEnabled)
{
MemberImportExport.RunProcess("hg", "init", InputDir, LogTarget);
MemberImportExport.RunProcess("hg", "update null", InputDir, LogTarget);
if (!MemberImportExport.RunProcess("hg", $"--config auth.rc.prefix={MemberImportSettings.HgURL} --config auth.rc.username={MemberImportSettings.HgUserName} --config auth.rc.password={MemberImportSettings.HgPassword} pull {MemberImportSettings.HgURL}", InputDir, LogTarget))
return false;
if (!MemberImportExport.RunProcess("hg", $"update", InputDir, LogTarget))
return false;
}
if (MemberImportSettings.GpgEnabled)
{
Console.WriteLine($"Import: Using GPG to decrypt {Path.Combine(InputDir, MemberImportSettings.GpgFile)}");
if (!MemberImportExport.Gpg($"--output {Path.Combine(InputDir, MemberImportSettings.ZipFile)} --decrypt {Path.Combine(InputDir, MemberImportSettings.GpgFile)}", MemberImportSettings.GpgPassword)) return false;
}
if (!Directory.Exists(MemberDir))
{
LogTarget.LogLine($"Cannot find directory '{MemberDir}'", LogEvent.ELogLevel.Error, "ImportProcess");
return false;
}
if (!Directory.Exists(InputDir))
{
LogTarget.LogLine($"Cannot find directory '{InputDir}'", LogEvent.ELogLevel.Error, "ImportProcess");
return false;
}
try
{
LogTarget.StepStarted(2, $"Backupping Contents of {MemberDir}");
string[] filenames = Directory.GetFiles(MemberDir, "*.xml");
LogTarget.LogLine($"Renaming {filenames.Length} xml files to xml.bak in {MemberDir}", LogEvent.ELogLevel.Info, "ImportProcess");
foreach (string f in filenames)
{
string fileName = Path.GetFileName(f);
string backupName = $"{fileName}.bak";
if (File.Exists(backupName)) File.Delete(backupName);
File.Move(fileName, backupName);
File.Delete(fileName);
}
LogTarget.StepStarted(3, $"Unpacking {MemberImportSettings.ZipFile}");
LogTarget.LogLine($"Extracting {Path.Combine(InputDir, MemberImportSettings.ZipFile)}", LogEvent.ELogLevel.Info, "ImportProcess");
string inFile = Path.Combine(InputDir, MemberImportSettings.ZipFile);
using (ZipInputStream s = new ZipInputStream(File.OpenRead(inFile)))
{
s.Password = MemberImportSettings.ZipPassword;
ZipEntry theEntry;
while ((theEntry = s.GetNextEntry()) != null)
{
Console.WriteLine(theEntry.Name);
string directoryName = Path.GetDirectoryName(theEntry.Name);
string fileName = Path.GetFileName(theEntry.Name);
// create directory
if (directoryName.Length > 0)
{
Directory.CreateDirectory(directoryName);
}
if (fileName != String.Empty)
{
using (FileStream streamWriter = File.Create(Path.Combine(MemberDir, theEntry.Name)))
{
int size = 2048;
byte[] data = new byte[2048];
while (true)
{
size = s.Read(data, 0, data.Length);
if (size > 0)
{
streamWriter.Write(data, 0, size);
}
else
{
break;
}
}
}
}
}
}
LogTarget.StepCompleted(3, $"Unpacking {MemberImportSettings.ZipFile}", true);
}
catch (Exception ex)
{
LogTarget.LogLine($"Exception during import: {ex.Message}", LogEvent.ELogLevel.Error, "ImportProcess");
return false;
}
return true;
}
private bool ZipImport()
{
try
{
}
catch (Exception ex)
{
Console.WriteLine($"Exception during import: {ex.Message}");
// No need to rethrow the exception as for our purposes its handled.
return false;
}
return true;
}
}
public class MemberImportExport
{
[XmlElement] public string ZipFile { get; set; } = "fnord.zip";
@ -174,97 +21,10 @@ namespace dezentrale.model
[XmlElement] public string GitUserName { get; set; } = "";
[XmlElement] public string GitPassword { get; set; } = "";
public static bool RunProcess(string cmd, string args, string workingDir = ".", view.ILogger log = null)
{
try
{
if (log != null)
{
log.LogLine($"Running {cmd}", LogEvent.ELogLevel.Info, "RunProcess");
log.LogRaw($"args: {cmd} {args}");
log.LogRaw($"dir: {workingDir}");
}
else
{
Console.WriteLine($"cmd: {cmd} {args}");
Console.WriteLine($"dir: {workingDir}");
}
Process p = new Process();
p.StartInfo.FileName = cmd;
p.StartInfo.Arguments = args;
p.StartInfo.WorkingDirectory = workingDir;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.CreateNoWindow = true;
p.Start();
string stdout = p.StandardOutput.ReadToEnd();
string stderr = p.StandardError.ReadToEnd();
p.WaitForExit();
if (stdout.Length > 0)
{
if (log != null) log.LogRaw(stdout);
else
{
Console.WriteLine("stdout:");
Console.WriteLine(stdout);
Console.WriteLine();
}
}
if (stderr.Length > 0)
{
if (log != null) log.LogRaw(stderr);
else
{
Console.WriteLine("stderr:");
Console.WriteLine(stderr);
Console.WriteLine();
}
}
return p.ExitCode == 0;
} catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
return false;
}
public static bool Gpg(string args, string gpgPassword)
{
string argsFull = $"--batch --yes --passphrase \"{gpgPassword}\" {args}";
return RunProcess("gpg", argsFull);
}
[Obsolete]
public bool Export(string memberDirectory, string outputDir)
{
string tmpFile = Path.Combine(outputDir, ZipFile);
Console.WriteLine($"Export: Packing {tmpFile}");
if (ZipExport(memberDirectory, outputDir) != true) return false;
if (GpgEnabled)
{
Console.WriteLine($"Export: Using GPG to encrypt {Path.Combine(outputDir, GpgFile)}");
if (!Gpg($"--output \"{Path.Combine(outputDir, GpgFile)}\" -c \"{tmpFile}\"", GpgPassword)) return false;
tmpFile = Path.Combine(outputDir, GpgFile);
}
if (HgEnabled)
{
Console.WriteLine($"Export: Using HG to commit / push {tmpFile}");
//this might fail as repo might be existing / file might be already in repo
RunProcess("hg", "init", outputDir);
RunProcess("hg", $"add {tmpFile}", outputDir);
//now, committing is more interesting
if (!RunProcess("hg",
"commit"
+ $" --message \"dezentrale-members.exe --mode=export\nProgram version={Program.VersionString}\""
+ $" --user \"{HgUserName}\"", outputDir))
return false;
if (!RunProcess("hg", $"--config auth.rc.prefix={HgURL} --config auth.rc.username={HgUserName} --config auth.rc.password={HgPassword} push {HgURL}", outputDir))
return false;
}
return true;
throw new NotImplementedException();
}
[Obsolete]
@ -308,6 +68,7 @@ namespace dezentrale.model
}
return true;
}
[Obsolete]
public bool VerifyExport(string memberDirectory, string outputDir)
{
if (!Directory.Exists(memberDirectory))
@ -386,172 +147,6 @@ namespace dezentrale.model
return true;
}
private bool ZipExport(string memberDirectory, string outputDir)
{
if (!Directory.Exists(memberDirectory))
{
Console.WriteLine($"Cannot find directory '{memberDirectory}'");
return false;
}
if (!Directory.Exists(outputDir))
{
Console.WriteLine($"Cannot find directory '{outputDir}'");
return false;
}
try
{
// Depending on the directory this could be very large and would require more attention
// in a commercial package.
string[] filenames = Directory.GetFiles(memberDirectory, "*.xml");
string outFile = Path.Combine(outputDir, ZipFile);
if (File.Exists(outFile))
{
string backupName = $"{outFile}.bak";
if (File.Exists(backupName)) File.Delete(backupName);
File.Move(outFile, backupName);
}
// 'using' statements guarantee the stream is closed properly which is a big source
// of problems otherwise. Its exception safe as well which is great.
using (ZipOutputStream s = new ZipOutputStream(File.Create(outFile)))
{
s.SetLevel(9); // 0 - store only to 9 - means best compression
s.Password = ZipPassword; //null is a desired value for "no encryption"
byte[] buffer = new byte[4096];
foreach (string file in filenames)
{
Console.WriteLine(file);
// Using GetFileName makes the result compatible with XP
// as the resulting path is not absolute.
var entry = new ZipEntry(Path.GetFileName(file));
// Setup the entry data as required.
// Crc and size are handled by the library for seakable streams
// so no need to do them here.
// Could also use the last write time or similar for the file.
entry.DateTime = DateTime.Now;
s.PutNextEntry(entry);
using (FileStream fs = File.OpenRead(file))
{
// Using a fixed size buffer here makes no noticeable difference for output
// but keeps a lid on memory usage.
int sourceBytes;
do
{
sourceBytes = fs.Read(buffer, 0, buffer.Length);
s.Write(buffer, 0, sourceBytes);
} while (sourceBytes > 0);
}
}
// Finish/Close arent needed strictly as the using statement does this automatically
// Finish is important to ensure trailing information for a Zip file is appended. Without this
// the created file would be invalid.
s.Finish();
// Close is important to wrap things up and unlock the file.
s.Close();
}
}
catch (Exception ex)
{
Console.WriteLine($"Exception during export: {ex.Message}");
// No need to rethrow the exception as for our purposes its handled.
return false;
}
return true;
}
/*
[Obsolete]
private bool ZipImport(string memberDirectory, string inputDir)
{
throw new NotImplementedException();
/*
if (!Directory.Exists(memberDirectory))
{
Console.WriteLine($"Cannot find directory '{memberDirectory}'");
return false;
}
if (!Directory.Exists(inputDir))
{
Console.WriteLine($"Cannot find directory '{inputDir}'");
return false;
}
try
{
//First, rename all member-*.xml to member-*.xml.bak
string[] filenames = Directory.GetFiles(memberDirectory, "*.xml");
foreach (string f in filenames)
{
string fileName = Path.GetFileName(f);
string backupName = $"{fileName}.bak";
if (File.Exists(backupName)) File.Delete(backupName);
File.Move(fileName, backupName);
File.Delete(fileName);
}
string inFile = Path.Combine(inputDir, ZipFile);
using (ZipInputStream s = new ZipInputStream(File.OpenRead(inFile)))
{
s.Password = ZipPassword;
ZipEntry theEntry;
while ((theEntry = s.GetNextEntry()) != null)
{
Console.WriteLine(theEntry.Name);
string directoryName = Path.GetDirectoryName(theEntry.Name);
string fileName = Path.GetFileName(theEntry.Name);
// create directory
if (directoryName.Length > 0)
{
Directory.CreateDirectory(directoryName);
}
if (fileName != String.Empty)
{
using (FileStream streamWriter = File.Create(Path.Combine(memberDirectory, theEntry.Name)))
{
int size = 2048;
byte[] data = new byte[2048];
while (true)
{
size = s.Read(data, 0, data.Length);
if (size > 0)
{
streamWriter.Write(data, 0, size);
}
else
{
break;
}
}
}
}
}
}
}
catch (Exception ex)
{
Console.WriteLine($"Exception during import: {ex.Message}");
// No need to rethrow the exception as for our purposes its handled.
return false;
}
return true;
}*/
}
}

54
view/ConsoleLogger.cs Normal file
View File

@ -0,0 +1,54 @@
using System;
using dezentrale.model;
namespace dezentrale.view
{
public class ConsoleLogger : core.IProcessController
{
public System.Windows.Forms.DialogResult DialogResult { get; set; } = System.Windows.Forms.DialogResult.Cancel;
public ConsoleLogger()
{
}
public System.Threading.Thread StartRunProcess(core.BackgroundProcess process)
{
Console.WriteLine($"Process - {process.Caption} (running)");
System.Threading.Thread t = process.StartRun();
if(t == null)
{
Console.WriteLine($"Process - {process.Caption} (starting error)");
}
return t;
}
public void Clear()
{
Console.Clear();
}
public void LogLine(string text, LogEvent.ELogLevel logLevel = LogEvent.ELogLevel.Info, string module = "")
{
Console.WriteLine($"{DateTime.Now} [{module}] {logLevel} {text}");
}
public void LogRaw(string text)
{
Console.WriteLine(text);
}
public void StepStarted(uint stepNumber = 0, string stepDescription = "Generic")
{
Console.WriteLine($"# {stepNumber + 1}: Started {stepDescription}");
}
public void StepCompleted(uint stepNumber = 0, string stepDescription = "Generic", bool success = true)
{
Console.WriteLine($"# {stepNumber + 1}: Completed({success}) {stepDescription}");
}
public void ActionCompleted(bool success)
{
if (success) Console.WriteLine($"Process - done (OK)");
else Console.WriteLine($"Process - done (ERROR)");
}
}
}

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using dezentrale.core;
using dezentrale.model;
using dezentrale.model.money;
@ -134,7 +135,7 @@ namespace dezentrale.view
private void mnuMain_File_Import(object sender, EventArgs e)
{
DialogResult dr = MessageBox.Show("Overwrite database?", "Warning\nImporting database from external source will overwrite all\nlocal changes! Really import?", MessageBoxButtons.YesNo);
DialogResult dr = MessageBox.Show("Warning\nImporting database from external source will overwrite all\nlocal changes! Really import?", "Overwrite local version of the database?", MessageBoxButtons.YesNo);
if(dr == DialogResult.Yes)
{
ImportProcess import = new ImportProcess()
@ -143,16 +144,16 @@ namespace dezentrale.view
MemberDir = Program.config.DbDirectory,
InputDir = Program.DmDirectory,
};
frmProcessWithLog frmImport = new frmProcessWithLog(import, false);
frmProcessWithLog frmImport = new frmProcessWithLog(import, true);
dr = frmImport.ShowDialog();
MessageBox.Show("DialogResult=" + dr);
//MessageBox.Show("DialogResult=" + dr);
//Reload member list
//Reload moneytransfers, if loaded
/*if (!Program.config.ImportExport.Import(Program.config.DbDirectory, Program.DmDirectory))
{
MessageBox.Show("Import failed!");
}*/
Application.Restart();
if(dr == DialogResult.OK) Application.Restart();
}
}

View File

@ -3,70 +3,13 @@ using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using dezentrale.core;
using dezentrale.model;
namespace dezentrale.view
{
public interface ILogger
{
void Clear();
void LogRaw(string text);
void LogLine(string text, LogEvent.ELogLevel logLevel = LogEvent.ELogLevel.Info, string module = "");
}
public interface IProcessController : ILogger
{
void ActionCompleted(bool success);
void StepStarted(uint stepNumber = 0, string stepDescription = "Generic");
void StepCompleted(uint stepNumber = 0, string stepDescription = "Generic", bool success = true);
}
public abstract class BackgroundProcess
{
public IProcessController LogTarget { get; set; } = null;
public bool CancelButton { get; protected set; } = false;
public string Caption { get; protected set; } = "BackgroundProcess";
public uint Steps { get; protected set; } = 1;
protected abstract bool Run();
private Thread thread = null;
public bool StartRun()
{
if (thread != null) return false;
thread = new Thread(RunAbstract) { IsBackground = true };
thread.Start();
return true;
}
private void RunAbstract()
{
LogTarget.ActionCompleted(Run());
}
}
public class HelloWorldProcess : BackgroundProcess
{
public HelloWorldProcess()
{
Caption = "Hello World";
Steps = 20;
}
protected override bool Run()
{
for(uint i = 0; i < 20; i++)
{
string stepName = ((i & 0x01) == 0) ? "Hello" : "World";
LogTarget.StepStarted(i, stepName);
LogTarget.LogLine(stepName, LogEvent.ELogLevel.Info, "HelloWorldProcess");
Thread.Sleep(200);
LogTarget.StepCompleted(i, stepName, (i & 0x01) == 0);
Thread.Sleep(200);
}
return true;
}
}
public class frmProcessWithLog : Form, IProcessController
{
private DialogResult toReturn = DialogResult.Cancel;
@ -111,13 +54,11 @@ namespace dezentrale.view
this.Text = $"Process - {this.process.Caption} (waiting)";
this.Width = 800;
this.Height = 600;
this.StartPosition = FormStartPosition.CenterParent;
this.FormClosing += (sender, e) => { DialogResult = toReturn; };
this.Load += (sender, e) => { if (autoRun) btnStart_Click(null, null); };
/* if (autoRun)
{
MessageBox.Show("autoRun=true leads to problems with Invoke on gui access from background thread");
this.Close();
return;
}*/
// build GUI
//[TextBox Multiline ]
//[Step display label ]
@ -184,11 +125,6 @@ namespace dezentrale.view
btnClose.Height += 2;
btnClose.Click += btnClose_Click;
this.Controls.Add(btnClose);
if (autoRun)
btnStart_Click(null,null);
this.FormClosing += (sender, e) => DialogResult = toReturn;
}
private void btnStart_Click(object sender, EventArgs e)