190515PX initial commit

This commit is contained in:
phantomix 2019-05-15 18:42:43 +02:00
commit 2c4825c3bf
15 changed files with 631 additions and 0 deletions

5
.hgignore Normal file
View File

@ -0,0 +1,5 @@
syntax: glob
obj/*
*.csproj.user
.vs/*
bin/*

6
App.config Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v2.0.50727"/></startup>
</configuration>

117
Program.cs Normal file
View File

@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.IO;
using dezentrale.model;
namespace dezentrale
{
public class Program
{
public static uint VersionNumber { get; private set; } = 0x19051500;
public static string VersionString { get; private set; } = $"{VersionNumber:x}";
public static string AppData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
public static string DmDirectory = Path.Combine(AppData, "dezentrale-members");
public static string ConfigFile = Path.Combine(DmDirectory, "configuration.xml");
public static string MemberFile = Path.Combine(DmDirectory, "members.xml");
public static Configuration config = new Configuration();
public static MemberList members = new MemberList();
public enum eMode
{
CommandLine = 0,
Gui = 1,
Cronjob = 2,
}
public static eMode ProgramMode = eMode.CommandLine;
static int Main(string[] args)
{
bool printHelp = false;
Console.WriteLine($"dezentrale-members, Version {VersionString}");
try { Directory.CreateDirectory(DmDirectory); }
catch (Exception ex) { Console.WriteLine($"Error while creating data directory:\n{ex.Message}"); return 1; }
try
{
try { config = (Configuration)XmlData.LoadFromFile(ConfigFile); }
catch (FileNotFoundException) { XmlData.SaveToFile(ConfigFile, config); Console.WriteLine("Created new configuration file."); }
try { members = (MemberList)XmlData.LoadFromFile(MemberFile); }
catch (FileNotFoundException) { XmlData.SaveToFile(MemberFile, members); Console.WriteLine("Created new member file."); }
} catch (Exception ex)
{
Console.WriteLine("Error while accessing program data:");
Console.WriteLine(ex.Message);
return 1;
}
List<string> clArgs = new List<string>();
foreach(string argIt in args)
{
string argn;
string argv;
if (argIt.Contains("="))
{
argn = argIt.Substring(0, argIt.IndexOf('='));
argv = argIt.Substring(argIt.IndexOf('=') + 1);
} else
{
argn = argIt;
argv = "";
}
if (argn.StartsWith("-")) argn = argn.Substring(1);
if (argn.StartsWith("/") || argn.StartsWith("-")) argn = argn.Substring(1);
switch(argn.ToLower())
{
case "help": printHelp = true; break;
case "mode":
switch(argv.ToLower())
{
case "cl": ProgramMode = eMode.CommandLine; Console.WriteLine($"Setting Mode to {ProgramMode}"); break;
case "gui": ProgramMode = eMode.Gui; Console.WriteLine($"Setting Mode to {ProgramMode}"); break;
case "cronjob":
case "auto": ProgramMode = eMode.Cronjob; Console.WriteLine($"Setting Mode to {ProgramMode}"); break;
default:
Console.WriteLine("Invalid mode.");
printHelp = true;
break;
}
break;
default:
Console.WriteLine($"Invalid command({argn})");
printHelp = true;
break;
}
}
if (printHelp)
{
Console.WriteLine("command line arguments:");
Console.WriteLine("--help Display this help message");
Console.WriteLine("--mode=<mode> Starts the program in given mode. Supported modes are:");
Console.WriteLine(" cl (default) Manual/interactive use from command line.");
Console.WriteLine(" gui Start in GUI mode (not implemented yet)");
Console.WriteLine(" auto, cronjob Check and perform pending required membership actions");
Console.WriteLine();
return 1;
}
Member m = new Member();
FormMail result = FormMail.GenerateNewMemberWelcome().ReplaceReflect(m);
Console.WriteLine($" To: {result.To}");
Console.WriteLine($" Subject: {result.Subject}");
Console.WriteLine($" Body: {result.Body}");
//result.Send();
return 0;
}
}
}

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// Allgemeine Informationen über eine Assembly werden über die folgenden
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
// die einer Assembly zugeordnet sind.
[assembly: AssemblyTitle("dezentrale-members")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("dezentrale-members")]
[assembly: AssemblyCopyright("Copyright © 2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
[assembly: ComVisible(false)]
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
[assembly: Guid("c5709dda-d3a7-4c2e-9ab5-98dd3ed1180e")]
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
//
// Hauptversion
// Nebenversion
// Buildnummer
// Revision
//
// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
// übernehmen, indem Sie "*" eingeben:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

84
dezentrale-members.csproj Normal file
View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{C5709DDA-D3A7-4C2E-9AB5-98DD3ED1180E}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>dezentrale</RootNamespace>
<AssemblyName>dezentrale-members</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="model\ConfigSmtp.cs" />
<Compile Include="model\Configuration.cs" />
<Compile Include="model\ConfigVSMail.cs" />
<Compile Include="model\FormMail.cs" />
<Compile Include="model\Member.cs" />
<Compile Include="model\LogEntry.cs" />
<Compile Include="model\MemberList.cs" />
<Compile Include="model\MemberPayment.cs" />
<Compile Include="model\XmlData.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

25
dezentrale-members.sln Normal file
View File

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27130.2024
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dezentrale-members", "dezentrale-members.csproj", "{C5709DDA-D3A7-4C2E-9AB5-98DD3ED1180E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C5709DDA-D3A7-4C2E-9AB5-98DD3ED1180E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C5709DDA-D3A7-4C2E-9AB5-98DD3ED1180E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C5709DDA-D3A7-4C2E-9AB5-98DD3ED1180E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C5709DDA-D3A7-4C2E-9AB5-98DD3ED1180E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {901A729B-6A62-423D-B793-3EE7A81BF327}
EndGlobalSection
EndGlobal

18
model/ConfigSmtp.cs Normal file
View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
namespace dezentrale.model
{
public class ConfigSmtp : XmlData
{
[XmlElement] public string Host { get; set; } = "localhost";
[XmlElement] public int Port { get; set; } = 587;
[XmlElement] public bool SSL { get; set; } = true;
[XmlElement] public string From { get; set; } = "John Doe <john.doe@example.com>";
[XmlElement] public string UserName { get; set; } = "username"; //you might need to use a complete e-mail address here.
[XmlElement] public string Password { get; set; } = "password";
}
}

14
model/ConfigVSMail.cs Normal file
View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
namespace dezentrale.model
{
public class ConfigVSMail : XmlData
{
[XmlElement] public string VSName { get; set; } = "dezentrale Vorstand";
[XmlElement] public string VSEmail { get; set; } = "vorstand@dezentrale.space";
}
}

15
model/Configuration.cs Normal file
View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
namespace dezentrale.model
{
public class Configuration : XmlData
{
[XmlElement] public ConfigSmtp Smtp { get; set; } = new ConfigSmtp();
[XmlElement] public ConfigVSMail VS { get; set; } = new ConfigVSMail();
}
}

119
model/FormMail.cs Normal file
View File

@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Mail;
using System.Reflection;
using System.Text;
namespace dezentrale.model
{
public class FormMail : XmlData
{
public string To { get; set; } = "{Nickname} <{EMail}>";
public string Subject { get; set; }
public string Body { get; set; }
public FormMail() { }
public FormMail(FormMail copyFrom)
{
if(copyFrom != null)
{
this.To = copyFrom.To;
this.Subject = copyFrom.Subject;
this.Body = copyFrom.Body;
}
}
public FormMail ReplaceReflect(object o)
{
FormMail ret = new FormMail(this);
PropertyInfo[] objProperties = o.GetType().GetProperties();
foreach (var mailProperty in ret.GetType().GetProperties())
{
if (mailProperty.PropertyType != typeof(string)) continue;
if (!mailProperty.CanRead) continue;
if (!mailProperty.CanWrite) continue;
string propVal = (string)mailProperty.GetValue(ret, null);
if (propVal == null) continue;
bool changed = false;
foreach (var objProperty in objProperties)
{
if (!objProperty.CanRead) continue;
//check if objProperty occurs in mailProperty contents, then replace
string token = $"{{{objProperty.Name}}}";
string tokenValue = objProperty.GetValue(o, null).ToString();
if (propVal.Contains(token))
{
//NOTE: This is problematic because it allows the user to generate recursive replacements by setting e.g. the Nickname to "{PgpFingerprint}"
propVal = propVal.Replace(token, tokenValue);
changed = true;
}
}
if (changed)
{
mailProperty.SetValue(ret, propVal, null);
}
}
return ret;
}
public bool Send(object replaceReflect = null)
{
FormMail src = replaceReflect == null ? this : ReplaceReflect(replaceReflect);
SmtpClient client = new SmtpClient(Program.config.Smtp.Host, Program.config.Smtp.Port);
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.EnableSsl = Program.config.Smtp.SSL;
client.UseDefaultCredentials = false;
client.Credentials = new System.Net.NetworkCredential(Program.config.Smtp.UserName, Program.config.Smtp.Password);
try
{
client.Send(Program.config.Smtp.From, src.To, src.Subject, src.Body);
} catch(Exception ex)
{
Console.WriteLine($"Error while sending Mail:\n{ex.Message}\n");
return false;
}
return true;
}
public static FormMail GenerateNewMemberWelcome()
{
FormMail ret = new FormMail();
ret.To = "{Nickname} <{EMail}>";
ret.Subject = "Willkommen in der dezentrale, {Nickname}!";
ret.Body = "Hello {Nickname}!\n"
+ "\n"
+ "Welcome to the dezentrale Hackspace.\n"
+ "Your membership number is {Number}\n";
return ret;
}
public static FormMail GenerateNewMemberVorstand()
{
FormMail ret = new FormMail();
ret.To = "{VSName} <{VSEMail}>";
ret.Subject = "Neuer Mitgliedsantrag, Nummer = {Number}, nick = {Nickname}!";
ret.Body = "Hallo Vorstandsliste,!\n"
+ "\n"
+ "Folgender Nutzer wurde in die Datenbank aufgenommen.\n"
+ "Mitgliednummer: {Number}\n"
+ "Nickname: {Nickname}\n"
+ "Realname: {FirstName} {LastName}\n"
+ "EMail: {EMail}\n"
+ "\n"
+ "Damit beginnt eine 7-Tage-Einspruchsfrist für den Vorstand.\n"
+ "\n"
+ "\n"
+ "Dies ist eine automatisch generierte E-Mail.\n";
return ret;
}
}
}

11
model/LogEntry.cs Normal file
View File

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace dezentrale.model
{
public class LogEntry : XmlData
{
}
}

71
model/Member.cs Normal file
View File

@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
namespace dezentrale.model
{
public class Member : XmlData
{
public enum eRole
{
Normal = 0,
Vorstandsvorsitzender,
Schatzmeister,
Schriftfuehrer,
Beisitzer,
}
public enum eType
{
Regulaer = 0,
Foerdermitglied
}
public enum eStatus
{
Uninitialized = 0,
Greeted,
Active,
Bannend,
Disabled,
}
public enum ePaymentClass
{
Reduced = 0,
Normal,
NerdClass1,
NerdClass2,
NerdClass3,
NerdClass4,
}
//metadata
[XmlElement] public uint Number { get; set; } = 0;
[XmlElement] public eRole Role { get; set; } = eRole.Normal;
[XmlElement] public eType Type { get; set; } = eType.Regulaer;
[XmlElement] public eStatus Status { get; set; } = eStatus.Uninitialized;
[XmlElement] public List<string> OpenPayments { get; set; } = new List<string>(); //todo: data type
[XmlElement] public List<LogEntry> Log { get; set; } = new List<LogEntry>();
//personal data
[XmlElement] public string Nickname { get; set; } = "{PgpFingerprint}";//"DoeJohnz";
[XmlElement] public string FirstName { get; set; } = "John";
[XmlElement] public string LastName { get; set; } = "_Doe";
[XmlElement] public string AddressStreet { get; set; } = "_HomeStreet";
[XmlElement] public string AddressNumber { get; set; } = "123";
[XmlElement] public string Zipcode { get; set; } = "12345";
[XmlElement] public string City { get; set; } = "_HomeCity";
[XmlElement] public DateTime Birthday { get; set; }
[XmlElement] public string EMail { get; set; } = "john.doe@example.com";
[XmlElement] public string PgpFingerprint { get; set; } = "print";
//membership organizational data
[XmlElement] public bool MvInvitationByPost { get; set; } = false;
[XmlElement] public DateTime SpawnDate { get; set; }
[XmlElement] public ePaymentClass PaymentClass { get; set; } = ePaymentClass.Normal;
[XmlElement] public DateTime MemberFormDate { get; set; }
}
}

10
model/MemberList.cs Normal file
View File

@ -0,0 +1,10 @@
using System.Collections.Generic;
using System.Xml.Serialization;
namespace dezentrale.model
{
public class MemberList : XmlData
{
[XmlElement("Member")] public List<Member> Entries { get; set; } = new List<Member>();
}
}

13
model/MemberPayment.cs Normal file
View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace dezentrale.model
{
public class MemberPayment : XmlData
{
//date
//amount
}
}

87
model/XmlData.cs Normal file
View File

@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
namespace dezentrale.model
{
public class XmlData
{
[XmlElement] public DateTime LastChanged { get; set; } = DateTime.Now;
[XmlIgnore]
private static Type[] SerializeTypes =
{
typeof(bool),
typeof(uint),
typeof(int),
typeof(string),
typeof(DateTime),
typeof(Configuration),
typeof(ConfigSmtp),
typeof(ConfigVSMail),
typeof(FormMail),
typeof(LogEntry),
typeof(Member),
typeof(Member.ePaymentClass),
typeof(Member.eRole),
typeof(Member.eStatus),
typeof(Member.eType),
typeof(MemberList),
typeof(MemberPayment),
//typeof(List<KeyValue>),
//typeof(List<TaskLogEntry>),
//typeof(List<ListColumn>),
};
public static XmlData LoadFromFile(string fileName)
{
FileStream fs = null;
try
{
XmlSerializer ser = new XmlSerializer(typeof(XmlData), XmlData.SerializeTypes);
fs = new FileStream(fileName, FileMode.Open);
XmlData ds = (XmlData)ser.Deserialize(fs);
fs.Close();
return ds;
}
catch (Exception e)
{
if (fs != null) fs.Close();
if (e.InnerException != null) throw e.InnerException; else throw e;
}
}
public static bool SaveToFile(string fileName, XmlData ds)
{
try
{
if (File.Exists(fileName))
{
if (File.Exists(fileName + ".bak")) File.Delete(fileName + ".bak");
File.Move(fileName, fileName + ".bak");
}
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
XmlSerializer ser = new XmlSerializer(typeof(XmlData), XmlData.SerializeTypes);
FileStream fs;
fs = new FileStream(fileName, FileMode.Create);
ser.Serialize(fs, ds, ns);
fs.Close();
}
catch (Exception e)
{
if (e.InnerException != null)
throw e.InnerException;
else
throw e;
}
return true;
}
}
}