diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fd5204b --- /dev/null +++ b/.gitignore @@ -0,0 +1,183 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Roslyn cache directories +*.ide/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +#NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding addin-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# If using the old MSBuild-Integrated Package Restore, uncomment this: +#!**/packages/repositories.config + +# Windows Azure Build Output +csx/ +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ diff --git a/App/MiteDesk.WinForms/Activities.Designer.cs b/App/MiteDesk.WinForms/Activities.Designer.cs new file mode 100644 index 0000000..deda871 --- /dev/null +++ b/App/MiteDesk.WinForms/Activities.Designer.cs @@ -0,0 +1,321 @@ +namespace SixtyNineDegrees.MiteDesk.WinForms +{ + partial class Activities + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.TabCustomers = new System.Windows.Forms.TabControl(); + this.ActiveActivitiesTabPage = new System.Windows.Forms.TabPage(); + this.ActivActivitiesTree = new System.Windows.Forms.TreeView(); + this.TreeContextMenu = new System.Windows.Forms.ContextMenuStrip(this.components); + this.DeleteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.ArchivedCustomersTabPage = new System.Windows.Forms.TabPage(); + this.ArchivedActivitiesTree = new System.Windows.Forms.TreeView(); + this.FormGroup = new System.Windows.Forms.GroupBox(); + this.BoxHourlyRate = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.CheckBoxBillable = new System.Windows.Forms.CheckBox(); + this.CheckBoxArchived = new System.Windows.Forms.CheckBox(); + this.label2 = new System.Windows.Forms.Label(); + this.BoxNote = new System.Windows.Forms.TextBox(); + this.BoxName = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.BtnNewActivity = new System.Windows.Forms.Button(); + this.BtnCancel = new System.Windows.Forms.Button(); + this.BtnSave = new System.Windows.Forms.Button(); + this.ErrorProvider = new System.Windows.Forms.ErrorProvider(this.components); + this.TreeDataBackgroundWorker = new System.ComponentModel.BackgroundWorker(); + this.TabCustomers.SuspendLayout(); + this.ActiveActivitiesTabPage.SuspendLayout(); + this.TreeContextMenu.SuspendLayout(); + this.ArchivedCustomersTabPage.SuspendLayout(); + this.FormGroup.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.ErrorProvider)).BeginInit(); + this.SuspendLayout(); + // + // TabCustomers + // + this.TabCustomers.Controls.Add(this.ActiveActivitiesTabPage); + this.TabCustomers.Controls.Add(this.ArchivedCustomersTabPage); + this.TabCustomers.Location = new System.Drawing.Point(9, 9); + this.TabCustomers.Name = "TabCustomers"; + this.TabCustomers.SelectedIndex = 0; + this.TabCustomers.Size = new System.Drawing.Size(189, 277); + this.TabCustomers.TabIndex = 10; + // + // ActiveActivitiesTabPage + // + this.ActiveActivitiesTabPage.Controls.Add(this.ActivActivitiesTree); + this.ActiveActivitiesTabPage.Location = new System.Drawing.Point(4, 22); + this.ActiveActivitiesTabPage.Name = "ActiveActivitiesTabPage"; + this.ActiveActivitiesTabPage.Padding = new System.Windows.Forms.Padding(3); + this.ActiveActivitiesTabPage.Size = new System.Drawing.Size(181, 251); + this.ActiveActivitiesTabPage.TabIndex = 0; + this.ActiveActivitiesTabPage.Text = "Aktive"; + this.ActiveActivitiesTabPage.UseVisualStyleBackColor = true; + // + // ActivActivitiesTree + // + this.ActivActivitiesTree.ContextMenuStrip = this.TreeContextMenu; + this.ActivActivitiesTree.Dock = System.Windows.Forms.DockStyle.Fill; + this.ActivActivitiesTree.FullRowSelect = true; + this.ActivActivitiesTree.HideSelection = false; + this.ActivActivitiesTree.Location = new System.Drawing.Point(3, 3); + this.ActivActivitiesTree.Name = "ActivActivitiesTree"; + this.ActivActivitiesTree.ShowLines = false; + this.ActivActivitiesTree.ShowPlusMinus = false; + this.ActivActivitiesTree.ShowRootLines = false; + this.ActivActivitiesTree.Size = new System.Drawing.Size(175, 245); + this.ActivActivitiesTree.TabIndex = 2; + // + // TreeContextMenu + // + this.TreeContextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.DeleteToolStripMenuItem}); + this.TreeContextMenu.Name = "TreeContextMenu"; + this.TreeContextMenu.Size = new System.Drawing.Size(119, 26); + // + // DeleteToolStripMenuItem + // + this.DeleteToolStripMenuItem.Name = "DeleteToolStripMenuItem"; + this.DeleteToolStripMenuItem.Size = new System.Drawing.Size(118, 22); + this.DeleteToolStripMenuItem.Text = "Löschen"; + this.DeleteToolStripMenuItem.Click += new System.EventHandler(this.DeleteToolStripMenuItem_Click); + // + // ArchivedCustomersTabPage + // + this.ArchivedCustomersTabPage.Controls.Add(this.ArchivedActivitiesTree); + this.ArchivedCustomersTabPage.Location = new System.Drawing.Point(4, 22); + this.ArchivedCustomersTabPage.Name = "ArchivedCustomersTabPage"; + this.ArchivedCustomersTabPage.Padding = new System.Windows.Forms.Padding(3); + this.ArchivedCustomersTabPage.Size = new System.Drawing.Size(181, 251); + this.ArchivedCustomersTabPage.TabIndex = 1; + this.ArchivedCustomersTabPage.Text = "Archivierte"; + this.ArchivedCustomersTabPage.UseVisualStyleBackColor = true; + // + // ArchivedActivitiesTree + // + this.ArchivedActivitiesTree.ContextMenuStrip = this.TreeContextMenu; + this.ArchivedActivitiesTree.Dock = System.Windows.Forms.DockStyle.Fill; + this.ArchivedActivitiesTree.FullRowSelect = true; + this.ArchivedActivitiesTree.HideSelection = false; + this.ArchivedActivitiesTree.Location = new System.Drawing.Point(3, 3); + this.ArchivedActivitiesTree.Name = "ArchivedActivitiesTree"; + this.ArchivedActivitiesTree.ShowLines = false; + this.ArchivedActivitiesTree.ShowPlusMinus = false; + this.ArchivedActivitiesTree.ShowRootLines = false; + this.ArchivedActivitiesTree.Size = new System.Drawing.Size(175, 245); + this.ArchivedActivitiesTree.TabIndex = 1; + // + // FormGroup + // + this.FormGroup.Controls.Add(this.BoxHourlyRate); + this.FormGroup.Controls.Add(this.label3); + this.FormGroup.Controls.Add(this.CheckBoxBillable); + this.FormGroup.Controls.Add(this.CheckBoxArchived); + this.FormGroup.Controls.Add(this.label2); + this.FormGroup.Controls.Add(this.BoxNote); + this.FormGroup.Controls.Add(this.BoxName); + this.FormGroup.Controls.Add(this.label1); + this.FormGroup.Enabled = false; + this.FormGroup.Location = new System.Drawing.Point(215, 10); + this.FormGroup.Name = "FormGroup"; + this.FormGroup.Size = new System.Drawing.Size(313, 276); + this.FormGroup.TabIndex = 11; + this.FormGroup.TabStop = false; + // + // BoxHourlyRate + // + this.BoxHourlyRate.Location = new System.Drawing.Point(10, 175); + this.BoxHourlyRate.Name = "BoxHourlyRate"; + this.BoxHourlyRate.Size = new System.Drawing.Size(43, 20); + this.BoxHourlyRate.TabIndex = 5; + this.BoxHourlyRate.TextChanged += new System.EventHandler(this.EnableBtnSave); + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(7, 159); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(86, 13); + this.label3.TabIndex = 7; + this.label3.Text = "Stundensatz in €"; + // + // CheckBoxBillable + // + this.CheckBoxBillable.AutoSize = true; + this.CheckBoxBillable.Location = new System.Drawing.Point(89, 177); + this.CheckBoxBillable.Name = "CheckBoxBillable"; + this.CheckBoxBillable.Size = new System.Drawing.Size(96, 17); + this.CheckBoxBillable.TabIndex = 6; + this.CheckBoxBillable.Text = "Verrechenbar?"; + this.CheckBoxBillable.UseVisualStyleBackColor = true; + this.CheckBoxBillable.CheckedChanged += new System.EventHandler(this.EnableBtnSave); + // + // CheckBoxArchived + // + this.CheckBoxArchived.AutoSize = true; + this.CheckBoxArchived.Location = new System.Drawing.Point(216, 177); + this.CheckBoxArchived.Name = "CheckBoxArchived"; + this.CheckBoxArchived.Size = new System.Drawing.Size(70, 17); + this.CheckBoxArchived.TabIndex = 7; + this.CheckBoxArchived.Text = "Archiviert"; + this.CheckBoxArchived.UseVisualStyleBackColor = true; + this.CheckBoxArchived.CheckedChanged += new System.EventHandler(this.EnableBtnSave); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(7, 63); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(61, 13); + this.label2.TabIndex = 3; + this.label2.Text = "Bemerkung"; + // + // BoxNote + // + this.BoxNote.Location = new System.Drawing.Point(10, 79); + this.BoxNote.Multiline = true; + this.BoxNote.Name = "BoxNote"; + this.BoxNote.Size = new System.Drawing.Size(276, 75); + this.BoxNote.TabIndex = 4; + this.BoxNote.TextChanged += new System.EventHandler(this.EnableBtnSave); + // + // BoxName + // + this.BoxName.Location = new System.Drawing.Point(10, 37); + this.BoxName.Name = "BoxName"; + this.BoxName.Size = new System.Drawing.Size(276, 20); + this.BoxName.TabIndex = 3; + this.BoxName.TextChanged += new System.EventHandler(this.EnableBtnSave); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(7, 20); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(35, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Name"; + // + // BtnNewActivity + // + this.BtnNewActivity.Location = new System.Drawing.Point(10, 296); + this.BtnNewActivity.Name = "BtnNewActivity"; + this.BtnNewActivity.Size = new System.Drawing.Size(87, 23); + this.BtnNewActivity.TabIndex = 11; + this.BtnNewActivity.Text = "Neue Leistung"; + this.BtnNewActivity.UseVisualStyleBackColor = true; + this.BtnNewActivity.Click += new System.EventHandler(this.BtnNewActivity_Click); + // + // BtnCancel + // + this.BtnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.BtnCancel.Location = new System.Drawing.Point(445, 296); + this.BtnCancel.Name = "BtnCancel"; + this.BtnCancel.Size = new System.Drawing.Size(83, 23); + this.BtnCancel.TabIndex = 9; + this.BtnCancel.Text = "Schließen"; + this.BtnCancel.UseVisualStyleBackColor = true; + this.BtnCancel.Click += new System.EventHandler(this.BtnCancel_Click); + // + // BtnSave + // + this.BtnSave.Enabled = false; + this.BtnSave.Location = new System.Drawing.Point(356, 296); + this.BtnSave.Name = "BtnSave"; + this.BtnSave.Size = new System.Drawing.Size(83, 23); + this.BtnSave.TabIndex = 8; + this.BtnSave.Text = "Übernehmen"; + this.BtnSave.UseVisualStyleBackColor = true; + this.BtnSave.Click += new System.EventHandler(this.BtnSave_Click); + // + // ErrorProvider + // + this.ErrorProvider.BlinkStyle = System.Windows.Forms.ErrorBlinkStyle.NeverBlink; + this.ErrorProvider.ContainerControl = this; + // + // Activities + // + this.AcceptButton = this.BtnSave; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.BtnCancel; + this.ClientSize = new System.Drawing.Size(537, 327); + this.Controls.Add(this.TabCustomers); + this.Controls.Add(this.FormGroup); + this.Controls.Add(this.BtnNewActivity); + this.Controls.Add(this.BtnCancel); + this.Controls.Add(this.BtnSave); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MaximumSize = new System.Drawing.Size(553, 365); + this.MinimizeBox = false; + this.MinimumSize = new System.Drawing.Size(553, 365); + this.Name = "Activities"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Leistungen"; + this.TabCustomers.ResumeLayout(false); + this.ActiveActivitiesTabPage.ResumeLayout(false); + this.TreeContextMenu.ResumeLayout(false); + this.ArchivedCustomersTabPage.ResumeLayout(false); + this.FormGroup.ResumeLayout(false); + this.FormGroup.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.ErrorProvider)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TabControl TabCustomers; + private System.Windows.Forms.TabPage ActiveActivitiesTabPage; + private System.Windows.Forms.TreeView ActivActivitiesTree; + private System.Windows.Forms.TabPage ArchivedCustomersTabPage; + private System.Windows.Forms.TreeView ArchivedActivitiesTree; + private System.Windows.Forms.GroupBox FormGroup; + private System.Windows.Forms.CheckBox CheckBoxArchived; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox BoxNote; + private System.Windows.Forms.TextBox BoxName; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Button BtnNewActivity; + private System.Windows.Forms.Button BtnCancel; + private System.Windows.Forms.Button BtnSave; + private System.Windows.Forms.ContextMenuStrip TreeContextMenu; + private System.Windows.Forms.ToolStripMenuItem DeleteToolStripMenuItem; + private System.Windows.Forms.ErrorProvider ErrorProvider; + private System.ComponentModel.BackgroundWorker TreeDataBackgroundWorker; + private System.Windows.Forms.TextBox BoxHourlyRate; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.CheckBox CheckBoxBillable; + } +} \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Activities.cs b/App/MiteDesk.WinForms/Activities.cs new file mode 100644 index 0000000..43bd6d1 --- /dev/null +++ b/App/MiteDesk.WinForms/Activities.cs @@ -0,0 +1,265 @@ +using System.Collections.Generic; +using System.Windows.Forms; +using SixtyNineDegrees.MiteDesk.Core.Model; +using SixtyNineDegrees.MiteDesk.Core.Services; +using SixtyNineDegrees.MiteDesk.WinForms.Localization; +using StructureMap; + +namespace SixtyNineDegrees.MiteDesk.WinForms +{ + public partial class Activities : Form + { + + #region Setup + + public Activities() + { + InitializeComponent(); + + LocalizeForm(); + + ActivityService = ObjectFactory.GetInstance(); + + TreeDataBackgroundWorker.DoWork += TreeDataBackgroundWorker_DoWork; + TreeDataBackgroundWorker.RunWorkerCompleted += TreeDataBackgroundWorker_RunWorkerCompleted; + Helper.StartBackgroundWorker(TreeDataBackgroundWorker, null); + + ActivActivitiesTree.NodeMouseClick += Tree_NodeMouseClick; + ArchivedActivitiesTree.NodeMouseClick += Tree_NodeMouseClick; + + PrepareFormForNewActivity(); + } + + private IList ActiveActivities; + private IList ArchivedActivities; + private readonly IActivityService ActivityService; + + private Activity CurrentActivity; + private bool CreateNewActivity; + + #endregion + + #region Lokalisierung + + private void LocalizeForm() + { + Text = ActivitiesLabels.FormTitle; + ActiveActivitiesTabPage.Text = ActivitiesLabels.TabActiveTitle; + ArchivedCustomersTabPage.Text = ActivitiesLabels.TabArchivedTitle; + BtnNewActivity.Text = ActivitiesLabels.ButtonNewActivity; + BtnSave.Text = ActivitiesLabels.ButtonApply; + BtnCancel.Text = ActivitiesLabels.ButtonCancel; + label1.Text = ActivitiesLabels.LabelName; + label2.Text = ActivitiesLabels.LabelNote; + label3.Text = ActivitiesLabels.LabelHourlyNote; + CheckBoxBillable.Text = ActivitiesLabels.LabelBillable; + CheckBoxArchived.Text = ActivitiesLabels.LabelArchived; + DeleteToolStripMenuItem.Text = ActivitiesLabels.ButtonDelete; + } + + #endregion + + #region Treeviews befüllen + + void TreeDataBackgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) + { + if (TreeDataBackgroundWorker.CancellationPending) + { + e.Cancel = true; + return; + } + ActiveActivities = ActivityService.GetAllActiveActivities(); + ArchivedActivities = ActivityService.GetAllArchivedActivities(); + } + + void TreeDataBackgroundWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) + { + + if (ActiveActivities == null || ArchivedActivities == null || e.Cancelled) + return; + + ActivActivitiesTree.Nodes.Clear(); + + foreach (var activity in ActiveActivities) + ActivActivitiesTree.Nodes.Add(activity.ID.ToString(), activity.Name); + + ArchivedActivitiesTree.Nodes.Clear(); + + foreach (var activity in ArchivedActivities) + ArchivedActivitiesTree.Nodes.Add(activity.ID.ToString(), activity.Name); + } + + #endregion + + #region Leistung darstellen + + private void LoadSingleActivityInForm(int activityID) + { + + FormGroup.Text = ActivitiesLabels.GroupBoxTitleEditMode; + CreateNewActivity = false; + CheckBoxArchived.Visible = true; + CurrentActivity = ActivityService.GetActivityByID(activityID); + BoxName.Text = CurrentActivity.Name; + BoxNote.Text = CurrentActivity.Note; + + if(CurrentActivity.HourlyRate % 100 == 0) + { + BoxHourlyRate.Text = (CurrentActivity.HourlyRate/100).ToString(); + } + else + { + BoxHourlyRate.Text = (CurrentActivity.HourlyRate/100) + "," + + (CurrentActivity.HourlyRate%100).ToString().PadRight(2, '0'); + } + + if (BoxHourlyRate.Text == "0") + BoxHourlyRate.Text = null; + + CheckBoxArchived.Checked = CurrentActivity.Archived; + CheckBoxBillable.Checked = CurrentActivity.Billable; + FormGroup.Enabled = true; + BtnSave.Enabled = false; + BoxName.Focus(); + } + + #endregion + + #region Formular zurücksetzen + + private void PrepareFormForNewActivity() + { + ResetForm(); + FormGroup.Text = ActivitiesLabels.GroupBoxTitleCreateMode; + FormGroup.Enabled = true; + CreateNewActivity = true; + CheckBoxArchived.Visible = false; + BtnSave.Enabled = false; + BoxName.Focus(); + } + + private void ResetForm() + { + FormGroup.Text = null; + CheckBoxArchived.Visible = true; + CurrentActivity = null; + CreateNewActivity = false; + ActivActivitiesTree.SelectedNode = null; + ArchivedActivitiesTree.SelectedNode = null; + BoxName.Text = null; + BoxNote.Text = null; + BoxHourlyRate.Text = null; + CheckBoxArchived.Checked = false; + CheckBoxBillable.Checked = false; + FormGroup.Enabled = false; + BtnSave.Enabled = false; + ErrorProvider.Clear(); + } + + #endregion + + #region Leistung erstellen/aktualisieren + + private void BtnSave_Click(object sender, System.EventArgs e) + { + + Cursor = Cursors.WaitCursor; + + var activity = CurrentActivity ?? new Activity(); + activity.Name = BoxName.Text; + activity.Note = BoxNote.Text; + activity.Archived = CheckBoxArchived.Checked; + activity.Billable = CheckBoxBillable.Checked; + + var result = CurrentActivity != null + ? ActivityService.UpdateActivity(activity, BoxHourlyRate.Text) + : ActivityService.CreateActivity(activity, BoxHourlyRate.Text); + + ErrorProvider.Clear(); + + if (result.Count == 0) + { + Helper.StartBackgroundWorker(TreeDataBackgroundWorker, null); + if (CreateNewActivity) + PrepareFormForNewActivity(); + else + BtnSave.Enabled = false; + Cursor = Cursors.Default; + return; + } + + if (result.ContainsKey("Name")) + ErrorProvider.SetError(BoxName, result["Name"]); + + Cursor = Cursors.Default; + + } + + #endregion + + #region Leistung löschen + + private void DeleteActivity(int activityID) + { + + var timeEntries = ObjectFactory.GetInstance().GetTimeEntriesByActivityID(activityID); + + if (timeEntries.Count > 0) + { + MessageBox.Show(ActivitiesLabels.MsgBoxDeleteErrorText, ActivitiesLabels.MsgBoxDeleteQuestionTitle, MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + + var question = MessageBox.Show(ActivitiesLabels.MsgBoxDeleteQuestionText, ActivitiesLabels.MsgBoxDeleteQuestionTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Question); + if (question == DialogResult.Yes) + { + ActivityService.DeleteActivity(activityID); + ResetForm(); + Helper.StartBackgroundWorker(TreeDataBackgroundWorker, null); + } + } + + #endregion + + #region Event-Handler + + private void BtnNewActivity_Click(object sender, System.EventArgs e) + { + PrepareFormForNewActivity(); + } + + private void BtnCancel_Click(object sender, System.EventArgs e) + { + Close(); + } + + void Tree_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) + { + e.Node.TreeView.SelectedNode = e.Node; + LoadSingleActivityInForm(int.Parse(e.Node.Name)); + } + + private void DeleteToolStripMenuItem_Click(object sender, System.EventArgs e) + { + if (ActiveActivitiesTabPage.Visible) + DeleteActivity(int.Parse(ActivActivitiesTree.SelectedNode.Name)); + else + DeleteActivity(int.Parse(ArchivedActivitiesTree.SelectedNode.Name)); + } + + protected override void OnClosed(System.EventArgs e) + { + Helper.StartBackgroundWorker(((Main)Owner).InitializationBackgroundWorker, null); + base.OnClosed(e); + } + + private void EnableBtnSave(object sender, System.EventArgs e) + { + if (FormGroup.Enabled) + BtnSave.Enabled = true; + } + + #endregion + + } +} diff --git a/App/MiteDesk.WinForms/Activities.resx b/App/MiteDesk.WinForms/Activities.resx new file mode 100644 index 0000000..f3715c5 --- /dev/null +++ b/App/MiteDesk.WinForms/Activities.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 32, 22 + + + 172, 22 + + + 289, 22 + + \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Bootstrapper.cs b/App/MiteDesk.WinForms/Bootstrapper.cs new file mode 100644 index 0000000..788d73e --- /dev/null +++ b/App/MiteDesk.WinForms/Bootstrapper.cs @@ -0,0 +1,69 @@ +using SixtyNineDegrees.MiteDesk.Core.Data; +using SixtyNineDegrees.MiteDesk.Core.Infrastructure; +using SixtyNineDegrees.MiteDesk.Core.Services; +using StructureMap; +using StructureMap.Configuration.DSL; +using System.Windows.Forms; + +namespace SixtyNineDegrees.MiteDesk.WinForms +{ + + public class Bootstrapper : IBootstrapper + { + + public void BootstrapStructureMap() + { + const string applicationID = "22b622bd-9a87-4bc8-b3b3-1d6bcd7084f9"; + ObjectFactory.Initialize(c => + { + c.AddRegistry(new InfraStructureRegistry(applicationID)); + c.AddRegistry(new ServicesRegistry()); + c.AddRegistry(new RepositoriesRegistry()); + }); + } + + public static void Initialize() + { + new Bootstrapper().BootstrapStructureMap(); + } + + } + + public class InfraStructureRegistry : Registry + { + public InfraStructureRegistry(string applicationID) + { + ForRequestedType().TheDefaultIsConcreteType(); + ForRequestedType().TheDefaultIsConcreteType(); + ForRequestedType() + .TheDefault.Is.OfConcreteType() + .WithCtorArg("applicationID").EqualTo(applicationID) + .WithCtorArg("executablePath").EqualTo(Application.ExecutablePath); + } + } + + public class ServicesRegistry : Registry + { + public ServicesRegistry() + { + ForRequestedType().TheDefaultIsConcreteType(); + ForRequestedType().TheDefaultIsConcreteType(); + ForRequestedType().TheDefaultIsConcreteType(); + ForRequestedType().TheDefaultIsConcreteType(); + ForRequestedType().TheDefaultIsConcreteType(); + } + } + + public class RepositoriesRegistry : Registry + { + public RepositoriesRegistry() + { + ForRequestedType().TheDefaultIsConcreteType(); + ForRequestedType().TheDefaultIsConcreteType(); + ForRequestedType().TheDefaultIsConcreteType(); + ForRequestedType().TheDefaultIsConcreteType(); + ForRequestedType().TheDefaultIsConcreteType(); + } + } + +} diff --git a/App/MiteDesk.WinForms/Customers.Designer.cs b/App/MiteDesk.WinForms/Customers.Designer.cs new file mode 100644 index 0000000..2604c32 --- /dev/null +++ b/App/MiteDesk.WinForms/Customers.Designer.cs @@ -0,0 +1,284 @@ +namespace SixtyNineDegrees.MiteDesk.WinForms +{ + partial class Customers + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.ActiveCustomersTree = new System.Windows.Forms.TreeView(); + this.TreeContextMenu = new System.Windows.Forms.ContextMenuStrip(this.components); + this.DeleteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.BtnSave = new System.Windows.Forms.Button(); + this.FormGroup = new System.Windows.Forms.GroupBox(); + this.CheckBoxArchived = new System.Windows.Forms.CheckBox(); + this.label2 = new System.Windows.Forms.Label(); + this.BoxNote = new System.Windows.Forms.TextBox(); + this.BoxName = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.TabCustomers = new System.Windows.Forms.TabControl(); + this.ActiveCustomersTabPage = new System.Windows.Forms.TabPage(); + this.ArchivedCustomersTabPage = new System.Windows.Forms.TabPage(); + this.ArchivedCustomersTree = new System.Windows.Forms.TreeView(); + this.ErrorProvider = new System.Windows.Forms.ErrorProvider(this.components); + this.TreeDataBackgroundWorker = new System.ComponentModel.BackgroundWorker(); + this.BtnCancel = new System.Windows.Forms.Button(); + this.BtnNewCustomer = new System.Windows.Forms.Button(); + this.TreeContextMenu.SuspendLayout(); + this.FormGroup.SuspendLayout(); + this.TabCustomers.SuspendLayout(); + this.ActiveCustomersTabPage.SuspendLayout(); + this.ArchivedCustomersTabPage.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.ErrorProvider)).BeginInit(); + this.SuspendLayout(); + // + // ActiveCustomersTree + // + this.ActiveCustomersTree.ContextMenuStrip = this.TreeContextMenu; + this.ActiveCustomersTree.Dock = System.Windows.Forms.DockStyle.Fill; + this.ActiveCustomersTree.FullRowSelect = true; + this.ActiveCustomersTree.HideSelection = false; + this.ActiveCustomersTree.Location = new System.Drawing.Point(3, 3); + this.ActiveCustomersTree.Name = "ActiveCustomersTree"; + this.ActiveCustomersTree.ShowLines = false; + this.ActiveCustomersTree.ShowPlusMinus = false; + this.ActiveCustomersTree.ShowRootLines = false; + this.ActiveCustomersTree.Size = new System.Drawing.Size(175, 245); + this.ActiveCustomersTree.TabIndex = 2; + // + // TreeContextMenu + // + this.TreeContextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.DeleteToolStripMenuItem}); + this.TreeContextMenu.Name = "TreeContextMenu"; + this.TreeContextMenu.Size = new System.Drawing.Size(119, 26); + // + // DeleteToolStripMenuItem + // + this.DeleteToolStripMenuItem.Name = "DeleteToolStripMenuItem"; + this.DeleteToolStripMenuItem.Size = new System.Drawing.Size(118, 22); + this.DeleteToolStripMenuItem.Text = "Löschen"; + this.DeleteToolStripMenuItem.Click += new System.EventHandler(this.DeleteToolStripMenuItem_Click); + // + // BtnSave + // + this.BtnSave.Enabled = false; + this.BtnSave.Location = new System.Drawing.Point(355, 295); + this.BtnSave.Name = "BtnSave"; + this.BtnSave.Size = new System.Drawing.Size(83, 23); + this.BtnSave.TabIndex = 6; + this.BtnSave.Text = "Übernehmen"; + this.BtnSave.UseVisualStyleBackColor = true; + this.BtnSave.Click += new System.EventHandler(this.BtnSave_Click); + // + // FormGroup + // + this.FormGroup.Controls.Add(this.CheckBoxArchived); + this.FormGroup.Controls.Add(this.label2); + this.FormGroup.Controls.Add(this.BoxNote); + this.FormGroup.Controls.Add(this.BoxName); + this.FormGroup.Controls.Add(this.label1); + this.FormGroup.Enabled = false; + this.FormGroup.Location = new System.Drawing.Point(214, 9); + this.FormGroup.Name = "FormGroup"; + this.FormGroup.Size = new System.Drawing.Size(313, 276); + this.FormGroup.TabIndex = 3; + this.FormGroup.TabStop = false; + // + // CheckBoxArchived + // + this.CheckBoxArchived.AutoSize = true; + this.CheckBoxArchived.Location = new System.Drawing.Point(10, 160); + this.CheckBoxArchived.Name = "CheckBoxArchived"; + this.CheckBoxArchived.Size = new System.Drawing.Size(70, 17); + this.CheckBoxArchived.TabIndex = 5; + this.CheckBoxArchived.Text = "Archiviert"; + this.CheckBoxArchived.UseVisualStyleBackColor = true; + this.CheckBoxArchived.CheckedChanged += new System.EventHandler(this.EnableBtnSave); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(7, 63); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(61, 13); + this.label2.TabIndex = 3; + this.label2.Text = "Bemerkung"; + // + // BoxNote + // + this.BoxNote.Location = new System.Drawing.Point(10, 79); + this.BoxNote.Multiline = true; + this.BoxNote.Name = "BoxNote"; + this.BoxNote.Size = new System.Drawing.Size(276, 75); + this.BoxNote.TabIndex = 4; + this.BoxNote.TextChanged += new System.EventHandler(this.EnableBtnSave); + // + // BoxName + // + this.BoxName.Location = new System.Drawing.Point(10, 37); + this.BoxName.Name = "BoxName"; + this.BoxName.Size = new System.Drawing.Size(276, 20); + this.BoxName.TabIndex = 3; + this.BoxName.TextChanged += new System.EventHandler(this.EnableBtnSave); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(7, 20); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(35, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Name"; + // + // TabCustomers + // + this.TabCustomers.Controls.Add(this.ActiveCustomersTabPage); + this.TabCustomers.Controls.Add(this.ArchivedCustomersTabPage); + this.TabCustomers.Location = new System.Drawing.Point(8, 8); + this.TabCustomers.Name = "TabCustomers"; + this.TabCustomers.SelectedIndex = 0; + this.TabCustomers.Size = new System.Drawing.Size(189, 277); + this.TabCustomers.TabIndex = 1; + // + // ActiveCustomersTabPage + // + this.ActiveCustomersTabPage.Controls.Add(this.ActiveCustomersTree); + this.ActiveCustomersTabPage.Location = new System.Drawing.Point(4, 22); + this.ActiveCustomersTabPage.Name = "ActiveCustomersTabPage"; + this.ActiveCustomersTabPage.Padding = new System.Windows.Forms.Padding(3); + this.ActiveCustomersTabPage.Size = new System.Drawing.Size(181, 251); + this.ActiveCustomersTabPage.TabIndex = 0; + this.ActiveCustomersTabPage.Text = "Aktive"; + this.ActiveCustomersTabPage.UseVisualStyleBackColor = true; + // + // ArchivedCustomersTabPage + // + this.ArchivedCustomersTabPage.Controls.Add(this.ArchivedCustomersTree); + this.ArchivedCustomersTabPage.Location = new System.Drawing.Point(4, 22); + this.ArchivedCustomersTabPage.Name = "ArchivedCustomersTabPage"; + this.ArchivedCustomersTabPage.Padding = new System.Windows.Forms.Padding(3); + this.ArchivedCustomersTabPage.Size = new System.Drawing.Size(181, 251); + this.ArchivedCustomersTabPage.TabIndex = 1; + this.ArchivedCustomersTabPage.Text = "Archivierte"; + this.ArchivedCustomersTabPage.UseVisualStyleBackColor = true; + // + // ArchivedCustomersTree + // + this.ArchivedCustomersTree.ContextMenuStrip = this.TreeContextMenu; + this.ArchivedCustomersTree.Dock = System.Windows.Forms.DockStyle.Fill; + this.ArchivedCustomersTree.FullRowSelect = true; + this.ArchivedCustomersTree.HideSelection = false; + this.ArchivedCustomersTree.Location = new System.Drawing.Point(3, 3); + this.ArchivedCustomersTree.Name = "ArchivedCustomersTree"; + this.ArchivedCustomersTree.ShowLines = false; + this.ArchivedCustomersTree.ShowPlusMinus = false; + this.ArchivedCustomersTree.ShowRootLines = false; + this.ArchivedCustomersTree.Size = new System.Drawing.Size(175, 245); + this.ArchivedCustomersTree.TabIndex = 1; + // + // ErrorProvider + // + this.ErrorProvider.BlinkStyle = System.Windows.Forms.ErrorBlinkStyle.NeverBlink; + this.ErrorProvider.ContainerControl = this; + // + // BtnCancel + // + this.BtnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.BtnCancel.Location = new System.Drawing.Point(444, 295); + this.BtnCancel.Name = "BtnCancel"; + this.BtnCancel.Size = new System.Drawing.Size(83, 23); + this.BtnCancel.TabIndex = 7; + this.BtnCancel.Text = "Schließen"; + this.BtnCancel.UseVisualStyleBackColor = true; + this.BtnCancel.Click += new System.EventHandler(this.BtnCancel_Click_1); + // + // BtnNewCustomer + // + this.BtnNewCustomer.Location = new System.Drawing.Point(9, 295); + this.BtnNewCustomer.Name = "BtnNewCustomer"; + this.BtnNewCustomer.Size = new System.Drawing.Size(87, 23); + this.BtnNewCustomer.TabIndex = 9; + this.BtnNewCustomer.Text = "Neuer Kunde"; + this.BtnNewCustomer.UseVisualStyleBackColor = true; + this.BtnNewCustomer.Click += new System.EventHandler(this.BtnNewCustomer_Click); + // + // Customers + // + this.AcceptButton = this.BtnSave; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.BtnCancel; + this.ClientSize = new System.Drawing.Size(537, 327); + this.Controls.Add(this.TabCustomers); + this.Controls.Add(this.FormGroup); + this.Controls.Add(this.BtnNewCustomer); + this.Controls.Add(this.BtnCancel); + this.Controls.Add(this.BtnSave); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MaximumSize = new System.Drawing.Size(553, 365); + this.MinimizeBox = false; + this.MinimumSize = new System.Drawing.Size(553, 365); + this.Name = "Customers"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Kunden"; + this.TreeContextMenu.ResumeLayout(false); + this.FormGroup.ResumeLayout(false); + this.FormGroup.PerformLayout(); + this.TabCustomers.ResumeLayout(false); + this.ActiveCustomersTabPage.ResumeLayout(false); + this.ArchivedCustomersTabPage.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.ErrorProvider)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TreeView ActiveCustomersTree; + private System.Windows.Forms.Button BtnSave; + private System.Windows.Forms.GroupBox FormGroup; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox BoxNote; + private System.Windows.Forms.TextBox BoxName; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TabControl TabCustomers; + private System.Windows.Forms.TabPage ActiveCustomersTabPage; + private System.Windows.Forms.TreeView ArchivedCustomersTree; + private System.Windows.Forms.TabPage ArchivedCustomersTabPage; + private System.Windows.Forms.ErrorProvider ErrorProvider; + private System.ComponentModel.BackgroundWorker TreeDataBackgroundWorker; + private System.Windows.Forms.CheckBox CheckBoxArchived; + private System.Windows.Forms.Button BtnCancel; + private System.Windows.Forms.Button BtnNewCustomer; + private System.Windows.Forms.ContextMenuStrip TreeContextMenu; + private System.Windows.Forms.ToolStripMenuItem DeleteToolStripMenuItem; + } +} \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Customers.cs b/App/MiteDesk.WinForms/Customers.cs new file mode 100644 index 0000000..5e733ee --- /dev/null +++ b/App/MiteDesk.WinForms/Customers.cs @@ -0,0 +1,247 @@ +using System; +using System.Collections.Generic; +using System.Windows.Forms; +using SixtyNineDegrees.MiteDesk.Core.Model; +using SixtyNineDegrees.MiteDesk.Core.Services; +using SixtyNineDegrees.MiteDesk.WinForms.Localization; +using StructureMap; + +namespace SixtyNineDegrees.MiteDesk.WinForms +{ + public partial class Customers : Form + { + + #region Setup + + public Customers() + { + InitializeComponent(); + LocalizeForm(); + + CustomerService = ObjectFactory.GetInstance(); + + TreeDataBackgroundWorker.DoWork += TreeDataBackgroundWorker_DoWork; + TreeDataBackgroundWorker.RunWorkerCompleted += TreeDataBackgroundWorker_RunWorkerCompleted; + Helper.StartBackgroundWorker(TreeDataBackgroundWorker, null); + + ActiveCustomersTree.NodeMouseClick += Tree_NodeMouseClick; + ArchivedCustomersTree.NodeMouseClick += Tree_NodeMouseClick; + + PrepareFormForNewCustomer(); + } + + private IList ActiveCustomers; + private IList ArchivedCustomers; + private readonly ICustomerService CustomerService; + + private Customer CurrentCustomer; + private bool CreateNewCustomer; + + #endregion + + #region Lokalisierung + + private void LocalizeForm() + { + Text = CustomersLabels.FormTitle; + ActiveCustomersTabPage.Text = CustomersLabels.TabActiveTitle; + ArchivedCustomersTabPage.Text = CustomersLabels.TabArchivedTitle; + BtnNewCustomer.Text = CustomersLabels.ButtonNewActivity; + BtnSave.Text = CustomersLabels.ButtonApply; + BtnCancel.Text = CustomersLabels.ButtonCancel; + label1.Text = CustomersLabels.LabelName; + label2.Text = CustomersLabels.LabelNote; + CheckBoxArchived.Text = CustomersLabels.LabelArchived; + DeleteToolStripMenuItem.Text = CustomersLabels.ButtonDelete; + } + + #endregion + + #region Treeviews befüllen + + void TreeDataBackgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) + { + if (TreeDataBackgroundWorker.CancellationPending) + { + e.Cancel = true; + return; + } + ActiveCustomers = CustomerService.GetAllActiveCustomers(); + ArchivedCustomers = CustomerService.GetAllArchivedCustomers(); + } + + void TreeDataBackgroundWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) + { + + if (ActiveCustomers == null || ArchivedCustomers == null || e.Cancelled) + return; + + ActiveCustomersTree.Nodes.Clear(); + + foreach (var customer in ActiveCustomers) + ActiveCustomersTree.Nodes.Add(customer.ID.ToString(), customer.Name); + + ArchivedCustomersTree.Nodes.Clear(); + + foreach (var customer in ArchivedCustomers) + ArchivedCustomersTree.Nodes.Add(customer.ID.ToString(), customer.Name); + } + + #endregion + + #region Kundendatensatz darstellen + + private void LoadSingleCustomerInForm(int customerID) + { + FormGroup.Text = CustomersLabels.GroupBoxTitleEditMode; + CreateNewCustomer = false; + CheckBoxArchived.Visible = true; + CurrentCustomer = CustomerService.GetCustomerByID(customerID); + BoxName.Text = CurrentCustomer.Name; + BoxNote.Text = CurrentCustomer.Note; + CheckBoxArchived.Checked = CurrentCustomer.Archived; + FormGroup.Enabled = true; + BoxName.Focus(); + BtnSave.Enabled = false; + } + + #endregion + + #region Formular zurücksetzen + + private void PrepareFormForNewCustomer() + { + ResetForm(); + FormGroup.Text = CustomersLabels.GroupBoxTitleCreateMode; + BtnSave.Enabled = false; + FormGroup.Enabled = true; + CreateNewCustomer = true; + CheckBoxArchived.Visible = false; + BoxName.Focus(); + } + + private void ResetForm() + { + FormGroup.Text = null; + CheckBoxArchived.Visible = true; + CurrentCustomer = null; + CreateNewCustomer = false; + ActiveCustomersTree.SelectedNode = null; + ArchivedCustomersTree.SelectedNode = null; + BoxName.Text = null; + BoxNote.Text = null; + CheckBoxArchived.Checked = false; + FormGroup.Enabled = false; + BtnSave.Enabled = false; + ErrorProvider.Clear(); + } + + #endregion + + #region Kunde erstellen/aktualisieren + + private void BtnSave_Click(object sender, EventArgs e) + { + + Cursor = Cursors.WaitCursor; + + var customer = CurrentCustomer ?? new Customer(); + customer.Name = BoxName.Text; + customer.Note = BoxNote.Text; + customer.Archived = CheckBoxArchived.Checked; + + var result = CurrentCustomer != null + ? CustomerService.UpdateCustomer(customer) + : CustomerService.CreateCustomer(customer); + + ErrorProvider.Clear(); + + if(result.Count == 0) + { + Helper.StartBackgroundWorker(TreeDataBackgroundWorker, null); + if (CreateNewCustomer) + PrepareFormForNewCustomer(); + else + BtnSave.Enabled = false; + Cursor = Cursors.Default; + return; + } + + if (result.ContainsKey("Name")) + ErrorProvider.SetError(BoxName, result["Name"]); + + Cursor = Cursors.Default; + + } + + #endregion + + #region Kunde löschen + + private void DeleteCustomer(int customerID) + { + + var projects = ObjectFactory.GetInstance().GetProjectsByCustomer(customerID); + + if(projects.Count > 0) + { + MessageBox.Show(CustomersLabels.MsgBoxDeleteErrorText, CustomersLabels.MsgBoxDeleteErrorTitle, MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + + var question = MessageBox.Show(CustomersLabels.MsgBoxDeleteQuestionText, CustomersLabels.MsgBoxDeleteQuestionTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Question); + if (question == DialogResult.Yes) + { + CustomerService.DeleteCustomer(customerID); + ResetForm(); + Helper.StartBackgroundWorker(TreeDataBackgroundWorker, null); + } + } + + #endregion + + #region Event-Handler + + private void BtnNewCustomer_Click(object sender, EventArgs e) + { + PrepareFormForNewCustomer(); + } + + private void BtnCancel_Click_1(object sender, EventArgs e) + { + Close(); + } + + void Tree_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) + { + e.Node.TreeView.SelectedNode = e.Node; + LoadSingleCustomerInForm(int.Parse(e.Node.Name)); + } + + private void DeleteToolStripMenuItem_Click(object sender, EventArgs e) + { + if(ActiveCustomersTabPage.Visible) + DeleteCustomer(int.Parse(ActiveCustomersTree.SelectedNode.Name)); + else + DeleteCustomer(int.Parse(ArchivedCustomersTree.SelectedNode.Name)); + } + + protected override void OnClosed(EventArgs e) + { + if (Owner is Main) + Helper.StartBackgroundWorker(((Main)Owner).InitializationBackgroundWorker, null); + else if(Owner is Projects) + Helper.StartBackgroundWorker(((Projects)Owner).TreeDataBackgroundWorker, null); + base.OnClosed(e); + } + + private void EnableBtnSave(object sender, EventArgs e) + { + if (FormGroup.Enabled) + BtnSave.Enabled = true; + } + + #endregion + + } +} diff --git a/App/MiteDesk.WinForms/Customers.resx b/App/MiteDesk.WinForms/Customers.resx new file mode 100644 index 0000000..04e2688 --- /dev/null +++ b/App/MiteDesk.WinForms/Customers.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 326, 17 + + + 209, 17 + + + 17, 17 + + \ No newline at end of file diff --git a/App/MiteDesk.WinForms/DebugConsole.Designer.cs b/App/MiteDesk.WinForms/DebugConsole.Designer.cs new file mode 100644 index 0000000..8d5ff27 --- /dev/null +++ b/App/MiteDesk.WinForms/DebugConsole.Designer.cs @@ -0,0 +1,150 @@ +namespace SixtyNineDegrees.MiteDesk.WinForms +{ + partial class DebugConsole + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.button1 = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.button2 = new System.Windows.Forms.Button(); + this.button3 = new System.Windows.Forms.Button(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.textBox1); + this.groupBox1.Location = new System.Drawing.Point(13, 65); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(594, 315); + this.groupBox1.TabIndex = 0; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Fehlermeldung"; + // + // textBox1 + // + this.textBox1.HideSelection = false; + this.textBox1.Location = new System.Drawing.Point(9, 22); + this.textBox1.Margin = new System.Windows.Forms.Padding(15); + this.textBox1.Multiline = true; + this.textBox1.Name = "textBox1"; + this.textBox1.ReadOnly = true; + this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Both; + this.textBox1.Size = new System.Drawing.Size(573, 269); + this.textBox1.TabIndex = 0; + // + // button1 + // + this.button1.Location = new System.Drawing.Point(386, 388); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(131, 23); + this.button1.TabIndex = 1; + this.button1.Text = "mite.desk neu starten"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(13, 15); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(251, 13); + this.label1.TabIndex = 2; + this.label1.Text = "Oops ... Es ist leider ein Fehler aufgetreten"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(13, 39); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(597, 13); + this.label2.TabIndex = 3; + this.label2.Text = "Es würde uns sehr helfen, wenn du uns die unten aufgeführte Fehlermeldung per E-M" + + "ail an info@69grad.de senden würdest."; + // + // button2 + // + this.button2.Location = new System.Drawing.Point(532, 388); + this.button2.Name = "button2"; + this.button2.Size = new System.Drawing.Size(75, 23); + this.button2.TabIndex = 7; + this.button2.Text = "OK"; + this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.button2_Click); + // + // button3 + // + this.button3.Location = new System.Drawing.Point(13, 388); + this.button3.Name = "button3"; + this.button3.Size = new System.Drawing.Size(245, 23); + this.button3.TabIndex = 8; + this.button3.Text = "Fehlermeldung in die Zwischenablage kopieren"; + this.button3.UseVisualStyleBackColor = true; + this.button3.Click += new System.EventHandler(this.button3_Click); + // + // DebugConsole + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(619, 418); + this.Controls.Add(this.button3); + this.Controls.Add(this.button2); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.Controls.Add(this.button1); + this.Controls.Add(this.groupBox1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MaximumSize = new System.Drawing.Size(635, 456); + this.MinimizeBox = false; + this.MinimumSize = new System.Drawing.Size(635, 456); + this.Name = "DebugConsole"; + this.ShowIcon = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Es ist ein Fehler aufgetreten"; + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.Button button3; + } +} \ No newline at end of file diff --git a/App/MiteDesk.WinForms/DebugConsole.cs b/App/MiteDesk.WinForms/DebugConsole.cs new file mode 100644 index 0000000..fc67369 --- /dev/null +++ b/App/MiteDesk.WinForms/DebugConsole.cs @@ -0,0 +1,80 @@ +using System; +using System.Windows.Forms; +using SixtyNineDegrees.MiteDesk.WinForms.Localization; + +namespace SixtyNineDegrees.MiteDesk.WinForms +{ + public partial class DebugConsole : Form + { + public DebugConsole(Exception exception) + { + + InitializeComponent(); + LocalizeForm(); + + textBox1.Text = "================================================================" + Environment.NewLine; + textBox1.Text += "Version:" + Environment.NewLine; + textBox1.Text += Helper.CurrentVersion + " (Build " + Helper.CurrentBuild + ")" + Environment.NewLine; + textBox1.Text += "================================================================"; + textBox1.Text += Environment.NewLine + Environment.NewLine; + + textBox1.Text += "================================================================"+ Environment.NewLine; + textBox1.Text += "Message:" + Environment.NewLine; + textBox1.Text += exception.Message + Environment.NewLine; + textBox1.Text += "================================================================"; + textBox1.Text += Environment.NewLine + Environment.NewLine; + + textBox1.Text += "================================================================"+ Environment.NewLine; + textBox1.Text += "StackTrace:" + Environment.NewLine; + textBox1.Text += exception.StackTrace + Environment.NewLine; + textBox1.Text += "================================================================"; + textBox1.Text += Environment.NewLine + Environment.NewLine; + + textBox1.Text += "================================================================"+ Environment.NewLine; + textBox1.Text += "HelpLink:" + Environment.NewLine; + textBox1.Text += exception.HelpLink + Environment.NewLine; + textBox1.Text += "================================================================"; + textBox1.Text += Environment.NewLine + Environment.NewLine; + + textBox1.Text += "================================================================"+ Environment.NewLine; + textBox1.Text += "Source:" + Environment.NewLine; + textBox1.Text += exception.Source + Environment.NewLine; + textBox1.Text += "================================================================"; + textBox1.Text += Environment.NewLine + Environment.NewLine; + + textBox1.Text += "================================================================"+ Environment.NewLine; + textBox1.Text += "TargetSite:" + Environment.NewLine; + textBox1.Text += exception.TargetSite.Name + Environment.NewLine; + textBox1.Text += "================================================================"; + textBox1.Text += Environment.NewLine + Environment.NewLine; + + } + + private void LocalizeForm() + { + Text = DebugConsoleLabels.FormTitle; + groupBox1.Text = DebugConsoleLabels.GroupBoxTitle; + button3.Text = DebugConsoleLabels.CopyToClipboardButton; + button1.Text = DebugConsoleLabels.RestartButton; + button2.Text = DebugConsoleLabels.OKButton; + label1.Text = DebugConsoleLabels.LabelTitle; + label2.Text = DebugConsoleLabels.LabelText; + } + + private void button1_Click(object sender, EventArgs e) + { + Application.Restart(); + } + + private void button2_Click(object sender, EventArgs e) + { + Close(); + } + + private void button3_Click(object sender, EventArgs e) + { + Clipboard.SetText(textBox1.Text); + } + + } +} diff --git a/App/MiteDesk.WinForms/DebugConsole.resx b/App/MiteDesk.WinForms/DebugConsole.resx new file mode 100644 index 0000000..19dc0dd --- /dev/null +++ b/App/MiteDesk.WinForms/DebugConsole.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Localization/ActivitiesLabels.Designer.cs b/App/MiteDesk.WinForms/Localization/ActivitiesLabels.Designer.cs new file mode 100644 index 0000000..95234fc --- /dev/null +++ b/App/MiteDesk.WinForms/Localization/ActivitiesLabels.Designer.cs @@ -0,0 +1,225 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30128.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace SixtyNineDegrees.MiteDesk.WinForms.Localization { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class ActivitiesLabels { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal ActivitiesLabels() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SixtyNineDegrees.MiteDesk.WinForms.Localization.ActivitiesLabels", typeof(ActivitiesLabels).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Übernehmen. + /// + internal static string ButtonApply { + get { + return ResourceManager.GetString("ButtonApply", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Schließen. + /// + internal static string ButtonCancel { + get { + return ResourceManager.GetString("ButtonCancel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Löschen. + /// + internal static string ButtonDelete { + get { + return ResourceManager.GetString("ButtonDelete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Neue Leistung. + /// + internal static string ButtonNewActivity { + get { + return ResourceManager.GetString("ButtonNewActivity", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Leistungen. + /// + internal static string FormTitle { + get { + return ResourceManager.GetString("FormTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Neue Leistung. + /// + internal static string GroupBoxTitleCreateMode { + get { + return ResourceManager.GetString("GroupBoxTitleCreateMode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Leistung bearbeiten. + /// + internal static string GroupBoxTitleEditMode { + get { + return ResourceManager.GetString("GroupBoxTitleEditMode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Archiviert. + /// + internal static string LabelArchived { + get { + return ResourceManager.GetString("LabelArchived", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Verrechenbar?. + /// + internal static string LabelBillable { + get { + return ResourceManager.GetString("LabelBillable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stundensatz in €. + /// + internal static string LabelHourlyNote { + get { + return ResourceManager.GetString("LabelHourlyNote", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Name. + /// + internal static string LabelName { + get { + return ResourceManager.GetString("LabelName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Bemerkung. + /// + internal static string LabelNote { + get { + return ResourceManager.GetString("LabelNote", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Dieser Leistung ist mindestens ein Zeiteintrag zugeordnet, sie darf deshalb nicht gelöscht werden.. + /// + internal static string MsgBoxDeleteErrorText { + get { + return ResourceManager.GetString("MsgBoxDeleteErrorText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Leistung darf nicht gelöscht werden. + /// + internal static string MsgBoxDeleteErrorTitle { + get { + return ResourceManager.GetString("MsgBoxDeleteErrorTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Möchtest du diese Leistung wirklich löschen?. + /// + internal static string MsgBoxDeleteQuestionText { + get { + return ResourceManager.GetString("MsgBoxDeleteQuestionText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Löschen. + /// + internal static string MsgBoxDeleteQuestionTitle { + get { + return ResourceManager.GetString("MsgBoxDeleteQuestionTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Aktive. + /// + internal static string TabActiveTitle { + get { + return ResourceManager.GetString("TabActiveTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Archivierte. + /// + internal static string TabArchivedTitle { + get { + return ResourceManager.GetString("TabArchivedTitle", resourceCulture); + } + } + } +} diff --git a/App/MiteDesk.WinForms/Localization/ActivitiesLabels.en.resx b/App/MiteDesk.WinForms/Localization/ActivitiesLabels.en.resx new file mode 100644 index 0000000..fd2ced4 --- /dev/null +++ b/App/MiteDesk.WinForms/Localization/ActivitiesLabels.en.resx @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Apply + + + Close + + + Delete + + + Add service + + + Services + + + Add service + + + Edit service + + + Archived + + + Billable? + + + Hourly rate in € + + + Name + + + Note + + + This service cannot be deleted. There are associated time entries. + + + Service cannot be deleted + + + Do you really want to delete this service? + + + Delete + + + Active + + + Archived + + \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Localization/ActivitiesLabels.resx b/App/MiteDesk.WinForms/Localization/ActivitiesLabels.resx new file mode 100644 index 0000000..d6c938d --- /dev/null +++ b/App/MiteDesk.WinForms/Localization/ActivitiesLabels.resx @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Übernehmen + + + Schließen + + + Löschen + + + Neue Leistung + + + Leistungen + + + Neue Leistung + + + Leistung bearbeiten + + + Archiviert + + + Verrechenbar? + + + Stundensatz in € + + + Name + + + Bemerkung + + + Dieser Leistung ist mindestens ein Zeiteintrag zugeordnet, sie darf deshalb nicht gelöscht werden. + + + Leistung darf nicht gelöscht werden + + + Möchtest du diese Leistung wirklich löschen? + + + Löschen + + + Aktive + + + Archivierte + + \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Localization/CustomersLabels.Designer.cs b/App/MiteDesk.WinForms/Localization/CustomersLabels.Designer.cs new file mode 100644 index 0000000..d716a0a --- /dev/null +++ b/App/MiteDesk.WinForms/Localization/CustomersLabels.Designer.cs @@ -0,0 +1,207 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30128.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace SixtyNineDegrees.MiteDesk.WinForms.Localization { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class CustomersLabels { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal CustomersLabels() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SixtyNineDegrees.MiteDesk.WinForms.Localization.CustomersLabels", typeof(CustomersLabels).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Übernehmen. + /// + internal static string ButtonApply { + get { + return ResourceManager.GetString("ButtonApply", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Schließen. + /// + internal static string ButtonCancel { + get { + return ResourceManager.GetString("ButtonCancel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Löschen. + /// + internal static string ButtonDelete { + get { + return ResourceManager.GetString("ButtonDelete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Neuer Kunde. + /// + internal static string ButtonNewActivity { + get { + return ResourceManager.GetString("ButtonNewActivity", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Kunden. + /// + internal static string FormTitle { + get { + return ResourceManager.GetString("FormTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Neuer Kunde. + /// + internal static string GroupBoxTitleCreateMode { + get { + return ResourceManager.GetString("GroupBoxTitleCreateMode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Kunde bearbeiten. + /// + internal static string GroupBoxTitleEditMode { + get { + return ResourceManager.GetString("GroupBoxTitleEditMode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Archiviert. + /// + internal static string LabelArchived { + get { + return ResourceManager.GetString("LabelArchived", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Name. + /// + internal static string LabelName { + get { + return ResourceManager.GetString("LabelName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Bemerkung. + /// + internal static string LabelNote { + get { + return ResourceManager.GetString("LabelNote", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Diesem Kunden ist mindestens ein Projekt zugeordnet, er darf deshalb nicht gelöscht werden.. + /// + internal static string MsgBoxDeleteErrorText { + get { + return ResourceManager.GetString("MsgBoxDeleteErrorText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Kunde darf nicht gelöscht werden. + /// + internal static string MsgBoxDeleteErrorTitle { + get { + return ResourceManager.GetString("MsgBoxDeleteErrorTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Möchtest du diesen Kunden wirklich löschen?. + /// + internal static string MsgBoxDeleteQuestionText { + get { + return ResourceManager.GetString("MsgBoxDeleteQuestionText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Löschen. + /// + internal static string MsgBoxDeleteQuestionTitle { + get { + return ResourceManager.GetString("MsgBoxDeleteQuestionTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Aktive. + /// + internal static string TabActiveTitle { + get { + return ResourceManager.GetString("TabActiveTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Archivierte. + /// + internal static string TabArchivedTitle { + get { + return ResourceManager.GetString("TabArchivedTitle", resourceCulture); + } + } + } +} diff --git a/App/MiteDesk.WinForms/Localization/CustomersLabels.en.resx b/App/MiteDesk.WinForms/Localization/CustomersLabels.en.resx new file mode 100644 index 0000000..925e9bc --- /dev/null +++ b/App/MiteDesk.WinForms/Localization/CustomersLabels.en.resx @@ -0,0 +1,168 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Apply + + + Close + + + Delete + + + Add customer + + + Customers + + + Add customer + + + Edit customer + + + Archived + + + Name + + + Note + + + This customer cannot be deleted. There are associated projects. + + + Customer cannot be deleted + + + Do you really want to delete this customer? + + + Delete + + + Active + + + Archived + + \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Localization/CustomersLabels.resx b/App/MiteDesk.WinForms/Localization/CustomersLabels.resx new file mode 100644 index 0000000..b0455a6 --- /dev/null +++ b/App/MiteDesk.WinForms/Localization/CustomersLabels.resx @@ -0,0 +1,168 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Übernehmen + + + Schließen + + + Löschen + + + Neuer Kunde + + + Kunden + + + Neuer Kunde + + + Kunde bearbeiten + + + Archiviert + + + Name + + + Bemerkung + + + Diesem Kunden ist mindestens ein Projekt zugeordnet, er darf deshalb nicht gelöscht werden. + + + Kunde darf nicht gelöscht werden + + + Möchtest du diesen Kunden wirklich löschen? + + + Löschen + + + Aktive + + + Archivierte + + \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Localization/DebugConsoleLabels.Designer.cs b/App/MiteDesk.WinForms/Localization/DebugConsoleLabels.Designer.cs new file mode 100644 index 0000000..db73fb7 --- /dev/null +++ b/App/MiteDesk.WinForms/Localization/DebugConsoleLabels.Designer.cs @@ -0,0 +1,126 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30128.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace SixtyNineDegrees.MiteDesk.WinForms.Localization { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class DebugConsoleLabels { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal DebugConsoleLabels() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SixtyNineDegrees.MiteDesk.WinForms.Localization.DebugConsoleLabels", typeof(DebugConsoleLabels).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Fehlermeldung in die Zwischenablage kopieren. + /// + internal static string CopyToClipboardButton { + get { + return ResourceManager.GetString("CopyToClipboardButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Es ist ein Fehler aufgetreten. + /// + internal static string FormTitle { + get { + return ResourceManager.GetString("FormTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Fehlermeldung. + /// + internal static string GroupBoxTitle { + get { + return ResourceManager.GetString("GroupBoxTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Es würde uns sehr helfen, wenn du uns die unten aufgeführte Fehlermeldung per E-Mail an info@69grad.de senden würdest.. + /// + internal static string LabelText { + get { + return ResourceManager.GetString("LabelText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Oops ... Es ist leider ein Fehler aufgetreten. + /// + internal static string LabelTitle { + get { + return ResourceManager.GetString("LabelTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to OK. + /// + internal static string OKButton { + get { + return ResourceManager.GetString("OKButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to mite.desk neu starten. + /// + internal static string RestartButton { + get { + return ResourceManager.GetString("RestartButton", resourceCulture); + } + } + } +} diff --git a/App/MiteDesk.WinForms/Localization/DebugConsoleLabels.en.resx b/App/MiteDesk.WinForms/Localization/DebugConsoleLabels.en.resx new file mode 100644 index 0000000..4b3217d --- /dev/null +++ b/App/MiteDesk.WinForms/Localization/DebugConsoleLabels.en.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Copy error message to clip board + + + An error has occurred + + + Error Message + + + It would be greatly appreciated if you could send us the error message below via Email to info@69grad.de + + + Oops ... An error has occurred + + + OK + + + Restart mite.desk + + \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Localization/DebugConsoleLabels.resx b/App/MiteDesk.WinForms/Localization/DebugConsoleLabels.resx new file mode 100644 index 0000000..7700f6b --- /dev/null +++ b/App/MiteDesk.WinForms/Localization/DebugConsoleLabels.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Fehlermeldung in die Zwischenablage kopieren + + + Es ist ein Fehler aufgetreten + + + Fehlermeldung + + + Es würde uns sehr helfen, wenn du uns die unten aufgeführte Fehlermeldung per E-Mail an info@69grad.de senden würdest. + + + Oops ... Es ist leider ein Fehler aufgetreten + + + OK + + + mite.desk neu starten + + \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Localization/FeedbackLabels.Designer.cs b/App/MiteDesk.WinForms/Localization/FeedbackLabels.Designer.cs new file mode 100644 index 0000000..bf5b2c0 --- /dev/null +++ b/App/MiteDesk.WinForms/Localization/FeedbackLabels.Designer.cs @@ -0,0 +1,126 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30128.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace SixtyNineDegrees.MiteDesk.WinForms.Localization { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class FeedbackLabels { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal FeedbackLabels() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SixtyNineDegrees.MiteDesk.WinForms.Localization.FeedbackLabels", typeof(FeedbackLabels).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Abbrechen. + /// + internal static string ButtonCancel { + get { + return ResourceManager.GetString("ButtonCancel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Senden. + /// + internal static string ButtonSend { + get { + return ResourceManager.GetString("ButtonSend", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Feedback senden. + /// + internal static string FormTitle { + get { + return ResourceManager.GetString("FormTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to (Nach dem Klick auf Senden öffnet sich dein Mailprogramm). + /// + internal static string LabelInfo { + get { + return ResourceManager.GetString("LabelInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Mein Feedback zu mite.desk:. + /// + internal static string LabelTitle { + get { + return ResourceManager.GetString("LabelTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Das Senden klappt nicht. Wahrscheinlich hast du kein Standard-Mail-Programm konfiguriert. Alternativ kannst du uns eine E-Mail an info@69grad.de senden - danke!. + /// + internal static string MsgBoxErrorText { + get { + return ResourceManager.GetString("MsgBoxErrorText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Senden fehlgeschlagen. + /// + internal static string MsgBoxErrorTitle { + get { + return ResourceManager.GetString("MsgBoxErrorTitle", resourceCulture); + } + } + } +} diff --git a/App/MiteDesk.WinForms/Localization/FeedbackLabels.en.resx b/App/MiteDesk.WinForms/Localization/FeedbackLabels.en.resx new file mode 100644 index 0000000..dee1d0a --- /dev/null +++ b/App/MiteDesk.WinForms/Localization/FeedbackLabels.en.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Cancel + + + Send + + + Give feedback + + + (After clicking the send button your email app will appear) + + + What I ever wanted to tell you about mite.desk: + + + Opening your email app has failed. Propably you don't have configured a default email application on this computer. But you can send us your feedback also by mail to info@69grad.de - thank you! + + + Sending feedback failed + + \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Localization/FeedbackLabels.resx b/App/MiteDesk.WinForms/Localization/FeedbackLabels.resx new file mode 100644 index 0000000..0b2e33a --- /dev/null +++ b/App/MiteDesk.WinForms/Localization/FeedbackLabels.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Abbrechen + + + Senden + + + Feedback senden + + + (Nach dem Klick auf Senden öffnet sich dein Mailprogramm) + + + Mein Feedback zu mite.desk: + + + Das Senden klappt nicht. Wahrscheinlich hast du kein Standard-Mail-Programm konfiguriert. Alternativ kannst du uns eine E-Mail an info@69grad.de senden - danke! + + + Senden fehlgeschlagen + + \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Localization/MainLabels.Designer.cs b/App/MiteDesk.WinForms/Localization/MainLabels.Designer.cs new file mode 100644 index 0000000..4ea9257 --- /dev/null +++ b/App/MiteDesk.WinForms/Localization/MainLabels.Designer.cs @@ -0,0 +1,522 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace SixtyNineDegrees.MiteDesk.WinForms.Localization { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class MainLabels { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal MainLabels() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SixtyNineDegrees.MiteDesk.WinForms.Localization.MainLabels", typeof(MainLabels).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Soll die Stoppuhr angehalten werden?. + /// + internal static string AskForStoppingStopwatch { + get { + return ResourceManager.GetString("AskForStoppingStopwatch", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Verbunden als. + /// + internal static string AuthenticatedAs { + get { + return ResourceManager.GetString("AuthenticatedAs", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Verwerfen. + /// + internal static string ButtonCancel { + get { + return ResourceManager.GetString("ButtonCancel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Erstellen. + /// + internal static string ButtonCreate { + get { + return ResourceManager.GetString("ButtonCreate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Löschen. + /// + internal static string ButtonDelete { + get { + return ResourceManager.GetString("ButtonDelete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Speichern. + /// + internal static string ButtonSave { + get { + return ResourceManager.GetString("ButtonSave", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Verbindung herstellen. + /// + internal static string Connect { + get { + return ResourceManager.GetString("Connect", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Verbunden. + /// + internal static string Connected { + get { + return ResourceManager.GetString("Connected", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Verbindung zu mite. fehlgeschlagen. + /// + internal static string ConnectionFailed { + get { + return ResourceManager.GetString("ConnectionFailed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Verbindung trennen. + /// + internal static string Disconnect { + get { + return ResourceManager.GetString("Disconnect", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Nicht verbunden. + /// + internal static string Disconnected { + get { + return ResourceManager.GetString("Disconnected", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Kalender. + /// + internal static string GroupboxCalendarTitle { + get { + return ResourceManager.GetString("GroupboxCalendarTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Neuen Zeiteintrag erstellen. + /// + internal static string GroupboxFormCreateMode { + get { + return ResourceManager.GetString("GroupboxFormCreateMode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Zeiteintrag bearbeiten. + /// + internal static string GroupboxFormEditMode { + get { + return ResourceManager.GetString("GroupboxFormEditMode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Einträge für den. + /// + internal static string GroupboxTimeEntriesTitle { + get { + return ResourceManager.GetString("GroupboxTimeEntriesTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Leistung. + /// + internal static string LabelActivity { + get { + return ResourceManager.GetString("LabelActivity", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Gewählte Kalenderwoche:. + /// + internal static string LabelCalendarWeek { + get { + return ResourceManager.GetString("LabelCalendarWeek", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Dauer. + /// + internal static string LabelDuration { + get { + return ResourceManager.GetString("LabelDuration", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Bemerkung. + /// + internal static string LabelNote { + get { + return ResourceManager.GetString("LabelNote", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Projekt. + /// + internal static string LabelProject { + get { + return ResourceManager.GetString("LabelProject", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Abgeschlossen. + /// + internal static string Locked { + get { + return ResourceManager.GetString("Locked", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Als abgeschlossen markieren. + /// + internal static string MarkAsLocked { + get { + return ResourceManager.GetString("MarkAsLocked", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Als nicht-abgeschlossen markieren. + /// + internal static string MarkAsUnlocked { + get { + return ResourceManager.GetString("MarkAsUnlocked", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Über mite.desk. + /// + internal static string MenuAbout { + get { + return ResourceManager.GetString("MenuAbout", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Aktivieren. + /// + internal static string MenuActivate { + get { + return ResourceManager.GetString("MenuActivate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Leistungen. + /// + internal static string MenuActivities { + get { + return ResourceManager.GetString("MenuActivities", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Auf Updates prüfen. + /// + internal static string MenuCheckForUpdates { + get { + return ResourceManager.GetString("MenuCheckForUpdates", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Kunden. + /// + internal static string MenuCustomers { + get { + return ResourceManager.GetString("MenuCustomers", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Daten. + /// + internal static string MenuData { + get { + return ResourceManager.GetString("MenuData", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Beenden. + /// + internal static string MenuExit { + get { + return ResourceManager.GetString("MenuExit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Feedback senden. + /// + internal static string MenuFeedback { + get { + return ResourceManager.GetString("MenuFeedback", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to mite.account. + /// + internal static string MenuMiteAccount { + get { + return ResourceManager.GetString("MenuMiteAccount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Neuen Zeiteintrag erstellen. + /// + internal static string MenuNewTimeEntry { + get { + return ResourceManager.GetString("MenuNewTimeEntry", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Onlinehilfe. + /// + internal static string MenuOnlinehelp { + get { + return ResourceManager.GetString("MenuOnlinehelp", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Projekte. + /// + internal static string MenuProjects { + get { + return ResourceManager.GetString("MenuProjects", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Projekte/Leistungen neu laden. + /// + internal static string MenuRefreshProjectsAndActivities { + get { + return ResourceManager.GetString("MenuRefreshProjectsAndActivities", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Einstellungen. + /// + internal static string MenuSettings { + get { + return ResourceManager.GetString("MenuSettings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to mite.desk-Website. + /// + internal static string MenuWebsite { + get { + return ResourceManager.GetString("MenuWebsite", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Die Verbindung zu mite. konnte nicht hergestellt werden. + /// + internal static string MsgBoxConnectionErrorText { + get { + return ResourceManager.GetString("MsgBoxConnectionErrorText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Verbindungsfehler. + /// + internal static string MsgBoxConnectionErrorTitle { + get { + return ResourceManager.GetString("MsgBoxConnectionErrorTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Möchtest du diesen Zeiteintrag wirklich löschen?. + /// + internal static string MsgBoxDeleteTimeEntryText { + get { + return ResourceManager.GetString("MsgBoxDeleteTimeEntryText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Löschen. + /// + internal static string MsgBoxDeleteTimeEntryTitle { + get { + return ResourceManager.GetString("MsgBoxDeleteTimeEntryTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Beenden. + /// + internal static string NotifyContextMenuExit { + get { + return ResourceManager.GetString("NotifyContextMenuExit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Wiederherstellen. + /// + internal static string NotifyContextMenuRestoreWindow { + get { + return ResourceManager.GetString("NotifyContextMenuRestoreWindow", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stoppuhr starten. + /// + internal static string NotifyContextMenuStartStopwatch { + get { + return ResourceManager.GetString("NotifyContextMenuStartStopwatch", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stoppuhr anhalten. + /// + internal static string NotifyContextMenuStopStopwatch { + get { + return ResourceManager.GetString("NotifyContextMenuStopStopwatch", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stoppuhr starten. + /// + internal static string StartStopwatch { + get { + return ResourceManager.GetString("StartStopwatch", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stoppuhr anhalten. + /// + internal static string StopStopwatch { + get { + return ResourceManager.GetString("StopStopwatch", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stoppuhr. + /// + internal static string Stopwatch { + get { + return ResourceManager.GetString("Stopwatch", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stoppuhr nicht gestartet. + /// + internal static string StopwatchNotStarted { + get { + return ResourceManager.GetString("StopwatchNotStarted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Zeit gesamt:. + /// + internal static string TimeTotal { + get { + return ResourceManager.GetString("TimeTotal", resourceCulture); + } + } + } +} diff --git a/App/MiteDesk.WinForms/Localization/MainLabels.en.resx b/App/MiteDesk.WinForms/Localization/MainLabels.en.resx new file mode 100644 index 0000000..c8e7408 --- /dev/null +++ b/App/MiteDesk.WinForms/Localization/MainLabels.en.resx @@ -0,0 +1,273 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Should the stopwatch be stopped? + + + Connected as + + + Cancel + + + Create + + + Delete + + + Save + + + Connect + + + Connected + + + Could not connect to mite. + + + Disconnect + + + Disconnected + + + Calendar + + + Create new time entry + + + Edit time entry + + + Time entries for + + + Service + + + Calendar week: + + + Hours + + + Note + + + Project + + + Locked + + + Lock + + + Unlock + + + About mite.desk + + + Activate + + + Services + + + Check for updates + + + Customers + + + Data + + + Exit + + + Give feedback + + + mite.account + + + Create new time entry + + + Online help + + + Projects + + + Refresh Projects/Services + + + Settings + + + mite.desk website + + + Could not connect to mite. + + + Connection error + + + Do you really want to delete this time entry? + + + Delete + + + Exit + + + Restore + + + Start stopwatch + + + Stop stopwatch + + + Start stopwatch + + + Stop stopwatch + + + Stopwatch + + + Stopwatch not started + + + Time total: + + \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Localization/MainLabels.resx b/App/MiteDesk.WinForms/Localization/MainLabels.resx new file mode 100644 index 0000000..a5b708e --- /dev/null +++ b/App/MiteDesk.WinForms/Localization/MainLabels.resx @@ -0,0 +1,273 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Soll die Stoppuhr angehalten werden? + + + Verbunden als + + + Verwerfen + + + Erstellen + + + Löschen + + + Speichern + + + Verbindung herstellen + + + Verbunden + + + Verbindung zu mite. fehlgeschlagen + + + Verbindung trennen + + + Nicht verbunden + + + Kalender + + + Neuen Zeiteintrag erstellen + + + Zeiteintrag bearbeiten + + + Einträge für den + + + Leistung + + + Gewählte Kalenderwoche: + + + Dauer + + + Bemerkung + + + Projekt + + + Abgeschlossen + + + Als abgeschlossen markieren + + + Als nicht-abgeschlossen markieren + + + Über mite.desk + + + Aktivieren + + + Leistungen + + + Auf Updates prüfen + + + Kunden + + + Daten + + + Beenden + + + Feedback senden + + + mite.account + + + Neuen Zeiteintrag erstellen + + + Onlinehilfe + + + Projekte + + + Projekte/Leistungen neu laden + + + Einstellungen + + + mite.desk-Website + + + Die Verbindung zu mite. konnte nicht hergestellt werden + + + Verbindungsfehler + + + Möchtest du diesen Zeiteintrag wirklich löschen? + + + Löschen + + + Beenden + + + Wiederherstellen + + + Stoppuhr starten + + + Stoppuhr anhalten + + + Stoppuhr starten + + + Stoppuhr anhalten + + + Stoppuhr + + + Stoppuhr nicht gestartet + + + Zeit gesamt: + + \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Localization/ProjectsLabels.Designer.cs b/App/MiteDesk.WinForms/Localization/ProjectsLabels.Designer.cs new file mode 100644 index 0000000..439a17c --- /dev/null +++ b/App/MiteDesk.WinForms/Localization/ProjectsLabels.Designer.cs @@ -0,0 +1,252 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30128.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace SixtyNineDegrees.MiteDesk.WinForms.Localization { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class ProjectsLabels { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal ProjectsLabels() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SixtyNineDegrees.MiteDesk.WinForms.Localization.ProjectsLabels", typeof(ProjectsLabels).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Euro. + /// + internal static string BudgetEuro { + get { + return ResourceManager.GetString("BudgetEuro", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stunden. + /// + internal static string BudgetHours { + get { + return ResourceManager.GetString("BudgetHours", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Übernehmen. + /// + internal static string ButtonApply { + get { + return ResourceManager.GetString("ButtonApply", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Schließen. + /// + internal static string ButtonCancel { + get { + return ResourceManager.GetString("ButtonCancel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Kunden .... + /// + internal static string ButtonCustomers { + get { + return ResourceManager.GetString("ButtonCustomers", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Löschen. + /// + internal static string ButtonDelete { + get { + return ResourceManager.GetString("ButtonDelete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Neues Projekt. + /// + internal static string ButtonNewActivity { + get { + return ResourceManager.GetString("ButtonNewActivity", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Projekte. + /// + internal static string FormTitle { + get { + return ResourceManager.GetString("FormTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Neues Projekt. + /// + internal static string GroupBoxTitleCreateMode { + get { + return ResourceManager.GetString("GroupBoxTitleCreateMode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Projekt bearbeiten. + /// + internal static string GroupBoxTitleEditMode { + get { + return ResourceManager.GetString("GroupBoxTitleEditMode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Archiviert. + /// + internal static string LabelArchived { + get { + return ResourceManager.GetString("LabelArchived", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Budget. + /// + internal static string LabelBudget { + get { + return ResourceManager.GetString("LabelBudget", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Kunde. + /// + internal static string LabelCustomer { + get { + return ResourceManager.GetString("LabelCustomer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Name. + /// + internal static string LabelName { + get { + return ResourceManager.GetString("LabelName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Bemerkung. + /// + internal static string LabelNote { + get { + return ResourceManager.GetString("LabelNote", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Diesem Projekt ist mindestens ein Zeiteintrag zugeordnet, es darf deshalb nicht gelöscht werden.. + /// + internal static string MsgBoxDeleteErrorText { + get { + return ResourceManager.GetString("MsgBoxDeleteErrorText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Projekt darf nicht gelöscht werden. + /// + internal static string MsgBoxDeleteErrorTitle { + get { + return ResourceManager.GetString("MsgBoxDeleteErrorTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Möchtest du dieses Projekt wirklich löschen?. + /// + internal static string MsgBoxDeleteQuestionText { + get { + return ResourceManager.GetString("MsgBoxDeleteQuestionText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Löschen. + /// + internal static string MsgBoxDeleteQuestionTitle { + get { + return ResourceManager.GetString("MsgBoxDeleteQuestionTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Aktive. + /// + internal static string TabActiveTitle { + get { + return ResourceManager.GetString("TabActiveTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Archivierte. + /// + internal static string TabArchivedTitle { + get { + return ResourceManager.GetString("TabArchivedTitle", resourceCulture); + } + } + } +} diff --git a/App/MiteDesk.WinForms/Localization/ProjectsLabels.en.resx b/App/MiteDesk.WinForms/Localization/ProjectsLabels.en.resx new file mode 100644 index 0000000..7bcc721 --- /dev/null +++ b/App/MiteDesk.WinForms/Localization/ProjectsLabels.en.resx @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + euros + + + hours + + + Apply + + + Close + + + Customers ... + + + Delete + + + Add project + + + Projects + + + Add project + + + Edit project + + + Archived + + + Budget + + + Customer + + + Name + + + Note + + + This project cannot be deleted. There are associated time entries. + + + Project cannot be deleted + + + Do you really want to delete this project? + + + Delete + + + Active + + + Archived + + \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Localization/ProjectsLabels.resx b/App/MiteDesk.WinForms/Localization/ProjectsLabels.resx new file mode 100644 index 0000000..7270575 --- /dev/null +++ b/App/MiteDesk.WinForms/Localization/ProjectsLabels.resx @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Euro + + + Stunden + + + Übernehmen + + + Schließen + + + Kunden ... + + + Löschen + + + Neues Projekt + + + Projekte + + + Neues Projekt + + + Projekt bearbeiten + + + Archiviert + + + Budget + + + Kunde + + + Name + + + Bemerkung + + + Diesem Projekt ist mindestens ein Zeiteintrag zugeordnet, es darf deshalb nicht gelöscht werden. + + + Projekt darf nicht gelöscht werden + + + Möchtest du dieses Projekt wirklich löschen? + + + Löschen + + + Aktive + + + Archivierte + + \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Localization/SettingsLabels.Designer.cs b/App/MiteDesk.WinForms/Localization/SettingsLabels.Designer.cs new file mode 100644 index 0000000..a7c3929 --- /dev/null +++ b/App/MiteDesk.WinForms/Localization/SettingsLabels.Designer.cs @@ -0,0 +1,517 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace SixtyNineDegrees.MiteDesk.WinForms.Localization { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class SettingsLabels { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal SettingsLabels() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SixtyNineDegrees.MiteDesk.WinForms.Localization.SettingsLabels", typeof(SettingsLabels).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Account. + /// + internal static string AccountGroupboxTitle { + get { + return ResourceManager.GetString("AccountGroupboxTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Login. + /// + internal static string AccountLabelAccountName { + get { + return ResourceManager.GetString("AccountLabelAccountName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Schlüssel. + /// + internal static string AccountLabelAPIKey { + get { + return ResourceManager.GetString("AccountLabelAPIKey", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Anmelden via. + /// + internal static string AccountLabelConnectBy { + get { + return ResourceManager.GetString("AccountLabelConnectBy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to API-Schlüssel. + /// + internal static string AccountLabelConnectByApiKey { + get { + return ResourceManager.GetString("AccountLabelConnectByApiKey", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to E-Mail/Passwort. + /// + internal static string AccountLabelConnectByEmailAndPassword { + get { + return ResourceManager.GetString("AccountLabelConnectByEmailAndPassword", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Beenden. + /// + internal static string AdvancedGroupboxTitle { + get { + return ResourceManager.GetString("AdvancedGroupboxTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to mite.desk beim Schließen des Fensters nicht beenden, sondern minimieren. + /// + internal static string AdvancedLabelMinimizeByClose { + get { + return ResourceManager.GetString("AdvancedLabelMinimizeByClose", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Vor dem Beenden fragen, ob die Stoppuhr angehalten werden soll. + /// + internal static string AskForStoppingStopwatchByClosing { + get { + return ResourceManager.GetString("AskForStoppingStopwatchByClosing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Abbrechen. + /// + internal static string ButtonCancel { + get { + return ResourceManager.GetString("ButtonCancel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Verbindung testen. + /// + internal static string ButtonCheckConnection { + get { + return ResourceManager.GetString("ButtonCheckConnection", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to OK. + /// + internal static string ButtonOK { + get { + return ResourceManager.GetString("ButtonOK", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Einstellungen. + /// + internal static string FormTitle { + get { + return ResourceManager.GetString("FormTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sprache. + /// + internal static string LanguageGroupboxTitle { + get { + return ResourceManager.GetString("LanguageGroupboxTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Anmeldung per E-Mail/Passwort. + /// + internal static string LoginByAccountGroupboxTitle { + get { + return ResourceManager.GetString("LoginByAccountGroupboxTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to E-Mail-Adresse. + /// + internal static string LoginByAccountLabelEmail { + get { + return ResourceManager.GetString("LoginByAccountLabelEmail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Passwort. + /// + internal static string LoginByAccountLabelPassword { + get { + return ResourceManager.GetString("LoginByAccountLabelPassword", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Passwort anzeigen. + /// + internal static string LoginByAccountLabelShowPassword { + get { + return ResourceManager.GetString("LoginByAccountLabelShowPassword", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Anmeldung per API-Schlüssel. + /// + internal static string LoginByApiKeyGroupboxTitle { + get { + return ResourceManager.GetString("LoginByApiKeyGroupboxTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Fehler: {0} + /// + ///Hast du den Zugriff auf die mite.api in deinem Account aktiviert?. + /// + internal static string MsgBoxConnectionTestFailureText { + get { + return ResourceManager.GetString("MsgBoxConnectionTestFailureText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Verbindung zu mite. fehlgeschlagen.. + /// + internal static string MsgBoxConnectionTestFailureTitle { + get { + return ResourceManager.GetString("MsgBoxConnectionTestFailureTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Anmeldung erfolgreich. Angemeldet als: {0} ({1}). + /// + internal static string MsgBoxConnectionTestSuccessText { + get { + return ResourceManager.GetString("MsgBoxConnectionTestSuccessText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Verbindung erfolgreich hergestellt. + /// + internal static string MsgBoxConnectionTestSuccessTitle { + get { + return ResourceManager.GetString("MsgBoxConnectionTestSuccessTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Gib zunächst deine mite.-Zugangsdaten oder deinen persönlichen API-Schlüssel ein. + /// + ///(Beachte auch, dass zum Zugriff die mite.api für deinen Account aktiviert sein muss.). + /// + internal static string MsgBoxFirstSetupMessageText { + get { + return ResourceManager.GetString("MsgBoxFirstSetupMessageText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Willkommen bei mite.desk!. + /// + internal static string MsgBoxFirstSetupMessageTitle { + get { + return ResourceManager.GetString("MsgBoxFirstSetupMessageTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Passwort. + /// + internal static string ProxyPassword { + get { + return ResourceManager.GetString("ProxyPassword", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Proxy-Server. + /// + internal static string ProxyServer { + get { + return ResourceManager.GetString("ProxyServer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Proxy-Server verwenden. + /// + internal static string ProxyServerLabel { + get { + return ResourceManager.GetString("ProxyServerLabel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Benutzername. + /// + internal static string ProxyUser { + get { + return ResourceManager.GetString("ProxyUser", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sichere Verbindung mit SSL-Verschlüsselung verwenden. + /// + internal static string SecurityConnectBySSL { + get { + return ResourceManager.GetString("SecurityConnectBySSL", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sicherheit. + /// + internal static string SecurityGroupboxTitle { + get { + return ResourceManager.GetString("SecurityGroupboxTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Einträge in umgekehrter Reihenfolge sortieren (neueste zuerst). + /// + internal static string SortDescending { + get { + return ResourceManager.GetString("SortDescending", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Start. + /// + internal static string StartGroupboxTitle { + get { + return ResourceManager.GetString("StartGroupboxTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to mite.desk bei der Windows-Anmeldung automatisch starten (Autostart). + /// + internal static string StartLabelAutostart { + get { + return ResourceManager.GetString("StartLabelAutostart", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Minimiert starten. + /// + internal static string StartLabelStartMinimized { + get { + return ResourceManager.GetString("StartLabelStartMinimized", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Beim Beenden die Stoppuhr automatisch anhalten. + /// + internal static string StopStopwatchByClosing { + get { + return ResourceManager.GetString("StopStopwatchByClosing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Account. + /// + internal static string TabAccount { + get { + return ResourceManager.GetString("TabAccount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Erweitert. + /// + internal static string TabAdvanced { + get { + return ResourceManager.GetString("TabAdvanced", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sprache. + /// + internal static string TabLanguage { + get { + return ResourceManager.GetString("TabLanguage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Netzwerk. + /// + internal static string TabNetwork { + get { + return ResourceManager.GetString("TabNetwork", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Updates. + /// + internal static string TabUpdates { + get { + return ResourceManager.GetString("TabUpdates", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Zeiteinträge. + /// + internal static string TimeEntriesGroupboxTitle { + get { + return ResourceManager.GetString("TimeEntriesGroupboxTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Niemals. + /// + internal static string UpdateCheck0 { + get { + return ResourceManager.GetString("UpdateCheck0", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Alle 14 Tage. + /// + internal static string UpdateCheck14 { + get { + return ResourceManager.GetString("UpdateCheck14", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Alle 30 Tage. + /// + internal static string UpdateCheck30 { + get { + return ResourceManager.GetString("UpdateCheck30", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Alle 7 Tage. + /// + internal static string UpdateCheck7 { + get { + return ResourceManager.GetString("UpdateCheck7", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to nie. + /// + internal static string UpdateCheckNever { + get { + return ResourceManager.GetString("UpdateCheckNever", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Automatische Update-Prüfung. + /// + internal static string UpdatesGroupboxTitle { + get { + return ResourceManager.GetString("UpdatesGroupboxTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Auf Updates prüfen. + /// + internal static string UpdatesLabelInterval { + get { + return ResourceManager.GetString("UpdatesLabelInterval", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Zuletzt geprüft. + /// + internal static string UpdatesLabelLastUpdatecheck { + get { + return ResourceManager.GetString("UpdatesLabelLastUpdatecheck", resourceCulture); + } + } + } +} diff --git a/App/MiteDesk.WinForms/Localization/SettingsLabels.en.resx b/App/MiteDesk.WinForms/Localization/SettingsLabels.en.resx new file mode 100644 index 0000000..53c53a4 --- /dev/null +++ b/App/MiteDesk.WinForms/Localization/SettingsLabels.en.resx @@ -0,0 +1,274 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Account + + + Login + + + API key + + + Authenticate by + + + API key + + + Email and password + + + Closing + + + Don't exit mite.desk by closing the window, minimize it + + + Cancel + + + Check connection + + + OK + + + Settings + + + Language + + + Authenticate by Email/password + + + Email address + + + Password + + + Show password + + + Authenticate by API key + + + Error: {0} + +Did you allow API access in your account settings? + + + Cannot connect to mite. + + + Connected successfully. Authenticated as: {0} ({1}) + + + Connected successfully + + + Please enter your mite. login data or your personal API key. + +(Keep in mind that you have to allow API access in your account) + + + Welcome to mite.desk! + + + Use a secure connection with SSL encryption + + + Security + + + Startup + + + Start mite.desk by logging in in Windows automatically (Autostart) + + + Start minimized + + + Account + + + Advanced + + + Language + + + Network + + + Updates + + + Never + + + Every 14 days + + + Every 30 days + + + Every 7 days + + + never + + + Automatic check for updates + + + Check for updates + + + Last checked + + + Proxy Server + + + Use a proxy server + + + Password + + + User name + + + Ask me for stopping the stopwatch before exiting + + + Automatically stop the stopwatch before exiting + + + Sort entries in reverse order (newest first) + + + Time Entries + + \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Localization/SettingsLabels.resx b/App/MiteDesk.WinForms/Localization/SettingsLabels.resx new file mode 100644 index 0000000..2a38ced --- /dev/null +++ b/App/MiteDesk.WinForms/Localization/SettingsLabels.resx @@ -0,0 +1,274 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Account + + + Login + + + Schlüssel + + + Anmelden via + + + API-Schlüssel + + + E-Mail/Passwort + + + Beenden + + + mite.desk beim Schließen des Fensters nicht beenden, sondern minimieren + + + Abbrechen + + + Verbindung testen + + + OK + + + Einstellungen + + + Sprache + + + Anmeldung per E-Mail/Passwort + + + E-Mail-Adresse + + + Passwort + + + Passwort anzeigen + + + Anmeldung per API-Schlüssel + + + Fehler: {0} + +Hast du den Zugriff auf die mite.api in deinem Account aktiviert? + + + Verbindung zu mite. fehlgeschlagen. + + + Anmeldung erfolgreich. Angemeldet als: {0} ({1}) + + + Verbindung erfolgreich hergestellt + + + Gib zunächst deine mite.-Zugangsdaten oder deinen persönlichen API-Schlüssel ein. + +(Beachte auch, dass zum Zugriff die mite.api für deinen Account aktiviert sein muss.) + + + Willkommen bei mite.desk! + + + Sichere Verbindung mit SSL-Verschlüsselung verwenden + + + Sicherheit + + + Start + + + mite.desk bei der Windows-Anmeldung automatisch starten (Autostart) + + + Minimiert starten + + + Account + + + Erweitert + + + Sprache + + + Netzwerk + + + Updates + + + Niemals + + + Alle 14 Tage + + + Alle 30 Tage + + + Alle 7 Tage + + + nie + + + Automatische Update-Prüfung + + + Auf Updates prüfen + + + Zuletzt geprüft + + + Proxy-Server + + + Proxy-Server verwenden + + + Passwort + + + Benutzername + + + Vor dem Beenden fragen, ob die Stoppuhr angehalten werden soll + + + Beim Beenden die Stoppuhr automatisch anhalten + + + Einträge in umgekehrter Reihenfolge sortieren (neueste zuerst) + + + Zeiteinträge + + \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Localization/UpdateCheckLabels.Designer.cs b/App/MiteDesk.WinForms/Localization/UpdateCheckLabels.Designer.cs new file mode 100644 index 0000000..5931070 --- /dev/null +++ b/App/MiteDesk.WinForms/Localization/UpdateCheckLabels.Designer.cs @@ -0,0 +1,126 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30128.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace SixtyNineDegrees.MiteDesk.WinForms.Localization { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class UpdateCheckLabels { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal UpdateCheckLabels() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SixtyNineDegrees.MiteDesk.WinForms.Localization.UpdateCheckLabels", typeof(UpdateCheckLabels).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Die aktuelle Version konnte nicht abgerufen werden.. + /// + internal static string Error { + get { + return ResourceManager.GetString("Error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to neueste Version. + /// + internal static string LatestVersion { + get { + return ResourceManager.GetString("LatestVersion", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Es ist eine neue mite.desk-Version verfügbar!. + /// + internal static string NewVersionAvailable { + get { + return ResourceManager.GetString("NewVersionAvailable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Möchtest du diese laden?. + /// + internal static string Question { + get { + return ResourceManager.GetString("Question", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Update-Check. + /// + internal static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Du verwendest die aktuellste Version! :-). + /// + internal static string UsingLatestVersion { + get { + return ResourceManager.GetString("UsingLatestVersion", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Deine Version. + /// + internal static string YourVersion { + get { + return ResourceManager.GetString("YourVersion", resourceCulture); + } + } + } +} diff --git a/App/MiteDesk.WinForms/Localization/UpdateCheckLabels.en.resx b/App/MiteDesk.WinForms/Localization/UpdateCheckLabels.en.resx new file mode 100644 index 0000000..17a76db --- /dev/null +++ b/App/MiteDesk.WinForms/Localization/UpdateCheckLabels.en.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Cannot connect to update server. + + + latest version + + + It's a new mite.desk version available! + + + Do you want to download it? + + + Checking for updates + + + You're using the latest version! :-) + + + Your version + + \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Localization/UpdateCheckLabels.resx b/App/MiteDesk.WinForms/Localization/UpdateCheckLabels.resx new file mode 100644 index 0000000..c389425 --- /dev/null +++ b/App/MiteDesk.WinForms/Localization/UpdateCheckLabels.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Die aktuelle Version konnte nicht abgerufen werden. + + + neueste Version + + + Es ist eine neue mite.desk-Version verfügbar! + + + Möchtest du diese laden? + + + Update-Check + + + Du verwendest die aktuellste Version! :-) + + + Deine Version + + \ No newline at end of file diff --git a/App/MiteDesk.WinForms/LoginHelp.Designer.cs b/App/MiteDesk.WinForms/LoginHelp.Designer.cs new file mode 100644 index 0000000..fd6c70d --- /dev/null +++ b/App/MiteDesk.WinForms/LoginHelp.Designer.cs @@ -0,0 +1,84 @@ +namespace SixtyNineDegrees.MiteDesk.WinForms +{ + partial class LoginHelp + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.button1 = new System.Windows.Forms.Button(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.SuspendLayout(); + // + // pictureBox1 + // + this.pictureBox1.BackColor = System.Drawing.SystemColors.Control; + this.pictureBox1.Dock = System.Windows.Forms.DockStyle.Fill; + this.pictureBox1.Image = global::SixtyNineDegrees.MiteDesk.WinForms.Properties.Resources.loginhelp; + this.pictureBox1.Location = new System.Drawing.Point(0, 0); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(505, 283); + this.pictureBox1.TabIndex = 0; + this.pictureBox1.TabStop = false; + // + // button1 + // + this.button1.Location = new System.Drawing.Point(418, 250); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(75, 23); + this.button1.TabIndex = 1; + this.button1.Text = "OK"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // LoginHelp + // + this.AcceptButton = this.button1; + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; + this.ClientSize = new System.Drawing.Size(505, 283); + this.Controls.Add(this.button1); + this.Controls.Add(this.pictureBox1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MaximumSize = new System.Drawing.Size(511, 311); + this.MinimizeBox = false; + this.MinimumSize = new System.Drawing.Size(511, 311); + this.Name = "LoginHelp"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Login"; + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.PictureBox pictureBox1; + private System.Windows.Forms.Button button1; + } +} \ No newline at end of file diff --git a/App/MiteDesk.WinForms/LoginHelp.cs b/App/MiteDesk.WinForms/LoginHelp.cs new file mode 100644 index 0000000..e142180 --- /dev/null +++ b/App/MiteDesk.WinForms/LoginHelp.cs @@ -0,0 +1,18 @@ +using System; +using System.Windows.Forms; + +namespace SixtyNineDegrees.MiteDesk.WinForms +{ + public partial class LoginHelp : Form + { + public LoginHelp() + { + InitializeComponent(); + } + + private void button1_Click(object sender, EventArgs e) + { + Close(); + } + } +} diff --git a/App/MiteDesk.WinForms/LoginHelp.resx b/App/MiteDesk.WinForms/LoginHelp.resx new file mode 100644 index 0000000..19dc0dd --- /dev/null +++ b/App/MiteDesk.WinForms/LoginHelp.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Main.Designer.cs b/App/MiteDesk.WinForms/Main.Designer.cs new file mode 100644 index 0000000..bd175f1 --- /dev/null +++ b/App/MiteDesk.WinForms/Main.Designer.cs @@ -0,0 +1,708 @@ +namespace SixtyNineDegrees.MiteDesk.WinForms +{ + partial class Main + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Main)); + this.MainMenu = new System.Windows.Forms.MenuStrip(); + this.MainMenuItemExit = new System.Windows.Forms.ToolStripMenuItem(); + this.MainMenuItemRefreshActivitiesAndProjects = new System.Windows.Forms.ToolStripMenuItem(); + this.MainMenuItemCreateNewTimeEntry = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); + this.einstellungenToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); + this.beendenToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.datenToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.MainMenuCustomersItem = new System.Windows.Forms.ToolStripMenuItem(); + this.MainMenuProjectsItem = new System.Windows.Forms.ToolStripMenuItem(); + this.MainMenuActivitiesItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); + this.miteZeitenToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.TimeEntryDetailView = new System.Windows.Forms.GroupBox(); + this.Locked = new System.Windows.Forms.CheckBox(); + this.BtnCancel = new System.Windows.Forms.Button(); + this.BtnAccept = new System.Windows.Forms.Button(); + this.Note = new System.Windows.Forms.TextBox(); + this.label5 = new System.Windows.Forms.Label(); + this.Time = new System.Windows.Forms.TextBox(); + this.label4 = new System.Windows.Forms.Label(); + this.ListActivities = new System.Windows.Forms.ComboBox(); + this.ListProjects = new System.Windows.Forms.ComboBox(); + this.label3 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.Calendar = new System.Windows.Forms.MonthCalendar(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.ListTimeEntries = new System.Windows.Forms.ListView(); + this.TextColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.TimeEntryContextMenu = new System.Windows.Forms.ContextMenuStrip(this.components); + this.TimeEntryContextMenuStopStopwatchItem = new System.Windows.Forms.ToolStripMenuItem(); + this.TimeEntryContextMenuStartStopwatchItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator9 = new System.Windows.Forms.ToolStripSeparator(); + this.TimeEntryContextMenuUnlock = new System.Windows.Forms.ToolStripMenuItem(); + this.TimeEntryContextMenuLock = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator8 = new System.Windows.Forms.ToolStripSeparator(); + this.TimeEntryContextMenuDeleteItem = new System.Windows.Forms.ToolStripMenuItem(); + this.StatusBar = new System.Windows.Forms.StatusStrip(); + this.ToolStripConnectionStatus = new System.Windows.Forms.ToolStripDropDownButton(); + this.ToolStripMenuItemDisconnectFromServer = new System.Windows.Forms.ToolStripMenuItem(); + this.ToolStripMenuItemConnectToServer = new System.Windows.Forms.ToolStripMenuItem(); + this.StopwatchStatus = new System.Windows.Forms.ToolStripStatusLabel(); + this.ErrorProvider = new System.Windows.Forms.ErrorProvider(this.components); + this.Stopwatch = new System.Windows.Forms.Timer(this.components); + this.NotifyIcon = new System.Windows.Forms.NotifyIcon(this.components); + this.NotifyContextMenu = new System.Windows.Forms.ContextMenuStrip(this.components); + this.NotifyContextMenuStartStopwatchItem = new System.Windows.Forms.ToolStripMenuItem(); + this.NotifyContextMenuStopStopwatchItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator5 = new System.Windows.Forms.ToolStripSeparator(); + this.NotifyContextMenuOpenMiteDeskItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator6 = new System.Windows.Forms.ToolStripSeparator(); + this.ExitButton = new System.Windows.Forms.ToolStripMenuItem(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.WeekOfYear = new System.Windows.Forms.Label(); + this.InitializationBackgroundWorker = new System.ComponentModel.BackgroundWorker(); + this.TimeEntriesBackgroundWorker = new System.ComponentModel.BackgroundWorker(); + this.MainMenu.SuspendLayout(); + this.TimeEntryDetailView.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.TimeEntryContextMenu.SuspendLayout(); + this.StatusBar.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.ErrorProvider)).BeginInit(); + this.NotifyContextMenu.SuspendLayout(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // MainMenu + // + this.MainMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.MainMenuItemExit, + this.datenToolStripMenuItem, + this.toolStripMenuItem1}); + this.MainMenu.Location = new System.Drawing.Point(0, 0); + this.MainMenu.Name = "MainMenu"; + this.MainMenu.Size = new System.Drawing.Size(572, 24); + this.MainMenu.TabIndex = 1; + this.MainMenu.Text = "MainMenu"; + // + // MainMenuItemExit + // + this.MainMenuItemExit.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.MainMenuItemRefreshActivitiesAndProjects, + this.MainMenuItemCreateNewTimeEntry, + this.toolStripSeparator2, + this.einstellungenToolStripMenuItem, + this.toolStripSeparator1, + this.beendenToolStripMenuItem}); + this.MainMenuItemExit.Name = "MainMenuItemExit"; + this.MainMenuItemExit.Size = new System.Drawing.Size(70, 20); + this.MainMenuItemExit.Text = "mite.desk"; + // + // MainMenuItemRefreshActivitiesAndProjects + // + this.MainMenuItemRefreshActivitiesAndProjects.ImageTransparentColor = System.Drawing.Color.Black; + this.MainMenuItemRefreshActivitiesAndProjects.Name = "MainMenuItemRefreshActivitiesAndProjects"; + this.MainMenuItemRefreshActivitiesAndProjects.ShortcutKeyDisplayString = ""; + this.MainMenuItemRefreshActivitiesAndProjects.ShortcutKeys = System.Windows.Forms.Keys.F5; + this.MainMenuItemRefreshActivitiesAndProjects.Size = new System.Drawing.Size(254, 22); + this.MainMenuItemRefreshActivitiesAndProjects.Text = "Projekte/Leistungen neu laden"; + this.MainMenuItemRefreshActivitiesAndProjects.Click += new System.EventHandler(this.MainMenuItemRefreshProjectsAndActivitiesClick); + // + // MainMenuItemCreateNewTimeEntry + // + this.MainMenuItemCreateNewTimeEntry.ImageTransparentColor = System.Drawing.Color.Black; + this.MainMenuItemCreateNewTimeEntry.Name = "MainMenuItemCreateNewTimeEntry"; + this.MainMenuItemCreateNewTimeEntry.ShortcutKeyDisplayString = ""; + this.MainMenuItemCreateNewTimeEntry.ShortcutKeys = System.Windows.Forms.Keys.F4; + this.MainMenuItemCreateNewTimeEntry.Size = new System.Drawing.Size(254, 22); + this.MainMenuItemCreateNewTimeEntry.Text = "Neuen Zeiteintrag erstellen"; + this.MainMenuItemCreateNewTimeEntry.Click += new System.EventHandler(this.MainMenuItemCreateNewTimeEntryClick); + // + // toolStripSeparator2 + // + this.toolStripSeparator2.Name = "toolStripSeparator2"; + this.toolStripSeparator2.Size = new System.Drawing.Size(251, 6); + // + // einstellungenToolStripMenuItem + // + this.einstellungenToolStripMenuItem.ImageTransparentColor = System.Drawing.Color.Fuchsia; + this.einstellungenToolStripMenuItem.Name = "einstellungenToolStripMenuItem"; + this.einstellungenToolStripMenuItem.ShortcutKeyDisplayString = ""; + this.einstellungenToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.F6; + this.einstellungenToolStripMenuItem.Size = new System.Drawing.Size(254, 22); + this.einstellungenToolStripMenuItem.Text = "Einstellungen"; + this.einstellungenToolStripMenuItem.Click += new System.EventHandler(this.MainMenuItemSettingsClick); + // + // toolStripSeparator1 + // + this.toolStripSeparator1.Name = "toolStripSeparator1"; + this.toolStripSeparator1.Size = new System.Drawing.Size(251, 6); + // + // beendenToolStripMenuItem + // + this.beendenToolStripMenuItem.Name = "beendenToolStripMenuItem"; + this.beendenToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Alt | System.Windows.Forms.Keys.F4))); + this.beendenToolStripMenuItem.Size = new System.Drawing.Size(254, 22); + this.beendenToolStripMenuItem.Text = "Beenden"; + this.beendenToolStripMenuItem.Click += new System.EventHandler(this.Exit); + // + // datenToolStripMenuItem + // + this.datenToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.MainMenuCustomersItem, + this.MainMenuProjectsItem, + this.MainMenuActivitiesItem}); + this.datenToolStripMenuItem.Name = "datenToolStripMenuItem"; + this.datenToolStripMenuItem.Size = new System.Drawing.Size(50, 20); + this.datenToolStripMenuItem.Text = "Daten"; + this.datenToolStripMenuItem.Visible = false; + // + // MainMenuCustomersItem + // + this.MainMenuCustomersItem.Name = "MainMenuCustomersItem"; + this.MainMenuCustomersItem.ShortcutKeys = System.Windows.Forms.Keys.F7; + this.MainMenuCustomersItem.Size = new System.Drawing.Size(151, 22); + this.MainMenuCustomersItem.Text = "Kunden"; + this.MainMenuCustomersItem.Click += new System.EventHandler(this.MainMenuCustomersItem_Click); + // + // MainMenuProjectsItem + // + this.MainMenuProjectsItem.Name = "MainMenuProjectsItem"; + this.MainMenuProjectsItem.ShortcutKeys = System.Windows.Forms.Keys.F8; + this.MainMenuProjectsItem.Size = new System.Drawing.Size(151, 22); + this.MainMenuProjectsItem.Text = "Projekte"; + this.MainMenuProjectsItem.Click += new System.EventHandler(this.MainMenuProjectsItem_Click); + // + // MainMenuActivitiesItem + // + this.MainMenuActivitiesItem.Name = "MainMenuActivitiesItem"; + this.MainMenuActivitiesItem.ShortcutKeys = System.Windows.Forms.Keys.F9; + this.MainMenuActivitiesItem.Size = new System.Drawing.Size(151, 22); + this.MainMenuActivitiesItem.Text = "Leistungen"; + this.MainMenuActivitiesItem.Click += new System.EventHandler(this.MainMenuActivitiesItem_Click); + // + // toolStripMenuItem1 + // + this.toolStripMenuItem1.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.miteZeitenToolStripMenuItem}); + this.toolStripMenuItem1.Name = "toolStripMenuItem1"; + this.toolStripMenuItem1.Size = new System.Drawing.Size(24, 20); + this.toolStripMenuItem1.Text = "?"; + // + // miteZeitenToolStripMenuItem + // + this.miteZeitenToolStripMenuItem.Image = global::SixtyNineDegrees.MiteDesk.WinForms.Properties.Resources.faviconico; + this.miteZeitenToolStripMenuItem.Name = "miteZeitenToolStripMenuItem"; + this.miteZeitenToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.F10; + this.miteZeitenToolStripMenuItem.Size = new System.Drawing.Size(169, 22); + this.miteZeitenToolStripMenuItem.Text = "mite.account"; + this.miteZeitenToolStripMenuItem.Click += new System.EventHandler(this.miteZeitenToolStripMenuItem_Click); + // + // TimeEntryDetailView + // + this.TimeEntryDetailView.Controls.Add(this.Locked); + this.TimeEntryDetailView.Controls.Add(this.BtnCancel); + this.TimeEntryDetailView.Controls.Add(this.BtnAccept); + this.TimeEntryDetailView.Controls.Add(this.Note); + this.TimeEntryDetailView.Controls.Add(this.label5); + this.TimeEntryDetailView.Controls.Add(this.Time); + this.TimeEntryDetailView.Controls.Add(this.label4); + this.TimeEntryDetailView.Controls.Add(this.ListActivities); + this.TimeEntryDetailView.Controls.Add(this.ListProjects); + this.TimeEntryDetailView.Controls.Add(this.label3); + this.TimeEntryDetailView.Controls.Add(this.label2); + this.TimeEntryDetailView.Location = new System.Drawing.Point(7, 34); + this.TimeEntryDetailView.Name = "TimeEntryDetailView"; + this.TimeEntryDetailView.Size = new System.Drawing.Size(343, 216); + this.TimeEntryDetailView.TabIndex = 29; + this.TimeEntryDetailView.TabStop = false; + this.TimeEntryDetailView.Text = "Neuen Zeiteintrag erstellen"; + // + // Locked + // + this.Locked.AutoSize = true; + this.Locked.Location = new System.Drawing.Point(161, 90); + this.Locked.Name = "Locked"; + this.Locked.Size = new System.Drawing.Size(98, 17); + this.Locked.TabIndex = 35; + this.Locked.Text = "Abgeschlossen"; + this.Locked.UseVisualStyleBackColor = true; + this.Locked.Visible = false; + this.Locked.Click += new System.EventHandler(this.Locked_Click); + // + // BtnCancel + // + this.BtnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.BtnCancel.Location = new System.Drawing.Point(161, 185); + this.BtnCancel.Name = "BtnCancel"; + this.BtnCancel.Size = new System.Drawing.Size(75, 23); + this.BtnCancel.TabIndex = 7; + this.BtnCancel.Text = "Verwerfen"; + this.BtnCancel.UseVisualStyleBackColor = true; + this.BtnCancel.Click += new System.EventHandler(this.BtnCancel_Click); + // + // BtnAccept + // + this.BtnAccept.Location = new System.Drawing.Point(78, 185); + this.BtnAccept.Name = "BtnAccept"; + this.BtnAccept.Size = new System.Drawing.Size(75, 23); + this.BtnAccept.TabIndex = 6; + this.BtnAccept.Text = "Erstellen"; + this.BtnAccept.UseVisualStyleBackColor = true; + this.BtnAccept.Click += new System.EventHandler(this.BtnAccept_Click); + // + // Note + // + this.Note.Location = new System.Drawing.Point(78, 120); + this.Note.Multiline = true; + this.Note.Name = "Note"; + this.Note.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.Note.Size = new System.Drawing.Size(242, 59); + this.Note.TabIndex = 5; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(6, 120); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(61, 13); + this.label5.TabIndex = 34; + this.label5.Text = "Bemerkung"; + // + // Time + // + this.ErrorProvider.SetIconPadding(this.Time, 3); + this.Time.Location = new System.Drawing.Point(78, 88); + this.Time.Name = "Time"; + this.Time.Size = new System.Drawing.Size(55, 20); + this.Time.TabIndex = 4; + this.Time.Text = "00:00"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(7, 91); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(36, 13); + this.label4.TabIndex = 32; + this.label4.Text = "Dauer"; + // + // ListActivities + // + this.ListActivities.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend; + this.ListActivities.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.ListItems; + this.ListActivities.FormattingEnabled = true; + this.ErrorProvider.SetIconPadding(this.ListActivities, 3); + this.ListActivities.Location = new System.Drawing.Point(78, 56); + this.ListActivities.Name = "ListActivities"; + this.ListActivities.Size = new System.Drawing.Size(242, 21); + this.ListActivities.TabIndex = 3; + // + // ListProjects + // + this.ListProjects.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.Suggest; + this.ListProjects.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.ListItems; + this.ListProjects.FormattingEnabled = true; + this.ErrorProvider.SetIconPadding(this.ListProjects, 3); + this.ListProjects.Location = new System.Drawing.Point(78, 24); + this.ListProjects.Name = "ListProjects"; + this.ListProjects.Size = new System.Drawing.Size(242, 21); + this.ListProjects.TabIndex = 2; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(6, 59); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(47, 13); + this.label3.TabIndex = 29; + this.label3.Text = "Leistung"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(6, 27); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(40, 13); + this.label2.TabIndex = 28; + this.label2.Text = "Projekt"; + // + // Calendar + // + this.Calendar.BackColor = System.Drawing.SystemColors.Control; + this.Calendar.FirstDayOfWeek = System.Windows.Forms.Day.Monday; + this.Calendar.Location = new System.Drawing.Point(12, 24); + this.Calendar.MaxSelectionCount = 1; + this.Calendar.Name = "Calendar"; + this.Calendar.ShowTodayCircle = false; + this.Calendar.TabIndex = 10; + this.Calendar.DateChanged += new System.Windows.Forms.DateRangeEventHandler(this.SelectDate); + // + // groupBox2 + // + this.groupBox2.Controls.Add(this.ListTimeEntries); + this.groupBox2.Location = new System.Drawing.Point(7, 256); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Padding = new System.Windows.Forms.Padding(10); + this.groupBox2.Size = new System.Drawing.Size(557, 121); + this.groupBox2.TabIndex = 30; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "Einträge für den 09.07.2009"; + // + // ListTimeEntries + // + this.ListTimeEntries.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.TextColumn}); + this.ListTimeEntries.ContextMenuStrip = this.TimeEntryContextMenu; + this.ListTimeEntries.Dock = System.Windows.Forms.DockStyle.Fill; + this.ListTimeEntries.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; + this.ListTimeEntries.HideSelection = false; + this.ListTimeEntries.LabelWrap = false; + this.ListTimeEntries.Location = new System.Drawing.Point(10, 23); + this.ListTimeEntries.MultiSelect = false; + this.ListTimeEntries.Name = "ListTimeEntries"; + this.ListTimeEntries.ShowGroups = false; + this.ListTimeEntries.ShowItemToolTips = true; + this.ListTimeEntries.Size = new System.Drawing.Size(537, 88); + this.ListTimeEntries.TabIndex = 9; + this.ListTimeEntries.UseCompatibleStateImageBehavior = false; + this.ListTimeEntries.View = System.Windows.Forms.View.Details; + this.ListTimeEntries.SelectedIndexChanged += new System.EventHandler(this.TimeEntries_SelectedIndexChanged); + // + // TextColumn + // + this.TextColumn.Text = "TextColumn"; + this.TextColumn.Width = 500; + // + // TimeEntryContextMenu + // + this.TimeEntryContextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.TimeEntryContextMenuStopStopwatchItem, + this.TimeEntryContextMenuStartStopwatchItem, + this.toolStripSeparator9, + this.TimeEntryContextMenuUnlock, + this.TimeEntryContextMenuLock, + this.toolStripSeparator8, + this.TimeEntryContextMenuDeleteItem}); + this.TimeEntryContextMenu.Name = "TimeEntryContextMenu"; + this.TimeEntryContextMenu.Size = new System.Drawing.Size(259, 126); + this.TimeEntryContextMenu.Opening += new System.ComponentModel.CancelEventHandler(this.TimeEntryContextMenu_Opening); + // + // TimeEntryContextMenuStopStopwatchItem + // + this.TimeEntryContextMenuStopStopwatchItem.Name = "TimeEntryContextMenuStopStopwatchItem"; + this.TimeEntryContextMenuStopStopwatchItem.Size = new System.Drawing.Size(258, 22); + this.TimeEntryContextMenuStopStopwatchItem.Text = "Stoppuhr anhalten"; + this.TimeEntryContextMenuStopStopwatchItem.Click += new System.EventHandler(this.TimeEntryContextMenuStopStopwatchItem_Click); + // + // TimeEntryContextMenuStartStopwatchItem + // + this.TimeEntryContextMenuStartStopwatchItem.Name = "TimeEntryContextMenuStartStopwatchItem"; + this.TimeEntryContextMenuStartStopwatchItem.Size = new System.Drawing.Size(258, 22); + this.TimeEntryContextMenuStartStopwatchItem.Text = "Stoppuhr starten"; + this.TimeEntryContextMenuStartStopwatchItem.Click += new System.EventHandler(this.TimeEntryContextMenuStartStopwatchItem_Click); + // + // toolStripSeparator9 + // + this.toolStripSeparator9.Name = "toolStripSeparator9"; + this.toolStripSeparator9.Size = new System.Drawing.Size(255, 6); + // + // TimeEntryContextMenuUnlock + // + this.TimeEntryContextMenuUnlock.Name = "TimeEntryContextMenuUnlock"; + this.TimeEntryContextMenuUnlock.Size = new System.Drawing.Size(258, 22); + this.TimeEntryContextMenuUnlock.Text = "Als nicht-abgeschlossen markieren"; + this.TimeEntryContextMenuUnlock.Visible = false; + this.TimeEntryContextMenuUnlock.Click += new System.EventHandler(this.alsNichtabgeschlossenMarkierenToolStripMenuItem_Click); + // + // TimeEntryContextMenuLock + // + this.TimeEntryContextMenuLock.Name = "TimeEntryContextMenuLock"; + this.TimeEntryContextMenuLock.Size = new System.Drawing.Size(258, 22); + this.TimeEntryContextMenuLock.Text = "Als abgeschlossen markieren"; + this.TimeEntryContextMenuLock.Click += new System.EventHandler(this.alsAbgeschlossenMarkierenToolStripMenuItem_Click); + // + // toolStripSeparator8 + // + this.toolStripSeparator8.Name = "toolStripSeparator8"; + this.toolStripSeparator8.Size = new System.Drawing.Size(255, 6); + // + // TimeEntryContextMenuDeleteItem + // + this.TimeEntryContextMenuDeleteItem.Name = "TimeEntryContextMenuDeleteItem"; + this.TimeEntryContextMenuDeleteItem.Size = new System.Drawing.Size(258, 22); + this.TimeEntryContextMenuDeleteItem.Text = "Löschen"; + this.TimeEntryContextMenuDeleteItem.Click += new System.EventHandler(this.TimeEntryContextMenuDeleteItem_Click); + // + // StatusBar + // + this.StatusBar.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.ToolStripConnectionStatus, + this.StopwatchStatus}); + this.StatusBar.Location = new System.Drawing.Point(0, 386); + this.StatusBar.Name = "StatusBar"; + this.StatusBar.Size = new System.Drawing.Size(572, 22); + this.StatusBar.SizingGrip = false; + this.StatusBar.TabIndex = 31; + this.StatusBar.Text = "statusStrip1"; + // + // ToolStripConnectionStatus + // + this.ToolStripConnectionStatus.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.ToolStripMenuItemDisconnectFromServer, + this.ToolStripMenuItemConnectToServer}); + this.ToolStripConnectionStatus.Image = ((System.Drawing.Image)(resources.GetObject("ToolStripConnectionStatus.Image"))); + this.ToolStripConnectionStatus.ImageTransparentColor = System.Drawing.Color.Magenta; + this.ToolStripConnectionStatus.Name = "ToolStripConnectionStatus"; + this.ToolStripConnectionStatus.Size = new System.Drawing.Size(94, 20); + this.ToolStripConnectionStatus.Text = "Verbunden"; + // + // ToolStripMenuItemDisconnectFromServer + // + this.ToolStripMenuItemDisconnectFromServer.Image = ((System.Drawing.Image)(resources.GetObject("ToolStripMenuItemDisconnectFromServer.Image"))); + this.ToolStripMenuItemDisconnectFromServer.ImageTransparentColor = System.Drawing.Color.Black; + this.ToolStripMenuItemDisconnectFromServer.Name = "ToolStripMenuItemDisconnectFromServer"; + this.ToolStripMenuItemDisconnectFromServer.Size = new System.Drawing.Size(190, 22); + this.ToolStripMenuItemDisconnectFromServer.Text = "Verbindung trennen"; + this.ToolStripMenuItemDisconnectFromServer.Click += new System.EventHandler(this.DisconnectFromServer); + // + // ToolStripMenuItemConnectToServer + // + this.ToolStripMenuItemConnectToServer.Image = ((System.Drawing.Image)(resources.GetObject("ToolStripMenuItemConnectToServer.Image"))); + this.ToolStripMenuItemConnectToServer.ImageTransparentColor = System.Drawing.Color.Black; + this.ToolStripMenuItemConnectToServer.Name = "ToolStripMenuItemConnectToServer"; + this.ToolStripMenuItemConnectToServer.Size = new System.Drawing.Size(190, 22); + this.ToolStripMenuItemConnectToServer.Text = "Verbindung herstellen"; + this.ToolStripMenuItemConnectToServer.Visible = false; + this.ToolStripMenuItemConnectToServer.Click += new System.EventHandler(this.ConnectToServer); + // + // StopwatchStatus + // + this.StopwatchStatus.Image = global::SixtyNineDegrees.MiteDesk.WinForms.Properties.Resources.mini_clock_animated_15x15; + this.StopwatchStatus.ImageAlign = System.Drawing.ContentAlignment.MiddleRight; + this.StopwatchStatus.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.StopwatchStatus.Name = "StopwatchStatus"; + this.StopwatchStatus.Overflow = System.Windows.Forms.ToolStripItemOverflow.Always; + this.StopwatchStatus.Size = new System.Drawing.Size(463, 17); + this.StopwatchStatus.Spring = true; + this.StopwatchStatus.Text = "Stoppuhr: 00:00"; + this.StopwatchStatus.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + this.StopwatchStatus.Visible = false; + this.StopwatchStatus.Click += new System.EventHandler(this.StopwatchStatus_Click); + // + // ErrorProvider + // + this.ErrorProvider.BlinkStyle = System.Windows.Forms.ErrorBlinkStyle.NeverBlink; + this.ErrorProvider.ContainerControl = this; + // + // Stopwatch + // + this.Stopwatch.Interval = 30000; + this.Stopwatch.Tick += new System.EventHandler(this.Stopwatch_Tick); + // + // NotifyIcon + // + this.NotifyIcon.ContextMenuStrip = this.NotifyContextMenu; + // + // NotifyContextMenu + // + this.NotifyContextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.NotifyContextMenuStartStopwatchItem, + this.NotifyContextMenuStopStopwatchItem, + this.toolStripSeparator5, + this.NotifyContextMenuOpenMiteDeskItem, + this.toolStripSeparator6, + this.ExitButton}); + this.NotifyContextMenu.Name = "NotifyContextMenu"; + this.NotifyContextMenu.Size = new System.Drawing.Size(173, 104); + this.NotifyContextMenu.Opening += new System.ComponentModel.CancelEventHandler(this.NotifyContextMenu_Opening); + // + // NotifyContextMenuStartStopwatchItem + // + this.NotifyContextMenuStartStopwatchItem.Name = "NotifyContextMenuStartStopwatchItem"; + this.NotifyContextMenuStartStopwatchItem.Size = new System.Drawing.Size(172, 22); + this.NotifyContextMenuStartStopwatchItem.Text = "Stoppuhr starten"; + this.NotifyContextMenuStartStopwatchItem.Click += new System.EventHandler(this.NotifyContextMenuStartStopwatchItem_Click); + // + // NotifyContextMenuStopStopwatchItem + // + this.NotifyContextMenuStopStopwatchItem.Name = "NotifyContextMenuStopStopwatchItem"; + this.NotifyContextMenuStopStopwatchItem.Size = new System.Drawing.Size(172, 22); + this.NotifyContextMenuStopStopwatchItem.Text = "Stoppuhr anhalten"; + this.NotifyContextMenuStopStopwatchItem.Click += new System.EventHandler(this.NotifyContextMenuStopStopwatchItem_Click); + // + // toolStripSeparator5 + // + this.toolStripSeparator5.Name = "toolStripSeparator5"; + this.toolStripSeparator5.Size = new System.Drawing.Size(169, 6); + // + // NotifyContextMenuOpenMiteDeskItem + // + this.NotifyContextMenuOpenMiteDeskItem.Name = "NotifyContextMenuOpenMiteDeskItem"; + this.NotifyContextMenuOpenMiteDeskItem.Size = new System.Drawing.Size(172, 22); + this.NotifyContextMenuOpenMiteDeskItem.Text = "Wiederherstellen"; + this.NotifyContextMenuOpenMiteDeskItem.Click += new System.EventHandler(this.NotifyContextMenuOpenMiteDeskItem_Click); + // + // toolStripSeparator6 + // + this.toolStripSeparator6.Name = "toolStripSeparator6"; + this.toolStripSeparator6.Size = new System.Drawing.Size(169, 6); + // + // ExitButton + // + this.ExitButton.Name = "ExitButton"; + this.ExitButton.Size = new System.Drawing.Size(172, 22); + this.ExitButton.Text = "Beenden"; + this.ExitButton.Click += new System.EventHandler(this.NotifyContextMenuExitApplicationItem_Click); + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.WeekOfYear); + this.groupBox1.Controls.Add(this.Calendar); + this.groupBox1.Location = new System.Drawing.Point(364, 34); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(200, 216); + this.groupBox1.TabIndex = 34; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Kalender"; + // + // WeekOfYear + // + this.WeekOfYear.AutoSize = true; + this.WeekOfYear.Location = new System.Drawing.Point(9, 195); + this.WeekOfYear.Name = "WeekOfYear"; + this.WeekOfYear.Size = new System.Drawing.Size(135, 13); + this.WeekOfYear.TabIndex = 28; + this.WeekOfYear.Text = "Gewählte Kalenderwoche: "; + // + // InitializationBackgroundWorker + // + this.InitializationBackgroundWorker.WorkerSupportsCancellation = true; + // + // TimeEntriesBackgroundWorker + // + this.TimeEntriesBackgroundWorker.WorkerSupportsCancellation = true; + // + // Main + // + this.AcceptButton = this.BtnAccept; + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; + this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.CancelButton = this.BtnCancel; + this.ClientSize = new System.Drawing.Size(572, 408); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.StatusBar); + this.Controls.Add(this.groupBox2); + this.Controls.Add(this.MainMenu); + this.Controls.Add(this.TimeEntryDetailView); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.HelpButton = true; + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MainMenuStrip = this.MainMenu; + this.MaximizeBox = false; + this.Name = "Main"; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "mite.desk"; + this.MainMenu.ResumeLayout(false); + this.MainMenu.PerformLayout(); + this.TimeEntryDetailView.ResumeLayout(false); + this.TimeEntryDetailView.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.TimeEntryContextMenu.ResumeLayout(false); + this.StatusBar.ResumeLayout(false); + this.StatusBar.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.ErrorProvider)).EndInit(); + this.NotifyContextMenu.ResumeLayout(false); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.MenuStrip MainMenu; + private System.Windows.Forms.ToolStripMenuItem MainMenuItemExit; + private System.Windows.Forms.ToolStripMenuItem beendenToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem1; + private System.Windows.Forms.ToolStripMenuItem einstellungenToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator1; + private System.Windows.Forms.GroupBox TimeEntryDetailView; + private System.Windows.Forms.Button BtnCancel; + private System.Windows.Forms.Button BtnAccept; + private System.Windows.Forms.TextBox Note; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.TextBox Time; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.ComboBox ListActivities; + private System.Windows.Forms.ComboBox ListProjects; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.MonthCalendar Calendar; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.StatusStrip StatusBar; + private System.Windows.Forms.ErrorProvider ErrorProvider; + private System.Windows.Forms.ToolStripStatusLabel StopwatchStatus; + private System.Windows.Forms.ToolStripMenuItem MainMenuItemCreateNewTimeEntry; + private System.Windows.Forms.ToolStripMenuItem MainMenuItemRefreshActivitiesAndProjects; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator2; + private System.Windows.Forms.ToolStripDropDownButton ToolStripConnectionStatus; + private System.Windows.Forms.ToolStripMenuItem ToolStripMenuItemConnectToServer; + private System.Windows.Forms.ToolStripMenuItem ToolStripMenuItemDisconnectFromServer; + private System.Windows.Forms.ListView ListTimeEntries; + private System.Windows.Forms.ColumnHeader TextColumn; + private System.Windows.Forms.Timer Stopwatch; + private System.Windows.Forms.NotifyIcon NotifyIcon; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.Label WeekOfYear; + private System.Windows.Forms.ToolStripMenuItem miteZeitenToolStripMenuItem; + private System.Windows.Forms.ContextMenuStrip TimeEntryContextMenu; + private System.Windows.Forms.ToolStripMenuItem TimeEntryContextMenuStartStopwatchItem; + private System.Windows.Forms.ToolStripMenuItem TimeEntryContextMenuStopStopwatchItem; + private System.Windows.Forms.ContextMenuStrip NotifyContextMenu; + private System.Windows.Forms.ToolStripMenuItem NotifyContextMenuStartStopwatchItem; + private System.Windows.Forms.ToolStripMenuItem NotifyContextMenuStopStopwatchItem; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator5; + private System.Windows.Forms.ToolStripMenuItem NotifyContextMenuOpenMiteDeskItem; + private System.Windows.Forms.ToolStripMenuItem ExitButton; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator6; + private System.ComponentModel.BackgroundWorker TimeEntriesBackgroundWorker; + private System.Windows.Forms.ToolStripMenuItem datenToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem MainMenuCustomersItem; + private System.Windows.Forms.ToolStripMenuItem MainMenuProjectsItem; + private System.Windows.Forms.ToolStripMenuItem MainMenuActivitiesItem; + public System.ComponentModel.BackgroundWorker InitializationBackgroundWorker; + private System.Windows.Forms.CheckBox Locked; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator9; + private System.Windows.Forms.ToolStripMenuItem TimeEntryContextMenuDeleteItem; + private System.Windows.Forms.ToolStripMenuItem TimeEntryContextMenuUnlock; + private System.Windows.Forms.ToolStripMenuItem TimeEntryContextMenuLock; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator8; + } +} \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Main.cs b/App/MiteDesk.WinForms/Main.cs new file mode 100644 index 0000000..3463595 --- /dev/null +++ b/App/MiteDesk.WinForms/Main.cs @@ -0,0 +1,1281 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Windows.Forms; +using SixtyNineDegrees.MiteDesk.Core.Infrastructure; +using SixtyNineDegrees.MiteDesk.Core.Model; +using SixtyNineDegrees.MiteDesk.Core.Services; +using SixtyNineDegrees.MiteDesk.Tools.Connector; +using StructureMap; +using Timer=System.Windows.Forms.Timer; +using SixtyNineDegrees.MiteDesk.WinForms.Localization; + +namespace SixtyNineDegrees.MiteDesk.WinForms +{ + + public partial class Main : Form + { + + #region Setup + + public void Initialize() + { + TimeEntryService = ObjectFactory.GetInstance(); + ConfigurationService = ObjectFactory.GetInstance(); + + SetAppSettings(); + Helper.SetCulture(AppSettings.Culture); + + InitializeComponent(); + LocalizeForm(); + + NotificationClockTimer = new Timer(); + NotificationClockTimer.Interval = 200; + NotificationClockTimer.Tick += NotificationClockTimer_Tick; + + ActiveTimeEntryClockTimer = new Timer(); + ActiveTimeEntryClockTimer.Tick += ActiveTimeEntryClockTimer_Tick; + ActiveTimeEntryClockTimer.Interval = 200; + + InitializationBackgroundWorker.DoWork += InitializationBackgroundWorker_DoWork; + InitializationBackgroundWorker.RunWorkerCompleted += InitializationBackgroundWorker_RunWorkerCompleted; + + TimeEntriesBackgroundWorker.DoWork += TimeEntriesBackgroundWorker_DoWork; + TimeEntriesBackgroundWorker.RunWorkerCompleted += TimeEntriesBackgroundWorker_RunWorkerCompleted; + + ListTimeEntries.MouseClick += TimeEntries_MouseClick; + StopwatchStatus.MouseMove += StopwatchStatus_MouseMove; + StopwatchStatus.MouseLeave += StopwatchStatus_MouseLeave; + + ListProjects.TextUpdate += Helper.ValidateTextEnteredInComboBox; + ListActivities.TextUpdate += Helper.ValidateTextEnteredInComboBox; + + Calendar.SizeChanged += Calendar_SizeChanged; + + InitializeForm(true); + } + + + private AppSettings AppSettings; + + private ITimeEntryService TimeEntryService; + private IConfigurationService ConfigurationService; + + private TimeEntry CurrentTimeEntry; + private Timer NotificationClockTimer; + + private Timer ActiveTimeEntryClockTimer; + private int ActiveTimeEntryClockTimerActiveFrame; + + public bool IsInitialized; + + private int LastTrackedTimeEntryID; + public bool StopInitialization; + + private IList ActiveDates; + private IList TimeEntries; + private IList Projects; + private IList Activities; + private User AuthenticatedUser; + private DateTime CalendarCurrentSelectionStart; + + private TimeEntry trackedTimeEntry; + private TimeEntry TrackedTimeEntry + { + get { return trackedTimeEntry; } + set + { + if (value != null) + { + int minutes = value.Minutes; + trackedTimeEntry = TimeEntryService.GetTimeEntryByID(value.ID); + trackedTimeEntry.Minutes = minutes; + } + else + { + if (trackedTimeEntry != null) + LastTrackedTimeEntryID = trackedTimeEntry.ID; + trackedTimeEntry = null; + } + } + } + + #endregion + + #region Initialisierung + + public void SetAppSettings() + { + AppSettings = ConfigurationService.GetAppSettings(); + } + + public void LocalizeForm() + { + MainMenuItemCreateNewTimeEntry.Text = MainLabels.MenuNewTimeEntry; + MainMenuItemRefreshActivitiesAndProjects.Text = MainLabels.MenuRefreshProjectsAndActivities; + einstellungenToolStripMenuItem.Text = MainLabels.MenuSettings; + beendenToolStripMenuItem.Text = MainLabels.MenuExit; + datenToolStripMenuItem.Text = MainLabels.MenuData; + MainMenuCustomersItem.Text = MainLabels.MenuCustomers; + MainMenuProjectsItem.Text = MainLabels.MenuProjects; + MainMenuActivitiesItem.Text = MainLabels.MenuActivities; + miteZeitenToolStripMenuItem.Text = MainLabels.MenuMiteAccount; + groupBox1.Text = MainLabels.GroupboxCalendarTitle; + TimeEntryContextMenuDeleteItem.Text = MainLabels.ButtonDelete; + BtnCancel.Text = MainLabels.ButtonCancel; + BtnAccept.Text = MainLabels.ButtonCreate; + WeekOfYear.Text = MainLabels.LabelCalendarWeek; + TimeEntryDetailView.Text = MainLabels.GroupboxFormCreateMode; + groupBox2.Text = MainLabels.GroupboxTimeEntriesTitle + string.Format(" {0:d}", Calendar.SelectionStart); + label2.Text = MainLabels.LabelProject; + label3.Text = MainLabels.LabelActivity; + label4.Text = MainLabels.LabelDuration; + label5.Text = MainLabels.LabelNote; + ToolStripConnectionStatus.Text = MainLabels.Connected; + ToolStripMenuItemDisconnectFromServer.Text = MainLabels.Disconnect; + ToolStripMenuItemConnectToServer.Text = MainLabels.Connect; + TimeEntryContextMenuStartStopwatchItem.Text = MainLabels.StartStopwatch; + TimeEntryContextMenuStopStopwatchItem.Text = MainLabels.StopStopwatch; + NotifyContextMenuStartStopwatchItem.Text = MainLabels.NotifyContextMenuStartStopwatch; + NotifyContextMenuStopStopwatchItem.Text = MainLabels.NotifyContextMenuStopStopwatch; + NotifyContextMenuOpenMiteDeskItem.Text = MainLabels.NotifyContextMenuRestoreWindow; + ExitButton.Text = MainLabels.NotifyContextMenuExit; + Locked.Text = MainLabels.Locked; + TimeEntryContextMenuLock.Text = MainLabels.MarkAsLocked; + TimeEntryContextMenuUnlock.Text = MainLabels.MarkAsUnlocked; + } + + public void InitializeForm(bool firstStart) + { + + if (!StopInitialization) + { + if (firstStart && string.IsNullOrEmpty(AppSettings.AccountName)) + new Settings(true).ShowDialog(this); + else + Helper.StartBackgroundWorker(InitializationBackgroundWorker, null); + } + + if (firstStart && AppSettings.StartMinimized) + { + WindowState = FormWindowState.Minimized; + Visible = false; + ShowInTaskbar = false; + } + + if (firstStart) + { + CurrentTimeEntry = null; + TrackedTimeEntry = null; + StopwatchStatus.Text = null; + Stopwatch.Enabled = true; + IsInitialized = true; + ListProjects.Focus(); + } + + EnableOrDisableForm(true); + + Stopwatch_Tick(null, null); + + } + + void InitializationBackgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) + { + if (InitializationBackgroundWorker.CancellationPending) + { + e.Cancel = true; + return; + } + AuthenticatedUser = ObjectFactory.GetInstance().GetAuthenticatedUser(); + Projects = ObjectFactory.GetInstance().GetAllActiveProjects(); + Activities = ObjectFactory.GetInstance().GetAllActiveActivities(); + } + + void InitializationBackgroundWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) + { + try + { + if (!e.Cancelled) + InitializeProjectsAndActivities(); + if (!e.Cancelled) + DisplayAuthenticatedUserInTitleBar(); + if (!e.Cancelled) + SetWeekOfYear(DateTime.Today); + if (!e.Cancelled) + EnableOrDisableDataMenuItem(); + if (!e.Cancelled) + InitializeEntryDetailView(); + if (!e.Cancelled) + Helper.StartBackgroundWorker(TimeEntriesBackgroundWorker, Calendar.SelectionStart); + } + catch (TargetInvocationException) + { + } + } + + private void InitializeEntryDetailView() + { + if (Locked == null || AuthenticatedUser == null) + return; + Locked.Visible = AuthenticatedUser.Role == UserRole.Admin || AuthenticatedUser.Role == UserRole.Owner; + } + + private void InitializeProjectsAndActivities() + { + + if (ListActivities == null || Projects == null) + return; + + int selectedProjectID = ListProjects.SelectedItem != null ? ((ListItem)ListProjects.SelectedItem).Value : AppSettings.SelectedProjectID; + + ListProjects.Items.Clear(); + ListProjects.ValueMember = "Value"; + ListProjects.DisplayMember = "Text"; + ListProjects.Items.Add(new ListItem { Text = string.Empty, Value = 0 }); + + var customers = new Dictionary(); + + foreach (var project in Projects) + { + if(!customers.ContainsKey(project.CustomerID)) + customers.Add(project.CustomerID, project.CustomerName); + } + + if(!customers.ContainsKey(0)) + customers.Add(0, string.Empty); + + customers.OrderBy(c => c.Value).ToDictionary(c => c.Key, c => c.Value); + + foreach (var customer in customers) + { + foreach (var project in Projects.Where(p => p.CustomerID == customer.Key)) + { + ListProjects.Items.Add(new ListItem { Text = !string.IsNullOrEmpty(customer.Value) ? customer.Value + ": " + project.Name : project.Name, Value = project.ID }); + if (selectedProjectID == project.ID) + ListProjects.SelectedItem = ListProjects.Items[ListProjects.Items.Count - 1]; + } + } + + int selectedActivityID = ListActivities.SelectedItem != null ? ((ListItem)ListActivities.SelectedItem).Value : AppSettings.SelectedActivityID; + + ListActivities.Items.Clear(); + ListActivities.ValueMember = "Value"; + ListActivities.DisplayMember = "Text"; + ListActivities.Items.Add(new ListItem { Text = string.Empty, Value = 0 }); + + foreach (var activity in Activities) + { + ListActivities.Items.Add(new ListItem { Text = activity.Name, Value = activity.ID }); + if (selectedActivityID == activity.ID) + ListActivities.SelectedItem = ListActivities.Items[ListActivities.Items.Count - 1]; + } + + } + + #endregion + + #region Menü + + private void EnableOrDisableDataMenuItem() + { + if (AuthenticatedUser != null) + datenToolStripMenuItem.Visible = AuthenticatedUser.Role != UserRole.TimeTracker; + } + + private void Exit(object sender, EventArgs e) + { + if (HandleClosing()) + Application.Exit(); + } + + private void MainMenuItemSettingsClick(object sender, EventArgs e) + { + new Settings(false).ShowDialog(this); + } + + private void MainMenuItemCreateNewTimeEntryClick(object sender, EventArgs e) + { + ResetForm(); + ListProjects.Focus(); + } + + private void MainMenuItemRefreshProjectsAndActivitiesClick(object sender, EventArgs e) + { + Helper.StartBackgroundWorker(InitializationBackgroundWorker, null); + } + + private void miteZeitenToolStripMenuItem_Click(object sender, EventArgs e) + { + Helper.OpenBrowser("https://" + AppSettings.AccountName + ".mite.yo.lk/daily"); + } + + private void MainMenuCustomersItem_Click(object sender, EventArgs e) + { + new Customers().ShowDialog(this); + } + + private void MainMenuProjectsItem_Click(object sender, EventArgs e) + { + new Projects().ShowDialog(this); + } + + private void MainMenuActivitiesItem_Click(object sender, EventArgs e) + { + new Activities().ShowDialog(this); + } + + #endregion + + #region Kopfleiste + + private void DisplayAuthenticatedUserInTitleBar() + { + if (AuthenticatedUser != null) + Text = "mite.desk " + Helper.CurrentVersion + " - " + MainLabels.AuthenticatedAs + " " + AuthenticatedUser.Name + " (" + AuthenticatedUser.Email + ")"; + } + + #endregion + + #region Zeiteintrag erstellen und bearbeiten + + private void CreateOrUpdateTimeEntry(bool update) + { + + TimeEntry entry = update ? CurrentTimeEntry : new TimeEntry(); + + if (ListActivities.SelectedIndex > -1) + entry.ActivityID = ((ListItem)ListActivities.SelectedItem).Value; + + if (ListProjects.SelectedIndex > -1) + entry.ProjectID = ((ListItem)ListProjects.SelectedItem).Value; + + entry.Note = Note.Text; + entry.Date = Calendar.SelectionStart; + entry.Locked = Locked.Checked; + + IDictionary result = update ? TimeEntryService.UpdateTimeEntry(CurrentTimeEntry, Time.Text) : + TimeEntryService.CreateTimeEntry(ref entry, Time.Text); + + if (result.Count > 0) + { + DisplayValidationMessages(result); + return; + } + + ErrorProvider.Clear(); + + if (!update) + { + + if (entry.Minutes == 0 && !entry.Locked) + TimeEntryService.StartStopwatch(entry.ID); + + Helper.StartBackgroundWorker(TimeEntriesBackgroundWorker, Calendar.SelectionStart); + Stopwatch_Tick(null, null); + + Note.Text = string.Empty; + Time.Text = "00:00"; + + } + else + { + CurrentTimeEntry = null; + ResetForm(); + Helper.StartBackgroundWorker(TimeEntriesBackgroundWorker, Calendar.SelectionStart); + } + + AppSettings.SelectedProjectID = entry.ProjectID; + AppSettings.SelectedActivityID = entry.ActivityID; + ConfigurationService.UpdateAppSettings(AppSettings); + + } + + private void Locked_Click(object sender, EventArgs e) + { + if (CurrentTimeEntry != null && CurrentTimeEntry.Locked && !Locked.Checked) + CreateOrUpdateTimeEntry(true); + } + + #endregion + + #region Zeiteintrag löschen + + private void DeleteTimeEntry(object sender, EventArgs e) + { + DeleteTimeEntry(CurrentTimeEntry.ID); + } + + private void DeleteTimeEntry(int entryID) + { + var question = MessageBox.Show(MainLabels.MsgBoxDeleteTimeEntryText, MainLabels.MsgBoxDeleteTimeEntryTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Question); + if (question == DialogResult.Yes) + { + TimeEntryService.DeleteTimeEntry(entryID); + ResetForm(); + Helper.StartBackgroundWorker(TimeEntriesBackgroundWorker, Calendar.SelectionStart); + } + } + + #endregion + + #region Zeiteintrag darstellen + + private void LoadSingleTimeEntryInForm(int id) + { + + CurrentTimeEntry = TimeEntryService.GetTimeEntryByID(id); + + foreach (var project in ListProjects.Items) + { + if (((ListItem)project).Value == CurrentTimeEntry.ProjectID) + { + ListProjects.SelectedItem = project; + break; + } + } + + foreach (var activity in ListActivities.Items) + { + if (((ListItem)activity).Value == CurrentTimeEntry.ActivityID) + { + ListActivities.SelectedItem = activity; + break; + } + } + + var minutes = CurrentTimeEntry.Minutes; + + if (TrackedTimeEntry != null && TrackedTimeEntry.ID == CurrentTimeEntry.ID) + minutes = TrackedTimeEntry.Minutes; + + Time.Text = Helper.GetFormattedTimeText(minutes); + Note.Text = CurrentTimeEntry.Note; + BtnAccept.Text = MainLabels.ButtonSave; + TimeEntryDetailView.Text = MainLabels.GroupboxFormEditMode; + Time.Enabled = CurrentTimeEntry == null || TrackedTimeEntry == null || id != TrackedTimeEntry.ID; + + EnableOrDisableTimeEntryDetailView(); + + } + + private void EnableOrDisableTimeEntryDetailView() + { + ListProjects.Enabled = !CurrentTimeEntry.Locked; + ListActivities.Enabled = !CurrentTimeEntry.Locked; + Time.Enabled = !CurrentTimeEntry.Locked; + Note.Enabled = !CurrentTimeEntry.Locked; + BtnAccept.Enabled = !CurrentTimeEntry.Locked; + BtnCancel.Enabled = !CurrentTimeEntry.Locked; + Locked.Checked = CurrentTimeEntry.Locked; + } + + private void BtnAccept_Click(object sender, EventArgs e) + { + Cursor = Cursors.WaitCursor; + CreateOrUpdateTimeEntry(CurrentTimeEntry != null); + Cursor = Cursors.Default; + } + + private void BtnCancel_Click(object sender, EventArgs e) + { + ResetForm(); + } + + #endregion + + #region Kalender + + private void SelectDate(object sender, DateRangeEventArgs e) + { + Helper.StartBackgroundWorker(TimeEntriesBackgroundWorker, e.Start); + SetWeekOfYear(e.Start); + } + + private void SetWeekOfYear(DateTime date) + { + CultureInfo cultureInfo = CultureInfo.CurrentCulture; + WeekOfYear.Text = MainLabels.LabelCalendarWeek + " " + cultureInfo.Calendar.GetWeekOfYear(date, cultureInfo.DateTimeFormat.CalendarWeekRule, cultureInfo.DateTimeFormat.FirstDayOfWeek); + } + + private void SetCalendarBoldedDays() + { + if(ActiveDates == null) + return; + + var firstDate = new DateTime(Calendar.SelectionStart.Year, Calendar.SelectionStart.Month, 1); + if (CalendarCurrentSelectionStart != firstDate) + { + Calendar.BoldedDates = ActiveDates.ToArray(); + CalendarCurrentSelectionStart = firstDate; + } + } + + void Calendar_SizeChanged(object sender, EventArgs e) + { + int width = ((MonthCalendar) sender).Size.Width; + if(width > 164) + { + Width = 600; + groupBox1.Width = 223; + groupBox2.Width = 580; + } + } + + #endregion + + #region Zeiteinträge + + void TimeEntriesBackgroundWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) + { + try + { + if(!e.Cancelled) + LoadTimeEntries(); + if (!e.Cancelled) + SetCalendarBoldedDays(); + } + catch (TargetInvocationException ex) + {} + } + + void TimeEntriesBackgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) + { + + if(TimeEntriesBackgroundWorker.CancellationPending) + { + e.Cancel = true; + return; + } + + var selectedDate = (DateTime) e.Argument; + var firstDate = new DateTime(selectedDate.Year, selectedDate.Month, 1); + + if (CalendarCurrentSelectionStart != firstDate && AuthenticatedUser != null) + { + var lastDate = firstDate.AddMonths(1).AddDays(-1); + ActiveDates = TimeEntryService.GetTimeEntryDatesByRange(firstDate, lastDate, AuthenticatedUser.ID); + } + + TimeEntries = TimeEntryService.GetTimeEntriesByDate(selectedDate); + + if (AppSettings.SortTimeEntriesDescending) + TimeEntries = TimeEntries.OrderByDescending(entry => entry.ID).ToList(); + + } + + private void LoadTimeEntries() + { + + if (TimeEntries == null) + return; + + ListTimeEntries.SmallImageList = new ImageList(); + + ListViewItem selected = new ListViewItem(); + selected.ImageKey = "-1"; + + if (ListTimeEntries.SelectedItems.Count > 0) + selected = ListTimeEntries.SelectedItems[0]; + + ListTimeEntries.Items.Clear(); + RefreshListTimeEntriesGroupLabel(); + + foreach (var entry in TimeEntries) + { + + var text = GetTimeEntryDisplayText(TrackedTimeEntry != null && TrackedTimeEntry.ID == entry.ID ? TrackedTimeEntry : entry); + ListViewItem item = new ListViewItem(text, entry.ID.ToString()); + item.ToolTipText = text; + item.Selected = item.ImageKey == selected.ImageKey; + + if (TrackedTimeEntry != null && TrackedTimeEntry.ID == entry.ID) + { + item.ForeColor = Color.FromArgb(255, 102, 51); + item.Font = new Font("Microsoft Sans Serif", 8.25f, FontStyle.Bold); + ListTimeEntries.SmallImageList.Images.Add(item.ImageKey, Properties.Resources.mite_desk_icon_16x16_1); + } + else + { + ListTimeEntries.SmallImageList.Images.Add(item.ImageKey, GetIdleIcon(entry.ID)); + } + + ListTimeEntries.Items.Add(item); + } + } + + private void RefreshListTimeEntriesGroupLabel() + { + + if (TimeEntries == null) + return; + + if (TimeEntries.Count == 0) + { + groupBox2.Text = MainLabels.GroupboxTimeEntriesTitle + " " + string.Format("{0:d}", Calendar.SelectionStart); + } + else + { + int minutes = 0; + foreach (var timeEntry in TimeEntries) + minutes += timeEntry.Minutes; + groupBox2.Text = MainLabels.GroupboxTimeEntriesTitle + " " + string.Format("{0:d}", Calendar.SelectionStart) + " (" + TimeEntries.Count + ", " + MainLabels.TimeTotal + " " + Helper.GetFormattedTimeText(minutes) + ")"; + } + } + + private void RefreshTimeEntries() + { + for (int i = 0; i < ListTimeEntries.Items.Count; i++) + { + var item = ListTimeEntries.Items[i]; + if (TrackedTimeEntry != null && item.ImageKey == TrackedTimeEntry.ID.ToString()) + { + var text = GetTimeEntryDisplayText(TrackedTimeEntry); + if (item.ForeColor != Color.FromArgb(255, 102, 51)) + { + item.ForeColor = Color.FromArgb(255, 102, 51); + item.Font = new Font("Microsoft Sans Serif", 8.25f, FontStyle.Bold); + item.Text = text; + item.ToolTipText = item.Text; + ListTimeEntries.SmallImageList.Images[i] = Properties.Resources.mite_desk_icon_16x16_1; + } + else if(item.Text != text) + { + item.Text = text; + } + } + else + { + if (item.ForeColor == Color.FromArgb(255, 102, 51)) + { + item.ForeColor = Color.Black; + item.Font = new Font("Microsoft Sans Serif", 8.25f, FontStyle.Regular); + } + ListTimeEntries.SmallImageList.Images[i] = GetIdleIcon(int.Parse(item.ImageKey)); + } + } + } + + private Bitmap GetIdleIcon(int timeEntryID) + { + if (TimeEntries != null) + { + var entry = TimeEntries.SingleOrDefault(e => e.ID == timeEntryID); + if(entry != null && entry.Locked) + return Properties.Resources.Locked; + } + return Properties.Resources.mite_desk_icon_16x16_1; + } + + void TimeEntries_MouseClick(object sender, MouseEventArgs e) + { + // In den ersten 20 Pixeln ist der Cursor auf dem Stoppuhr-Icon + if(e.X <= 20 && e.Button != MouseButtons.Right && TimeEntries != null && ListTimeEntries.SelectedItems != null && ListTimeEntries.SelectedItems.Count > 0) + { + var entry = TimeEntries.SingleOrDefault(t => t.ID == int.Parse(ListTimeEntries.SelectedItems[0].ImageKey)); + if (entry != null && !entry.Locked) + HandleStopwatch(entry.ID); + } + } + + private static string GetTimeEntryDisplayText(TimeEntry entry) + { + return GetTimeEntryDisplayText(entry, 0); + } + + private static string GetTimeEntryDisplayText(TimeEntry entry, int maxLength) + { + + var text = string.Format(" {0} / {1} / {2} / {3}{4}", + Helper.GetFormattedTimeText(entry.Minutes), + entry.CustomerName, entry.ProjectName, entry.ActivityName, + entry.Note.Trim().Length > 0 ? " (" + entry.Note + ")" : string.Empty); + + if (maxLength == 0 || text.Length <= maxLength) + return text; + + return text.Substring(0, maxLength - 4) + " ..."; + + } + + private void TimeEntries_SelectedIndexChanged(object sender, EventArgs e) + { + if (ListTimeEntries.SelectedItems.Count > 0) + { + Cursor = Cursors.WaitCursor; + LoadSingleTimeEntryInForm(int.Parse(ListTimeEntries.SelectedItems[0].ImageKey)); + Cursor = Cursors.Default; + } + } + + void ActiveTimeEntryClockTimer_Tick(object sender, EventArgs e) + { + + if (ActiveTimeEntryClockTimerActiveFrame == 8) + ActiveTimeEntryClockTimerActiveFrame = 0; + + ActiveTimeEntryClockTimerActiveFrame++; + + for (int i = 0; i < ListTimeEntries.Items.Count; i++) + { + if (TrackedTimeEntry != null && ListTimeEntries.Items[i].ImageKey == TrackedTimeEntry.ID.ToString()) + { + switch (ActiveTimeEntryClockTimerActiveFrame) + { + case 1: + ListTimeEntries.SmallImageList.Images[i] = Properties.Resources.mite_desk_icon_16x16_1; + break; + case 2: + ListTimeEntries.SmallImageList.Images[i] = Properties.Resources.mite_desk_icon_16x16_2; + break; + case 3: + ListTimeEntries.SmallImageList.Images[i] = Properties.Resources.mite_desk_icon_16x16_3; + break; + case 4: + ListTimeEntries.SmallImageList.Images[i] = Properties.Resources.mite_desk_icon_16x16_4; + break; + case 5: + ListTimeEntries.SmallImageList.Images[i] = Properties.Resources.mite_desk_icon_16x16_5; + break; + case 6: + ListTimeEntries.SmallImageList.Images[i] = Properties.Resources.mite_desk_icon_16x16_6; + break; + case 7: + ListTimeEntries.SmallImageList.Images[i] = Properties.Resources.mite_desk_icon_16x16_7; + break; + case 8: + ListTimeEntries.SmallImageList.Images[i] = Properties.Resources.mite_desk_icon_16x16_8; + break; + } + ListTimeEntries.RedrawItems(i, i, false); + break; + } + + } + + } + + #region Kontextmenü + + private void TimeEntryContextMenu_Opening(object sender, System.ComponentModel.CancelEventArgs e) + { + + TimeEntry entry = null; + + if (ListTimeEntries.SelectedItems.Count > 0 && TimeEntries != null) + entry = TimeEntries.SingleOrDefault(t => t.ID == int.Parse(ListTimeEntries.SelectedItems[0].ImageKey)); + + if(entry == null) + { + e.Cancel = true; + return; + } + + TimeEntryContextMenuStartStopwatchItem.Visible = !entry.Locked; + TimeEntryContextMenuStopStopwatchItem.Visible = !entry.Locked; + + toolStripSeparator8.Visible = !entry.Locked; + TimeEntryContextMenuDeleteItem.Visible = !entry.Locked; + TimeEntryContextMenuLock.Visible = !entry.Locked && (AuthenticatedUser.Role == UserRole.Admin || AuthenticatedUser.Role == UserRole.Owner); + TimeEntryContextMenuUnlock.Visible = entry.Locked; + + toolStripSeparator9.Visible = !entry.Locked && (AuthenticatedUser.Role == UserRole.Admin || AuthenticatedUser.Role == UserRole.Owner); + + if (entry.Locked) + return; + + if(TrackedTimeEntry != null && TrackedTimeEntry.ID == entry.ID) + { + TimeEntryContextMenuStartStopwatchItem.Visible = false; + TimeEntryContextMenuStopStopwatchItem.Visible = true; + } + else + { + TimeEntryContextMenuStartStopwatchItem.Visible = true; + TimeEntryContextMenuStopStopwatchItem.Visible = false; + } + + } + + private void TimeEntryContextMenuStartStopwatchItem_Click(object sender, EventArgs e) + { + HandleStopwatch(int.Parse(ListTimeEntries.SelectedItems[0].ImageKey)); + } + + private void TimeEntryContextMenuStopStopwatchItem_Click(object sender, EventArgs e) + { + HandleStopwatch(int.Parse(ListTimeEntries.SelectedItems[0].ImageKey)); + } + + private void TimeEntryContextMenuDeleteItem_Click(object sender, EventArgs e) + { + if (TrackedTimeEntry != null) + HandleStopwatch(int.Parse(ListTimeEntries.SelectedItems[0].ImageKey)); + DeleteTimeEntry(int.Parse(ListTimeEntries.SelectedItems[0].ImageKey)); + } + + private void alsNichtabgeschlossenMarkierenToolStripMenuItem_Click(object sender, EventArgs e) + { + Locked.Checked = false; + CreateOrUpdateTimeEntry(true); + } + + private void alsAbgeschlossenMarkierenToolStripMenuItem_Click(object sender, EventArgs e) + { + if(TrackedTimeEntry != null) + HandleStopwatch(int.Parse(ListTimeEntries.SelectedItems[0].ImageKey)); + Locked.Checked = true; + CreateOrUpdateTimeEntry(true); + } + + #endregion + + #endregion + + #region Formular-Helfer + + private void ResetForm() + { + ListTimeEntries.SelectedItems.Clear(); + Note.Text = null; + Time.Text = "00:00"; + Locked.Checked = false; + BtnAccept.Text = MainLabels.ButtonCreate; + TimeEntryDetailView.Text = MainLabels.GroupboxFormCreateMode; + CurrentTimeEntry = null; + ErrorProvider.Clear(); + RefreshStopwatchStatus(); + ListProjects.Enabled = true; + ListActivities.Enabled = true; + Time.Enabled = true; + Note.Enabled = true; + BtnAccept.Enabled = true; + BtnCancel.Enabled = true; + Locked.Checked = false; + } + + private void DisplayValidationMessages(IDictionary errors) + { + ErrorProvider.Clear(); + if (errors.ContainsKey("Time")) + ErrorProvider.SetError(Time, errors["Time"]); + } + + #endregion + + #region Fehlerbehandlung + + public void NotifyAboutNetworkErrorAndDisableForm(MiteConnectorException exception) + { + EnableOrDisableForm(false); + ToolStripConnectionStatus.Text += " - " + MainLabels.ConnectionFailed; + } + + public void EnableOrDisableForm(bool enabled) + { + + Stopwatch.Enabled = enabled; + Cursor = Cursors.Default; + + Calendar.Enabled = enabled; + ListProjects.Enabled = enabled; + ListActivities.Enabled = enabled; + Time.Enabled = enabled; + Note.Enabled = enabled; + ListTimeEntries.Enabled = enabled; + MainMenuItemCreateNewTimeEntry.Enabled = enabled; + MainMenuItemRefreshActivitiesAndProjects.Enabled = enabled; + MainMenuCustomersItem.Enabled = enabled; + MainMenuProjectsItem.Enabled = enabled; + MainMenuActivitiesItem.Enabled = enabled; + BtnAccept.Enabled = enabled; + BtnCancel.Enabled = enabled; + + if (enabled) + { + DisplayAuthenticatedUserInTitleBar(); + ToolStripConnectionStatus.Text = MainLabels.Connected; + ToolStripMenuItemDisconnectFromServer.Visible = true; + ToolStripMenuItemConnectToServer.Visible = false; + ToolStripConnectionStatus.Image = Properties.Resources.Connected; + } + else + { + Text = "mite.desk " + Helper.CurrentVersion + " - " + MainLabels.Disconnected; + ToolStripConnectionStatus.Text = MainLabels.Disconnected; + ToolStripMenuItemDisconnectFromServer.Visible = false; + ToolStripMenuItemConnectToServer.Visible = true; + ToolStripConnectionStatus.Image = Properties.Resources.Disconnected; + StopwatchStatus.Visible = false; + if (NotificationClockTimer != null && NotificationClockTimer.Enabled) + NotificationClockTimer.Stop(); + if (ActiveTimeEntryClockTimer != null) + ActiveTimeEntryClockTimer.Stop(); + } + } + + #endregion + + #region Online- und Offlinemodus + + private void ConnectToServer(object sender, EventArgs e) + { + try + { + ObjectFactory.GetInstance().GetAuthenticatedUser(); + InitializeForm(false); + } + catch (MiteConnectorException ex) + { + NotifyAboutNetworkErrorAndDisableForm(ex); + MessageBox.Show(MainLabels.MsgBoxConnectionErrorText, MainLabels.MsgBoxConnectionErrorTitle, MessageBoxButtons.OK, MessageBoxIcon.Error); + } + + } + + private void DisconnectFromServer(object sender, EventArgs e) + { + EnableOrDisableForm(false); + } + + #endregion + + #region Stoppuhr + + private void HandleStopwatch(int timeEntryID) + { + Cursor = Cursors.WaitCursor; + Stopwatch.Enabled = false; + + int stoppedTimeEntryID = StopStopwatch(); + if (stoppedTimeEntryID != timeEntryID) + StartStopwatch(timeEntryID); + RefreshStopwatchStatus(); + RefreshTimeEntries(); + RefreshListTimeEntriesGroupLabel(); + + Stopwatch.Enabled = true; + Cursor = Cursors.Default; + } + + private void StartStopwatch(int timeEntryID) + { + TimeEntryService.StartStopwatch(timeEntryID); + TrackedTimeEntry = TimeEntryService.GetTimeEntryCurrentlyTrackedByStopwatch(); + if (NotifyIcon.Visible) + { + NotifyIcon.Icon = Properties.Resources.mitedesk_clock_6; + NotifyIcon.Text = GetTimeEntryDisplayText(TrackedTimeEntry, 63); + NotificationClockTimer.Start(); + } + } + + private int StopStopwatch() + { + int trackedTimeEntryID = TrackedTimeEntry != null ? TrackedTimeEntry.ID : 0; + if (trackedTimeEntryID != 0) + { + TimeEntryService.StopStopwatch(trackedTimeEntryID); + TrackedTimeEntry = null; + Time.Enabled = CurrentTimeEntry == null || !CurrentTimeEntry.Locked; + if (NotifyIcon.Visible) + { + NotifyIcon.Icon = Properties.Resources.mitedesk_clock_6; + NotifyIcon.Text = MainLabels.StopwatchNotStarted; + NotificationClockTimer.Stop(); + } + } + return trackedTimeEntryID; + } + + void StopwatchStatus_MouseLeave(object sender, EventArgs e) + { + Cursor = Cursors.Default; + } + + void StopwatchStatus_MouseMove(object sender, MouseEventArgs e) + { + Cursor = e.X >= 355 ? Cursors.Hand : Cursors.Default; + } + + private void Stopwatch_Tick(object sender, EventArgs e) + { + + bool wasTracking = TrackedTimeEntry != null; + TrackedTimeEntry = TimeEntryService.GetTimeEntryCurrentlyTrackedByStopwatch(); + + bool isTracking = TrackedTimeEntry != null; + bool statusHasChanged = wasTracking != isTracking; + + if (statusHasChanged) + Helper.StartBackgroundWorker(TimeEntriesBackgroundWorker, Calendar.SelectionStart); + + RefreshStopwatchStatus(); + RefreshTimeEntries(); + + if (WindowState == FormWindowState.Minimized) + RefreshTaskbarNotificationInfo(false); + + } + + private void RefreshStopwatchStatus() + { + if (TrackedTimeEntry != null) + { + StopwatchStatus.Text = string.Format(MainLabels.Stopwatch + ": {0}", Helper.GetFormattedTimeText(TrackedTimeEntry.Minutes)); + StopwatchStatus.Visible = true; + ActiveTimeEntryClockTimer.Enabled = true; + if(NotifyIcon.Visible) + { + NotifyIcon.Icon = Properties.Resources.mitedesk_clock_6; + NotifyIcon.Text = GetTimeEntryDisplayText(TrackedTimeEntry, 63); + if (!NotificationClockTimer.Enabled) + NotificationClockTimer.Start(); + } + else + { + if (TimeEntries != null) + { + var timeEntry = TimeEntries.SingleOrDefault(t => t.ID == TrackedTimeEntry.ID); + if (timeEntry != null) + timeEntry.Minutes = TrackedTimeEntry.Minutes; + } + RefreshListTimeEntriesGroupLabel(); + } + if (CurrentTimeEntry != null && CurrentTimeEntry.ID == TrackedTimeEntry.ID) + { + Time.Text = Helper.GetFormattedTimeText(TrackedTimeEntry.Minutes); + Time.Enabled = false; + } + else if(CurrentTimeEntry == null || !CurrentTimeEntry.Locked) + { + Time.Enabled = true; + } + } + else + { + Time.Enabled = CurrentTimeEntry == null || !CurrentTimeEntry.Locked; + StopwatchStatus.Visible = false; + ActiveTimeEntryClockTimer.Enabled = false; + if(NotifyIcon.Visible) + { + NotifyIcon.Icon = Properties.Resources.mitedesk_clock_6; + NotifyIcon.Text = MainLabels.StopwatchNotStarted; + NotificationClockTimer.Stop(); + } + } + } + + private void StopwatchStatus_Click(object sender, EventArgs e) + { + Calendar.SelectionStart = TrackedTimeEntry.Date; + Helper.StartBackgroundWorker(TimeEntriesBackgroundWorker, TrackedTimeEntry.Date); + SetWeekOfYear(TrackedTimeEntry.Date); + } + + #endregion + + #region Minimierung Taskbar + + private const int SizeMinimized = 1; + private const int WmSize = 5; + private const int SwRestore = 9; + + protected override void WndProc(ref Message m) + { + if (m.Msg == WmSize && (int)m.WParam == SizeMinimized) + { + RefreshTaskbarNotificationInfo(true); + } + else if(m.Msg == SingleInstance.WM_SHOWFIRSTINSTANCE) + { + if(!Visible) + ReOpenMainForm(); + Activate(); + } + base.WndProc(ref m); + } + + void RefreshTaskbarNotificationInfo(bool initialize) + { + + Visible = false; + WindowState = FormWindowState.Minimized; + NotifyIcon.Icon = Properties.Resources.mitedesk_clock_6; + + // Nur wenn nicht im offline-Modus + if (Stopwatch.Enabled && TrackedTimeEntry != null) + { + NotifyIcon.Text = GetTimeEntryDisplayText(TrackedTimeEntry, 63); + NotificationIconNumber = 0; + NotificationClockTimer.Start(); + } + else + { + NotifyIcon.Text = MainLabels.StopwatchNotStarted; + } + + if (!initialize) + return; + + NotifyIcon.Visible = true; + NotifyIcon.MouseClick += NotifyIcon_MouseClick; + + } + + private void ReOpenMainForm() + { + if (NotificationClockTimer != null && NotificationClockTimer.Enabled) + NotificationClockTimer.Stop(); + Visible = true; + ShowInTaskbar = true; + NotifyIcon.Visible = false; + WinApi.ShowWindow(Handle, SwRestore); + ListProjects.Focus(); + } + + void NotifyIcon_MouseClick(object sender, MouseEventArgs e) + { + if(e.Button != MouseButtons.Left || e.Clicks > 1) + return; + ReOpenMainForm(); + } + + private void NotifyContextMenu_Opening(object sender, System.ComponentModel.CancelEventArgs e) + { + TimeEntry entry = null; + if (TimeEntries != null) + entry = TimeEntries.SingleOrDefault(t => t.ID == LastTrackedTimeEntryID); + NotifyContextMenuStartStopwatchItem.Visible = NotificationClockTimer != null && !NotificationClockTimer.Enabled; + NotifyContextMenuStartStopwatchItem.Enabled = entry != null && !entry.Locked; + NotifyContextMenuStopStopwatchItem.Visible = NotificationClockTimer != null && NotificationClockTimer.Enabled; + } + + private void NotifyContextMenuStartStopwatchItem_Click(object sender, EventArgs e) + { + HandleStopwatch(LastTrackedTimeEntryID); + } + + private void NotifyContextMenuStopStopwatchItem_Click(object sender, EventArgs e) + { + HandleStopwatch(TrackedTimeEntry.ID); + } + + private void NotifyContextMenuOpenMiteDeskItem_Click(object sender, EventArgs e) + { + ReOpenMainForm(); + } + + private void NotifyContextMenuExitApplicationItem_Click(object sender, EventArgs e) + { + if(HandleClosing()) + Application.Exit(); + } + + #region Stoppuhr in Taskbar + + private int NotificationIconNumber; + + void NotificationClockTimer_Tick(object sender, EventArgs e) + { + + if(WindowState != FormWindowState.Minimized) + { + NotificationClockTimer.Stop(); + return; + } + + if (NotificationIconNumber == 8) + NotificationIconNumber = 0; + + NotificationIconNumber++; + + // bad, bad, bad ... :( + switch(NotificationIconNumber) + { + case 1: + NotifyIcon.Icon = Properties.Resources.mitedesk_clock_1; + break; + case 2: + NotifyIcon.Icon = Properties.Resources.mitedesk_clock_2; + break; + case 3: + NotifyIcon.Icon = Properties.Resources.mitedesk_clock_3; + break; + case 4: + NotifyIcon.Icon = Properties.Resources.mitedesk_clock_4; + break; + case 5: + NotifyIcon.Icon = Properties.Resources.mitedesk_clock_5; + break; + case 6: + NotifyIcon.Icon = Properties.Resources.mitedesk_clock_6; + break; + case 7: + NotifyIcon.Icon = Properties.Resources.mitedesk_clock_7; + break; + case 8: + NotifyIcon.Icon = Properties.Resources.mitedesk_clock_8; + break; + } + } + + #endregion + + #endregion + + #region Schließen/Minimieren + + protected override void OnClosing(System.ComponentModel.CancelEventArgs e) + { + + if (AppSettings.MinimizeByClosing) + { + RefreshTaskbarNotificationInfo(true); + e.Cancel = true; + return; + } + + if (!HandleClosing()) + { + e.Cancel = true; + return; + } + + base.OnClosing(e); + + } + + private bool HandleClosing() + { + + if (AppSettings.StopStopwatchByClosing) + { + StopStopwatch(); + } + + else if (AppSettings.AskForStoppingStopwatchByClosing && TrackedTimeEntry != null) + { + var result = MessageBox.Show(MainLabels.AskForStoppingStopwatch, MainLabels.Stopwatch, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question); + if (result == DialogResult.Yes) + { + StopStopwatch(); + } + else if (result == DialogResult.Cancel) + { + return false; + } + } + + return true; + + } + + #endregion + + + } + +} diff --git a/App/MiteDesk.WinForms/Main.resx b/App/MiteDesk.WinForms/Main.resx new file mode 100644 index 0000000..f156ccf --- /dev/null +++ b/App/MiteDesk.WinForms/Main.resx @@ -0,0 +1,1161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 140, 17 + + + 16, 16 + + + True + + + 255, 17 + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGOSURBVDhPzVK9S0JxFH2puSQhqYOZpU/ylSmYiUpvyBBb + pEKEviAzRzGHoKHNLfoHIseIdg0SopZoDBIKJfpAa7KGIKhcitN9YqJ9UQTRgQOXe+47/O65j/m/SCQS + eGWl9TMIHxbuAKPR+IcGcxoNTnkeWb8fG7EYLm6fygatUimSDgdyPh9SVitkYvHHpk0iEXbsdiAaxUM4 + jPXVNZjIIOXxAKEQSsQhpfLrF8nI5MpmA8bHcBmJYisQACYmAbcbEZXqe+skF1jcD/LA1CSeR0bx3M9j + WaeDiGHeG9Se65Un6WEczEfx6HSi5HTh2GaFh7KJx+N1c1WDPKUtUEhdYDbRi4LPiyKtckMs2vqwMj2L + k+tSdbZ6HaGopYvjcOblkLdYsNfDYdvUiQLV52YzBtq0dbNlg1qIaccltRoZEo+IQZ0MQV6MtEmLHNeF + fYMBGonk8yAXFQocsiwyrB4zcnl1UEV3323vQJYMNrVayOlSFakeLSToGxvRSWx4k3gzaSz1u+mnEupK + +7dgmBcvoP5m2FVw5gAAAABJRU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAK8SURBVDhPfdNrSNNRGMfxo/mmsKigrKAbVOCLQMhRlJWm + dmNEvsguU7Oy0swYomBS5i1aF0S7aTdnZumc2ijXZiVkY6botgSjZVpp2bxjjuqN8e0/MdTIfnDeHB4+ + 53keOGJ83v34zpOBajLfP2RnjQGlNYdzTR/JaxhgtGTyFDuM5HQk8aDvCuouFSn2UEJeejP/TibTL/b8 + H8lqySfWtpHcztNU/zTyaKiC7PYM9phXMq9QhUhz4JYeSkrp838jPuW+7K9fxdnWA+R33eRGZxpFPbXs + Nb9ltroGt0uJiHSBR4ICk8k0EVFoHXhe0LHp6VIiJeS4zR9l0x4Sm7/iZwCP3EZElkCcFrjFzcR3l4SN + j/c5O+4nbSwpXoTv4wVsMC5mvbGOlTpYcfsb2+9bia/UodSUMjfOmxUhMRO7WJZsRcTYcE+pZOq1MGYW + eDEtX8bC7F4eWrvRWPrRtQ1z/7WTW6Z2FMl5qNXqMUCWKgG7qxFHLYh4uzTrCcRFwdrsaFIf2Clp7MPY + MoRlEMy9UPX5FxnXtGNAalU3Ili6CDEgFDWIQ+WIJEGAKmEEyHnaSUmTk8d2JzmGL6SXdRASfgKtdhRx + AfPDDbj5FSKCShFbK5gaPYspoWHIwu6S9aSDQssQaZo2tI291HX+4HD8mYl7cCELw6vxCtQwZ/MNlu9L + wNPvLjPW5SNT6MnSO0jTtnMs/R5qo5VXXcOUVdWPAa64kNUHCvA5UkfQkWfskHayLrIWWagO2ZZYAoO3 + 4e/vj0qlosT8iZs1DgwGaezxcSGuAp+tB/EJimBNwA7ku6KIiE6kSN/Ai+Z+zG1OHrUMc768GblcPhEY + nyrzGyqaBtF/AI3NSbFVOtJ/uKxvJfWyZqSjqKioyYECUzdXXzqJPnV9pPU/x/WqUqkc6XS0dPK4iv/M + PXr1V4T4DTto6F1/z57KAAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAK8SURBVDhPfdNrSNNRGMfxo/mmsKigrKAbVOCLQMhRlJWm + dmNEvsguU7Oy0swYomBS5i1aF0S7aTdnZumc2ijXZiVkY6botgSjZVpp2bxjjuqN8e0/MdTIfnDeHB4+ + 53keOGJ83v34zpOBajLfP2RnjQGlNYdzTR/JaxhgtGTyFDuM5HQk8aDvCuouFSn2UEJeejP/TibTL/b8 + H8lqySfWtpHcztNU/zTyaKiC7PYM9phXMq9QhUhz4JYeSkrp838jPuW+7K9fxdnWA+R33eRGZxpFPbXs + Nb9ltroGt0uJiHSBR4ICk8k0EVFoHXhe0LHp6VIiJeS4zR9l0x4Sm7/iZwCP3EZElkCcFrjFzcR3l4SN + j/c5O+4nbSwpXoTv4wVsMC5mvbGOlTpYcfsb2+9bia/UodSUMjfOmxUhMRO7WJZsRcTYcE+pZOq1MGYW + eDEtX8bC7F4eWrvRWPrRtQ1z/7WTW6Z2FMl5qNXqMUCWKgG7qxFHLYh4uzTrCcRFwdrsaFIf2Clp7MPY + MoRlEMy9UPX5FxnXtGNAalU3Ili6CDEgFDWIQ+WIJEGAKmEEyHnaSUmTk8d2JzmGL6SXdRASfgKtdhRx + AfPDDbj5FSKCShFbK5gaPYspoWHIwu6S9aSDQssQaZo2tI291HX+4HD8mYl7cCELw6vxCtQwZ/MNlu9L + wNPvLjPW5SNT6MnSO0jTtnMs/R5qo5VXXcOUVdWPAa64kNUHCvA5UkfQkWfskHayLrIWWagO2ZZYAoO3 + 4e/vj0qlosT8iZs1DgwGaezxcSGuAp+tB/EJimBNwA7ku6KIiE6kSN/Ai+Z+zG1OHrUMc768GblcPhEY + nyrzGyqaBtF/AI3NSbFVOtJ/uKxvJfWyZqSjqKioyYECUzdXXzqJPnV9pPU/x/WqUqkc6XS0dPK4iv/M + PXr1V4T4DTto6F1/z57KAAAAAElFTkSuQmCC + + + + 588, 17 + + + + AAABAAcAQEAAAAEAIAB8GQAAdgAAAICAAAABACAAzD0AAPIZAAAAAAAAAQAgABxEAAC+VwAAEBAAAAEA + IABoBAAA2psAABgYAAABACAAiAkAAEKgAAAgIAAAAQAgAKgQAADKqQAAMDAAAAEAIACoJQAAcroAAIlQ + TkcNChoKAAAADUlIRFIAAABAAAAAQAgGAAAAqmlx3gAAGUNJREFUeJy9W2eYXNV5fs+907Y3bdGudrUr + CRVU0UqAQIDoHRkCxI6AYBODbeKSxzY8iCQPJnlw7MRx/jjVCTbFBTDGGIyDTReIDhKiSIBAEmq70q52 + Z8vM7Nx78n7nlrmzjRUlRzpz586cOef73vP1c1fhY7SBKzrMVWsNKPmvpEGVViitlO1mBuvguPOg3eX8 + ejGHzmdv5rB6jk7KUG0mgMuXHCfq4t1e9rf4+WtQ1suwra1Wquyg0m5eDw14a8mrLMkJpJXf9t5H5sH+ + SIxf3oH1y2rgGr61YduyYwol5RZcp1HnMqcgl7lWaX0dv/iSlSo51yqvOsqqqZ9hT5teZTc0x9iV3TgD + vIdV26jsmvqYVVlTpUrKZqhYfBmnPBWue6Fy8+cgl12k8yNxWFafKikfUq4jGCgPDIUbScv6JTW4ZfOh + Tx8AYV52W9a2uIOwbYV4Ig7HWYCR3DX85jtk4GoycywZbIq1dKTs6TOVXddIySinXMRAxoCRESCX867C + kG3DSpWCvxNQtF3fLKCkCEgTEV4BJ38B1ziDazSSjB4kEr0E32VXnjTgI4GgDptx11uM3JP5uA1nZD6F + 4PO8v1Clylrs2gabOw2CAmSHlZs+BD3QB50Zgh4hw46j4InxWFJkYtvWir9VBEOVV0FVVFNZSjQZh9vb + Daeny9GZwd0Uv1/zJ7eShrdIg8N7Ty0sb+ry26emFlMCINR11yygKIoi+Q1c8QqK6lVWaXmHVd9sWVV1 + 0E5eCaG67wD08JDs7pRBHrdZBKSEYFRNgwBLCdJu30E43Xtc2oT3CeaPaW9uI3ZdlCwBQfumYUq24UOJ + i4q8MVrKjtMgHc/P1qtE8ngynrBrG2nCsso5sJeMHwTyI0Vz6/DlMJoaQ5xGjGaAIFu0G1xbOwf3wz2w + J8e1n+a3t2hlPa20MxKCMAVJmBQAb+eVsbyckMJllXPuzyvL+iZ1dbo9vY2QWMrt3mPEk3o6MdM+QdKN + BriR741K+aqlImMnAoNSINJA8EUstbN3p3b7e/by7Q8okbfyswGu4SofhckkYUIARjPP13oOvlHFYn9u + NbSUUtctt68HTtduGrNMOJcu4py/p31zc9p0Ld21OEawpDHkjpomEuPmuY5LPNkTXNDvyi6eTxUo1kik + YDe0UCpqxTa4btfuIZ3P/5Sjb+HKXVMBYVwvMHB5Own0rInP/AxO8wOVTK6zadWtsirL2bcTsvM0QGbP + ooyLbXaJSb5fI58mw4k6qLbFiK04DYmT1yJ59qVInvEnSJ51CZKnfQaJE89G/LgzYM/vhGpo5ywpOH1Z + 5HszcLOe1VW2MF8EhBKJ0+k+Y1yphopeJq6HB5bR0LaT5uc5Jh0wsX5J1bgeYgwAovMUcW/nETL/zypV + stZumRUXt+fufg863Stz+4GMDkXbpd0b6dNwVDXsxauRvOgqlKy7FqkL1iGx6hTEFiyD3Tob9vQZUNOa + qM9NfN9qPovNX4rEyhOQOOkcJI47HVYHHUzeQn5fD9z0sLANFYuIrUFBK4iHGR6koaxTVkWNRRDmIZ/v + IO0bOTYtpFnkaf2S6jEgjAFAApzA4PG1wdv51Fq7uT0mW+vsoShxQW/X/R0XxrNk/BAZt+sQP2ktSv/i + OqTWXoY4mbKqKQEi7hH5zY3ksfODPTjU14+yslLiaheYEmNXXYvYnIUE4jTElh3LNWIY2bmXQAzxe081 + gtlM+Ekp0ENpWqlKglAtIMyhhMzgt09x3BDjJrP8LZsmASDQey9EVRUcf5NKJNbZzTPjEnk5e3eQ8qzy + AkAduvM8BS0/nDAiXvrlGynil8Bu4g7Hfab9HvgnOis8+sTTuPX2X+KZ515EMpVER3ubF9oqFV4NcwJG + /XTEO1cjvmgF3EP9yL/3gcQTtBER4gUFUYnBAYJQpdgJQnoux5WRl6epPjkhe/3S6iIQQgDS1HvL13sN + K87lv8ht+Su7sTWlKPbOPjI/kg113Ug9DdwINUGXNiF1xTeQWvcVWM3tMNFehPGgGSfNPsLo76577sPb + 77yLgYE0aXSw6pgV/voo/CYKhGXTDBOIFSdA0QOMbN0Kt4/MJhHxEgSPcYeoA6VAMZiyKBVHclFybL1k + IkfOdwPtwXd9VQgBkHja2DDLRDlcRX2flr6WhsVy9u9iVJcpZj5PYSDz1szFKPnqzYgffzojtpSJj6NM + j9ckiNzf3Y3NmzaTJAdrTlqNRUfONzuvJvqt8riUKDF2xELYRyxC/p234e7r8kCwtL+sgEBJyGYkZqAJ + 524MDy4ixS8zY9kp4idrBFJge7vfEawh4t3IeX5olVcstqrrLbdnPzUoDS/fKzAvO28duQolX78ZNnXV + eA3LKuzYRNz7TM7uaMeC+fNw0onHY/VxxyLuq8vk0AVqRL4apiO2cAXy72+Hs+sDgqAisQTnEdfq5AnC + NIV8tpx5RytJf5hrDwoP65fWGCkwAPz1UbVmem1kV3/JisWvsKc1xShKykR2UZ1nZDsiArXgGJR87TvQ + zOi6e3o5fw6pZGIMA9H7aGwTo9Gb3tRoejwWK5KaiUAoCibFP1fS0yxcDufdrXB274adVKE6eIYx69kQ + 5hQMm1u06/ZSl54TVRA5v2VTL+z0Ze1mYiVy6eojGev/AwOLesSTyj24z8TyOmLwxNKrlgVIfe1mqOaZ + eHLji/jFrx7AS5tfR1lpKRob6j1R9omcdD+jIv8hY0f90ANEfl9eaSQwv+UVhsUHYKeKgKTpIwhllUSL + fiM71EEeHyGv3YwajRTYkd2nTXW/ZSVSZ1o10yzd3yPuztd7rzmDHJGsR+rLfwt73lJkciO4/6E/Yg/1 + cGBwCO++vxNVlRVoaqwPGft4mdAU8WAkaDGWyL/4jGSgxjuE9sBkzNy08mrGC8NVTNZytNKPSYxqEtob + FleJheXuuwtI9M1WZW21QY6iLzlFqPdUqZGhGBKXXI34mvNoPZgWEtQdu3bjgz37jHXPZnPYvmMXqg0I + DUXu7NPjXnm2u6HZ1BVGNr3IDNktsgcSakt6DSnaZIaaSezDDPa6hWbbWH8rZlMtrrYSyfOoV7bk8OLy + ggjP+HqGtdaCY5G8/BveZDR44raamxqwv+sADtIOyHAPhJ0EoRKNIgn4fwDBQwLWjA44216Du/cDowph + oOQHbVYZQ5tsppxS0K2s2AbReXv9slpKidtAGr/DAc0iDXrgkOyol9woL8pzdQUSn/82w9N5XtXBZ6qk + JIWOtla6tYPFIIg6VFESjE0ICfnUmtFUJkdWRRXyzz/BtbjrcYS2iEES3XSpR0YuU00bcD9twaC9fjmT + PNc5RdnWNaqiKi56zwF+1QZ+pEf0jlqD+NoraFUTYwKcUoLQPrMVXVEQ6BXee3+XAaGxYaw6fFJgjKkr + 1TXC3f4W3F3bPSkogK/MxqVKpDpFt6CfVXZ8m33D0jopcFyrEslVKlnCULKf2uCqYHJt3GkK8c/9JYOe + uYVQdRQjpaUl6Jg5Y3wQKss9w/gJMu6pp/bKVF5t1AODnpwpO5znHmMM5JhIJ/RIHEv1pXCMcEC+lxbg + D/b6xVUNBOZ6ftFkUBoeUiiy/LxpXYD4xV+U2pzR/TFM+ICUlkwAwo5dxjuEkgBMHPF9GNPwy/BBkyQo + k/VqkAE5VTVwNm2E7u0yAVIYJksQmJCwUfx5Li51RZspYid14RorVVai6SGQzxUocz3XZ598IazOEwuR + 2jjE+14ntAkGhN5RIAQ2IYj5RwE4EcPRey8ad5kU9WL4+afRfcet6H72aVSuONpLvqSRSd21G/qNlyNq + 4F8lp4hR6HOZFOd7hPKC5dzVCvMtkxQfY++VIS/iJVALVwYsIsz/J2l1tTW4+DNn4577HsJbb78jtUr0 + pwdw3wMPmwmWL1tEL1owpJPOFzKtjTvL79yBgWeewsENj6N33z6o9lloOvt8MGuNIgqLNLsP3k4eGAgl + PMqlwqFkk0VahGfXWW7fuLTmGurNUaY8lRtWUWLE+uvqZsQuuJLyXT5G/8dtUe9Addjf3YOeUZJQV1dj + XGQwftxwOTTCGm66H5lXXsDBn92GHbffin3btgILl6DliqvQ/tl1qF6wkJ48FsYEppEf99k/QGX6mdtG + oeF/MeT5EUu57j6b4eD1zERmGLFkrl8ghM5hmG/amXmdcqExLjrK/BR0uERsQptnEwQE44EYPQ4PZ7Bk + 4XyvCBIwaoyUDrvO5xnf70L/w7/D3v/5d+x88H4coiRVnnU+2r9wDWaceQ7KW9tgJxJFtPj20NDrvroB + qnu3HMKZL0KPIJstxtNxMgLA3xCtKrN+fqQAoZa6Hm+Wrqb+r4nsztRCXBVKgmcYuw0Ifeazma0tWHzk + PKpjkI17UmByjsEBZF57FT133YGdt/0Yeza/CmfWXDRddiU61l2J+s6VSFXXeHYk4loLXtsHUcp62zYB + 773uZYreEZ63Fu0A8rQj2XyOcoN686kcT41uglpFTShaDndlYGAQCaIuIj6VeF+GiE245MJz8cLLm00x + ZMXyJUhznq1vbzdj5h8xC9VVlRjesgldt/03DjLPz9fVo+b0czFv9UmomDmTGxoPFxqtJgKcSJVkpOXl + ZaF9kRwhegil84THEZCZ4xAABkPVYgSToeiNYt5QX1FlFsg7Dv742FN4csMzqK2txecuvRAtzdMnE4IC + COw11VU4/eTVZloB8ad33oW3tr7jATB3Nq687E+RS6fRX12Hxq+uRd2yo5CUnR4vWwwY9293Mxf5+d2/ + Rk9PD0484TicdtIJ5vDKlZBdisdDIu7aSL33w+ANykUCLPF3kh6Ou33iUrhgX38/HnuSbqf7APYx9n/p + lc1ont5UPHaSpiN99+692LzpNWaTOfPd5s1bmFCdiHnHrkbNMccVucnA7kzkJ+Tzl17ZhDfe2maAefTx + DThy7hxUUhJitDcpyLmET2I0YPJaMmYubsH4FAMAk2HJp0mKfW1NtQEgEY+ZHfUOTSZ3ZWM+5bhkMkHX + mMcgrbu0RHW1KaYU9BQTAhrUFV1KpNQS81TLVCppiio5qldlRZk5fc7TZdpORK31qKvfGAjRCAaeKCh8 + +E2QU/M6gbnL6ChiaGudgUpGdMesXI4VnctoO+NTCm3NzpOovr5+DA0NGTcYI8F79+xBGUPo8849C8uX + LzNGMTBuAaOGSTIm+p3LZJjSSx9Ghu9z2Sy/y5vNkN7S1IjVq47mRlUZuuJvv4IYu+fTRgHg2cWsSl/W + PshFS7Sx+zpywmMxCaKunPsFqEu/XuRjlV93+tCYQHbKZ0LE9MHfP2J27JwzT8VKApjuTxsmKyoqzFwC + kmu641/dEAjoidUgqFkYRo2Ye9Jcet+/IfnEvb6EF9FlYiJ+1qN6L2h7h6Fwh8+QCjMWSYPlyG7F6chf + 9Z3wYCPws0pZhcUj6AZuyIipiCKZf3XTFtz724fQ3582w46Y1U6jdylVKV6QOFWY46M07SOkgt0m0OV3 + /D3imzYYA1hEZKCwWr+jes5qe4R8r/H1uUhS5AQm3zoH6Wu+B7esCkUO70MIFoIcx8WmLW/gwf99lJZ/ + wPsZRfzozqVYe84Z4WnQh9WCJ1lmTFO+qVUD/aj8z+sR28VQPB9RAU9atK8Cj9vXzalaxbedQTwWjflN + 3YMeIrvgWLiVNdGvJ6U0ZP71N/E7Mp/2mZca3BFzOnDmqWuM7ocJyifVQvVlotu1CyUb72conPEe7CjW + geDysH397KpWvj3TnALroHDgjzMHBXnkW2ZjpOWIqdHAORzq8OYtZP7hR03AI80wP7sDF3Dna2n1P3Hm + owSQs+SbzyL52gZIwAPtS4BvSvwTc0n1fkIAKsUVMthXJTCHJgWyzGmszf1kLCBSIKlkNBwetbK/vsaW + N7ZS7B8Zw/z5Z5/OIKram/fT4d47p2fGV/rUrxDb9z4MmzpCcXi8BUlO/sW+bnZlhuScxZsWhOYvYB/m + FNbODSI7txOuhMVF7I4FYDiTxQNkvvtAj2FSmJ8jOy/Mi3tSn2hdKGwFxdWI7d+B8id+CUWXi7yOqEZR + 20IUfsQUD3JItpEzrAxNg68EBiwiaA30IvX600g3zDRSUJRaBexHAghz0gNv52fT4p9/1mnGT4c0qqma + tKm3cH2qn9AqNDPWKo7PCg5LrhvZD4kN0KYCrNVa0pvwnogpSIEMFZ7t4UPIHrECbkk5JtpBmVWOvCTQ + UT7zp5x4nGFefVo6H12cBjvWsw8Vj/wUVn8fs73i3fefexCsqJv6+6TxbQFA9qOP5J3KnZkR2VoVlJKk + 2c4gdDyJXPuiQl1gHI6kjF5ZUY65c2ZhdkcbSlIpfGoGL1zUiz0Ut7zsmfuQ2voc3GFJgDzmi3InL1J6 + ha8/JIND6sCprfIhrZv6Jun8O/Eg8O1fML/R5TIOqa1DzyXXI9e2wPhIHRGUyBKjbsaJHT4xxoNptcnw + EjvfRO3d34PqOQh30CkunuogQIAkNwz/9Q/Is2Nf11HlV2NwgC9n86Y2EjoH5UTjS21kYacPItOxjJ4h + NSXiPv3TEI8vK92L6od/jPi+9+BK+ht9DC9g37Ns75Hxm8jzAWPKDpze5oWKtF3sN3Hkt7wYEEUhmry1 + Si1Y5XGkjz4PfWvWGZWAGlXhPVz68TExklr/SBZVj9+JiucfgDswYgDQUe7DG50nH//ENzexM13kXfcp + raEgU2Tm8XoP0Zkf+AIVkQKpHNgV1JCKEvSdtA7pTgqMHfdswmEq+ejnCQ/bRmjf5zsjqHjpIVQ9cSeQ + HoaTdszuBzzrwu6LMXiL7y6mSm8NSDDLHji1zWRM/Cf6/xX277Ib6xWcBHh0KnPSEqukPaisxKE1l2Fw + ySnQYaJ0GFz4yZI0K1oin0JTfsIlT4GUbX4U1Y/fAdXfj3y/4xm+gu6HRXV2eZrzBvZ/lZNCeZRn2iM7 + PYpFCkIbqXUdB9zKd2eJakV2p2Dx5CHPKoLANLZ/1UXoX86hiZLIkAmYCZMyjb37u7DxuZfM3apjOjG9 + sQGhzk2ERcgYmc8No/Ll36Ny471Q6TTyfY4X9UXSvlDKJKHR6vfmMV+lDsIvENQ/uquw1IGTW4P5LQ48 + hl/cwfdtMII/Dgy0EgICylIYWHQyDh17EfKV0+AXCibi3zSpCfzi7l/jlU2vmfujli7GZy+50BRJJuM/ + iPNj/QdQ/ey9KN/yGDCYMcxLwTO6SMT5iZjtpHu+jDw8x/eukDftsV3Fa3UTAFOUcAQsxJX8DQDwPfaK + CP/FGxTz1MEqjSHbPBe9qy7GcBvjBDs4QS5mJ3BLAsBPbrsTL7zgScDKlZ248op1BQCKAPTFRkTeyaFk + 5xbUbLwHyT3baOzyntjniwGO8C+vUoS4nmTcSp5GlO09BVs/GgADwppW+H8HI6pQxo/+lsRcy2v0abzi + FzEL5Ra7DbesAgPzViG95DRkp800hxNFACifHxLw/vs78NsHf2c+P//cc9DePtM/Qh8dKWvztFfywA5U + bP4jyrduhDWYhjPgsLuBFyxmvqD3Wa71I15vlqfDjDmk7tc/viscPEbaBASpEJtM2NV1HPGPfPtZeG4y + WhAoAkJOX2yRhpQFp7wGg7M6MTB3FbKNs+Amy8bJHWDOCKTJI3JjtEae6MgOIrl/O8q3bUTZ9pdgS3yf + ceFw17Uf5hYzHl7M9Lz8grffVpY6KNZQjWJ+fABObPEeljYWWllEsBGe77xIaC2qF4yukMijgiWWkQgr + yTmSKUpCGzItC5CZPhe5mmY4JVVwGURpE0kWiCDasHIZ5hx9SPTuQWrvNqR2v8mdp6XOZjzGB10vxHUR + adFgP8REkL2Xb7/Fnd8vwULAU/2TuycHIADBuDWjDiEIt3DwpfDVoTjAHVUpEiAIgF2qvPN5wqYZL0gi + 5ZRWI19KEFJlcGNeNGmNZLjbA4gN9cMePAQrM0B9H4Ee0SahcYa099h8lHE9zovn80U27iKi6w3zWphX + RiZGMz8hAB4IM7wZvXBBQKjl4Ov4/ov8wEsJ1ThAjJ7djvzxg5xKS4xp+z+M5BDeQ5ja1O/ksebgjyyK + C5oTcu9bAgzw8l98I5leD/xng6XVP/nBuHxOGn10nVAAwftzBRpGjT/jpAQCMxGYwYhKTFrgDJi2JgAg + KF9PUi7Qo0QehV/sIJ3f5/Vn5nFYr7xlBjQ8NT7zAUmTtrEg0PlpvYKTr+fHJyP0EEVAfOIJcNGRTTHj + WfbHSN8tXPJFZWpAU2MemGIe0rXaq5ZFSufiJuURU5GGq3mVimnhrHvUGh+1/qeLE4bwY/8qQe/bHPMf + vP7cE3n5ScC8RsOGsTo/uh0WZV2rZ0TPAwUEYXo27y/n9RJ4ahH7kPknBGQChkd/JmHPDva7Of52Xt+V + P5Xwfu7R1rBh8l0vImbKI/0mIATEBpxo+XsWYBZv1/IqfSF72TjzT3W98XZdSsyvs/+G6/2G1+1cL++v + XxD5w2D+cAga04w0uP5fdBWAkLxBVKOTH53Gqxy6zGGXg4BAMqYqAcKcFGzlIYKN/O4PvL7M3qPk7+vg + b7lsBH384TIetI9lqAq2wQRNxUdrnnoI43P48VLeL+FV6g1yECP1dXGlSX+4GDI5PpJa/S6O3cqxm3nd + BA+AQ76YB3Mbd6H8PxSeiq5P1P4P8t+8rwDuyu0IAAAAAElFTkSuQmCCiVBORw0KGgoAAAANSUhEUgAA + AIAAAACACAYAAADDPmHLAAA9k0lEQVR4nO19B5gd1X3v/8zcu7039V4AgSQkhJAACQkhioUNBBscwAaX + OHbynPKc2ID9Sr7k2S9+Thy/lJcvX2wS24mNbWywwVRTRRdN9KaC+u5qe7/3zrzf739m7p27e3f33tUu + CJIjzc6ddubM+fdyzjHyPijdn1ggKc8X1+DA8I9vjO7Txfi+V4xdLX6XY79MjJyMO3g8xxdZYsQ0iT2O + 6WbEST/t618PWxIHCTzf4Yt/GM+/jnOHcLkd97yMa6/guBf7dmOcwfST+sMPdnjK2CuVP9g9dZ0yScV9 + rxswVum9dqFcv6JK+9NxxNiiewHAS9DHTb6YVTi8FDddjStfwrXfxXYlbjsPx2djOwXHM3BPNbYibDFs + jrGoZDetE+f0muE9eKmZgW05tvW4vAHnt+H8FTj+CN61AucWAyeLfUUYPwGESAZNM6JYIPLVlTXy1VV1 + cuOKavn6zo73uDdzl+MOAXpA7devrJUbVtQIycgxIazQsb5UYLcY5z+C/afQ2zfg6qfw+2L0++k4nolb + q/FIiXFjromB0ONxcUrLjSmvEqeiWkxVnTiVNeLUNopT0yBONY7LKnGtRgyvF5eKlJSKicX5Wt9YzAPz + 8UvQhmq+A+9ajYvnolVABnMpzpPjTMe+H7AfxDlyEUUH3/eVG3x1ZbXcuLLuuEOE4wYBej65QG5YaYFO + iDuOpXdSOk4uEFK5yBdx6svYLgfFnYF9E3q5zMRc18SLDQBsAFTjNEw3btNs486YZ2Iz5ht32mxxG2Ya + l+frphkA395XXW+3Wvyua8S1Jnu9Hvc1zhS3cRZ+47iqzgA5DJDDAKnIfoiSLvZlbAMRAm0+D3u2cRUE + EkVNN87143qS36NP4NtuPLUWyFArX3/h+ECE9xwBCPgbl9eGMjTkzg4oh9iwAb+/iLM3onOvANBPxe8a + 47hxEy8yTmU1AWXc6XOMO2uBBXTdNFGglleCmZcYcACj2MTaSY0eRL2XCrZksHnBZkW68nE3Zp9FHazL + qaw1LjlG/XRRpMAxOYuQQ/kelRCwDKnBiWVEBqHIgO6BGhP4tA5UORioHdqUG1cAEU597xEh9l69uBuA + V+7IP6FcF98FxU/DifNI5Th/RqDIQRKACxcViVMOSqyuVzZuyKrdmIWYil5P/BQAmxgUf3AAGzjyEHQ1 + bH5yCCoeYJECwFNe0IpwH+iDrmPrA/s3sSKRomIx3CAWTDEYkQOkcF0xpZBEpdA165p81ucP9Buvu0P8 + zqPi9XTy/aVAqMVo10LUejm+7Gl85U/xvvtw7gg+N0WEx6f7XRB5RPmK7783CuO7jgDdn5gPajHoH8vq + VcRD1AMRZuDyJThzNTroZKE2T0qMgQrLQIF1TSqvFRiuNltZsZ8YssDu68HWJV5/DwECYCctlQMpFNPy + LFl3Kl4BORwwSuoTQDintFJMWQW2Sh4btsWUQ4cor/T9xhl4d5/xOtvEa28xfm+XkVSyHjVdiJo2oO6X + 8IJ/R6tvQ8WHUL1HroP/fs81IAgIisof7JnE3h6/mPFvmbxCdk+i861KFyh2fgP2VOI+Q+0av0vZ6aA8 + yOc6ymBSvVKlUjqpfAhA7+1Cr3WI193JTgdVBwB/NwoUQ3HBJUrKrGJJbgQl04BDKcKQrYHbkBt4bUcE + COGDE/mW3fkD+Ps89jehpjvwTS3Ku7ygW5x3lxu8KwhA7CaX9b2A3eM/vrccnGAjzn/eWFlfroKguMRR + aocsJ6Wp/Qdm4ZN9s0PBZv3udqV6BfrxUMgFICJMZa1aFooMQFifUo2QBXdSRGhrZru9ABF68ecREMQ/ + gvIfxlf2WqGAx8gVQSgVP5x6RJhyJbBHZb3FM+NY9QoffiLO3Ijj63FmBQBfjA50VaGbswjAn25lLh5U + ltp6SFIHd4vXvB/Ah9IEtq+s/XgpbEvCciWvo9W2EQgLLgERESdSQ2cBclTXiuoyiQEH3AoXzGJ0yFbU + MBu9A2ibNvSJbyWWgZJYM+VK4pQiAG16lfWOdbSAA4BXCp0038SJrdgqQCmOWzvNUcA3zIDdTsB7Kj+9 + 5n2SOrBL/PYWESh0xxXQRyts49CAIgGRgQopFEejukwREKGq1vocyB8GBynSykAMK/HkRooHcIA9QIIh + W5VvLYUp9B1MCQKQ5dPe9QPgA5v5niUA+P/E9kdQe2fDNIuZihonNnuRcabNsg6YEPCH3wHF71FWr5Qk + +StxLH4BSl8hZZj7ebxWqIhSrgCxRaSgDyEwLS0i0IxMDBlJDrGXGlH7JhAHXdd0QXc4xlrH7EuaylOB + CJOOAKT6lFjvF8xvUL3AGDcfwiVQvVyA3yUmXuw402ab2MwFqkGrQghWnzpMit9tWaiXn3yfKmDnW/JC + Cpimfi/0gK42IPSQOPFiI0SEknJFBAGHEJiSxktBi5Tl2NZia0Hf7QU3ABP1lfd9beXkI8GkKoEq71OB + M8U1luWLXIeP+GP01DRDo6+i2nGnzxVQvzWzkgnjtTdLCnJeqM3nAdCCgO4HVYabl3neD4wGE5CBApMu + AWufhA7ovEteyMB7YD3Q0+jUNNK6sbofLJoUOB8UXQtv3z+C/vsb3P0v4Jjt6FdPDSd3cq2ESUOA7mvm + 2w62ETva9XOg891AmS+U9W7MCV2sdLCogtfbLSnIeUvxY8v3vIDuB2Z/Esp3Ap3K2F7SAlqBTSvEN6HX + MSNZTLizTlt6ZogUusXs5sTpkzBqpuXTa+Mig+PCaqgWt2lOwAXx4qFB9Md+Wgt0MBEJetDGm4ER30B9 + +0QRw76/8od7xm9EHmVSECAL+MBRwGoZaP3P0AcXMromRSUO3bRObRPZnVJ96ijMIlI9ZONYZTzAU88g + kD2Y2d6gpAGvTr7RHo0XqWMnq9BxlBjKfb+GIzOI4AB/nWJjkcMZuwvHRQTqA1B+3fpplhtAXChHPLKf + fUNuMIQuuAvI+z/wrldwnDJikXgykOCYdYBu2vgW+PxS9up6UAm1/C3Y4lJe6bizFwrdt+rH6e81quAd + PRwoeLnLWIBXoNOzC8s52YU+40bpQfgpWzfWFieFzZgjsRVrJX7aWXZbfZYUbfqQFG3+sBSfvVWKztoq + 8TOxrVonsRNWSOyUNeIuXiZO00xq79YPQU/g0JDNFsDOA856fb4iHTmLCRFkDGCPei1QFH0qiRq7AHKW + VogpLacr2xgqiCIwF81y9Mlb+H1QgvSDG6ETfOMYdYJjQoAI5ZugrrPQ0L+Ghr9WtfyqWic2a4F16JDl + d7UZ2vPS0zWqrB8V8L4FvNdvgZ7o5G+bvqENYEgX1kR87TkS33ChlPzWtVL60U9LycVXSvE526Ro3SYp + Ou1siZ+6TuInLJfYgqUSmx9s+M1zvFa0ar0iSvG6c6X43G2KKDGcc/AdTuM09UKqUjeU0HeT66T66JIO + dZ9MaCJXyYkI/OaBfiBVl3V9F5dY3wG+yU/AVBwapJUwG0+eirtfQx37JY0ENceEBBNGgBzA34iGfQvy + c6XGz2saGY4VoUPHSxnvaLN4h94ZleX7QULNyAuiiiU7OdmOrSegdL4WHeTOXyJFWz4iJR/7rJRe+TkA + bJsUrzlLYnMW2tgBKIlUpVQc7XwzbEufN3qvPoNnWUds9gIpWgkucvpGKd54EbjEaWLqm2yQaQDfQ2Qg + XpAzDOSDCEZyMgT1dnapP5ihZw1+EQlSSSPgBnisCc+tRl/RTNwn5tiRYEIIoA4esX0lGcon8FcQ+A6B + P32OlbXU8psPiNdycFTXbU6qjwK+A/seq9CpD75hOtj4Nin5+O9KyRW/I0VnniexeYvB8is1ASRwPoQK + ac4t9E7aYgJg5dqC5vBjUbepqBKKNOUkEB+xE1coh2MAiFTsJzwrIgbHR4Sc3MCjydit2izMRJqLigTQ + UYAEfXyA/gLGTF7Ftl+MHJOJWDACUObTmxmQLJ9fT7YfUr4mW0Dh0+BNcsh4R/YxMpbTi5eT6oMQMVl9 + Igp41OfMXSTFH7lGyq77Iyk+7xJl3055eYS6M+RshtlwfuaFMuznyBJgdnAQ4RwZ+5CZQk5ZOTjNIqtb + gDuYmnpFBL+nO4MIQ+oMswqj5EKEHNyAZuFAr/oMTGmF0fA0xWiUE1jv4cvYDhAajuvIDcsL5wQFIQDZ + Pl/kMZ4OUw+9Cm3JfBPa6Vra+E51g+M0BcCH7Eod2is+nR85ensE1ftWqFGuJjs8yPlAvrsxC/hLPyGl + 1/0xqH2LuA1NNmXLcTKUyhKl3JEvTO+GoO2/uWuPPPP8i/L27r2ShEyvqqwQl6w/DethdQyvO3LMkLBT + 16CiIX7aBii8dUB6xgS6FBEoGmiamrj1M+RChBHcgEjAfAa01SktAxIU2TwE5QT9fB5mgyxCl+0ADFqh + H1kkWFFdEBIUhAD0S3spYrS184ECfwm4n4tj16mqd9KUT+DTqdHdKbnkek7g41wKWn0Ccp6Uo2wcplHx + R66Sss/+qQLeAYWpZm6c3EAep/AdA4NDcs/9j8hPf3G7PL/zZXnltTdk58uvwsryZd6cWRJjwkehdZsM + VzBVNRI/ebVyBb+4WLzD0Nf6AUiaqQM2MczE80QCFuY6QG9St3HM6iUUqzyPMkuMPxtNfxyPdgM2vgOz + tJAAUt4IoOae+vaZxuDX4i/t0o/S1FPvHoFPmZ8Yyh/4IdUnM1QvKespi63dJGWfv15ZPRM4JTTJJgD4 + 8L1888uvvS4//skvpKO9QxKJBPoyIf19ffLOvv0yf/4cmdbUqPdP6D0hV6B4qK6V+PLTJXbSqeJ1dUIH + os8DNj6jwTQdi3Jzg5zv1YymhEWCuM1DAFIYEpowX9IXKAnyGOAxQM7BdLN8uUBeCNATyH2fppxnikGA + n8XLfh+NLZPScgB/jqZO+arwHbRBnDyBT2pPtnlq0imAp8+Wkqt+T0qv/oK4cxeHKV9ZFF8oaPzg3Z7n + yYMPb5dnn9upgFcEQMemkkkQab/U19XIySeekFYIJ/Ie28BADwHSqmm6ej04Q62k9sME7u0F8Hzd1LOY + QzfIjQQDmu4G4DOyqMEzpqKZVIIa71Lc0Qul+Vk8m6IouBH6QD5KYV4IwNx2T338bLJcCCPvz9HKegZ1 + QPmGFIveNX7rYc2LG1fmB8Any0+2e8yqV9Hhnrpeyr5woxSdfb5NuxoucydagneTzT/3/E55CSw/CvwU + rBMPnXvC4kWyYvnJOkDAHOs7g+e1BgArtvQUcZecIl7zIfFbD1klcdBjzATEkycSgO3T8hMVB1CKYSb6 + /b2iuQUiJ8NUeRUPvk1cJ8F+/YX2cZs4LgLQ5FOW5ajBsQS98y007gRgt8P0a+bS00jShMj2I6No+372 + b0ZKe6jlMxMXTS+vkPj5l0np574isQUn2Pw7x5kc4IfPayq3kR5Q4FNP7ZB+mGwEfIquV5heJZDXH7ro + fJk3d47V9R1n3GrHfWewN0FuoTttpsRWnAGg9Yt3YI/6D2guappMUXh7hNONNA80E0r9FOAEov4NCP3+ + XrJnmEOyELdsx6uOEmb55BKMiQAZk49Zz0zmcP47Kr9QvXw19cytZzNFY/j066dG5uTlAn6yG5TfSURB + J0NjLv7tz0vJlZ+DrG8ITDpn8nPVgs6sq62Vzs5O2b17dxoJSkuKZeuWzXLB1i1SBKoyE2D/eb2f9VZU + qLUgQLjUrtdB1YOqF2hCfFHUhJURv7UE1oFmK8dt1rKah0P9BBQtgwrc8whE9CDjFOOZhmMiwNfSrF/H + 0l2JCv/Iyv0Kx62friYa2JLxWg7kDKTkBH4nXbkW+KZxhpR++ktSfOFHwfLLR3jrJhsI7Mx4PCYnnXSC + zJ41EzK/VpaddKJcvO0i2fahC6SivBxqiDPp747qBrSeGQ2lSDBQblNvvggroU/FASOVDDKFbY22O6sw + zxCKoUMuQH2A+hfgYGgdGCiFIu/gqRfzEQWjIgCpnxFaR1m/nATg/yVaMsfEYg5H2FCuEfM8BnUoh4Z/ + dC623x0BPpSj0s/dIEUbLxJRqgtZ7mju08kp7ExS+cIF82XN6tVy+prVsnjhAhBkcRr4U13086AgxuYv + QT/MltQbQAImjAx5NncuH06g4xuSNAutPgCxyeRTKDp8mqLgIXRpKxOlxwoa5USA7qvno0I7isX3/Aq8 + /HpUdoGy/uoGwyibioWuo9bRM6yMAL5YmU/gE9COAv96ia/fYsOyOZw36aNjlf9ZDbN/QgWP4tMJdI1s + z9/kvXLUEuoFDDI1zZTUq88pIali6OTJCcB1VR9g3ID+F/q3rbu4TjQsr6IgQS5wwynV8o0XRyJBTgQg + xgRwYx7veWgns3croe07bl2THSgx0GuY5jw8Fz9L2Q9+M3RqZb5RtldM4J+5NQ18KmItR9tkz9590tnV + DWoswvfEMx88WTLZSE5vXnYcYDJeNF47MlYGYwzOrHki0H9Srzxro4IwER2aiHHres7AfaTb2E9AFJAb + 00kEpVBDyCkVBajU7MQtb6kLBA/m4gIjEIDUr69ydGRrI/Z/ztRtA6XfqW3SMKV4YP1tRyTwRg0r0Wwb + G7dPtAfaPjhH8TV/oCFW0aANEHZoSLY/sUN+duuv5ckdz8tzO1+Wve8ckAbI5+qqqhEwmSz4mMj2XpR0 + MlLICWYv1FHJqdee19wDIoFb7NhgUoQr5dIHqHwbuotdaz35/QygCLBCagDD+8Dp+qyDqGYEFxiBADQd + gpxO6v9XAAE+x+EaprzKoTODTWAKl6ZxDf+oKOtXjQ//21Pic3xsUYkUXXqtFH/4arWLFftx3/M7X5Fb + b79HOjo6YZcn8e0JaWltk3cOHJLp0xqltqYmIM73ClRTX0zgPXTnLlLlznvzZXRcSn0FRILhySYj+sKO + QaByaZSrUhlMcP4KmYFO3g02Q06gA06Gm4VZCKDU70vg7pWZQIG/wMsWmXgMJl+TCSN8fnvzCJNvBOsn + /LtsmhYx0wXLL77mizoGnx/LW4aA6fc/9Cgofn+W35C/e7p7ZL8iQYPU1FRLOIvDB7nQw+fMW6p5E/6B + PTbBlimKJeOLAiIBuAAHsKoH0jqI/CJGDgGQu41ruplQM5wLZCEAEwuU1YD7AOuuASJco77+8mrHVNDd + TIdPW06tfzjrT/VbxY/szVm8TEo+/1WbZhVx8BABdjz3ohxpbs1ZW09Pr+w/eNhygurqSE9NgZ1+vBRw + R3feIklSKew4aqOI0Aec+HiiIPDWlUAhBBKAQI0MaXYKB6fuA0E/o/bcMF0gGwGW19A1SeqfQdkPFXA+ + x8lzMgWtdGjAWFdvtrdvOOsPgzsq9+no+dSXxF12mpo+w1nZvgMHZe++AzmTQkYgQSgOMhV8IBAhK4bA + b+JgU2ypnU/aaCCQQLnAeKIgBS5QVGrHKoZcwPfpvq/HS+4EbHsY0MvJAULlT2xm/KVAgGt1vpwKyH4m + I5D6u9vsEK3RPiRg/UzS9IaMavnxi66Qoos+rkrfcPcqP6AcbOvtPXult7cvZxLvqEjwARUHYSKLC1PZ + Awfw3n7FzmfgjRQFORVC9g0dRLTUvDQXqBGbRqa6QFQMpCFCu5/Ur4M5HPNR4WhdjmljEgILo1HjOHwI + Lk2SHLDJgs6SUyS+7SqbF5gDYDwze/YM+fBFW6Whvm5UoDKIcwAIcMttd8pbu/egP7z3fETQlBb2Q0mZ + xD98jZiFJ/KE9qnqUxEyyck1+/s0fKzVEHYcdURYOuZywpYwDnw8WpQDdJH6PQ1KcFjEBuiAXwQHKDOl + 5Q6zbTWjt6djFLMvbIwolqqnL2nTsuNQ+tyTT7NevlFCrDwm8OvqamTf/oPS1z9K0qhkOMG0pgZwgg+m + ThAFqQ4YKSoWb+cTYhIJzYp2S0LfRXDPCMeAZwe2FJVYLpDUsYeUzHWo+0ncv4vK5Q0BF1AEoOOHmr+X + 9Io1zu84G7A5TlVN2qzwuzrGlP2h4kenjyZErN0sxTD7qNSkWf9wQAXH/IiG+nqNx+eDBAfSSBDRCcIY + fAGdfVyX4HvcxumS2vu6+Af3gLWiB0KFcMxYQcoOtg0SaHTGFEACPdiDOh4wrkONUZVBRQBGjKzjRxY4 + joG6bho4QweoWG0v9TFz3N4oJaT+VFeg+NU0SNF1/1WcOYskHBse/ajsB+0HOBNBAjURhymGud7zfi5x + wK2iSlLPbLccOB8uQF2AiiAHoTJ1kyLBS/KuanT5HRADbX6gDLpdgfLHcDn+XQZEuEJNv7JKR8fwMdGj + tzOH3Z+D+vsD6j/rAolfcIWdaMnJM7Qb2PmFIIFVDC0ShG7eDxDoM1yN2cb73hZ/35t2fKObBxcgfEpK + LQFmlMEynH6NeTG8hWLAJftnsABco8KxaV6rdEKDiiqdXk0HcpADjFZCzb87oP6qWolf9UVxOBxsFLmf + q4rwI7jVQycoFAlCcfBBEQVZ6h0dcGDp3jOPWAWPXKDUjOsiNrFine5Ok2h1gg2fQ/e6IcnvwekhtTZC + 9o+Li7D/CrYaKBAOEw9Zr9/XO/qgSQny+gbtMC3V/FdtgOn3cTvFWo4o33iFdzsRJHhn/wHpzwcJAsUw + 7d//IIkBfCmnxUu9+ZIIdQFae+QAsQzgc1pQQe6B9gXdw6kku6wKsL4DcD5KMZCxB4w5C/dNU5BR7rAb + yfZzDOVKs/8w2qdWh1GW467fKkLtdaI0GHCBmOvIshOXymUXXwCxUDumiXiQJuIv75K3d+3VSaVt/sH7 + yEwM2ztqm/HtsKq0b0tK9Nj2uWQcsLmeJexUdBubPWTxZBphHd7ifnVVLSNPpRADnKZtLScxMmXllv0z + oDAwuuNHX8y8BM53xaFW85ZI/PLP6UDNtOwvlBLDD0nrBHWauZM3J5jWqLGDNGsMbzpeOMJYCbM5rKTo + nsPSUs9BGeRMaaTeYjtR6ahigJ5fhonVFyBWiVTPoDniJfz7nLhJukHsn3PuctauRp3Dhp4ksn9q/mMN + 4eY/OigoIRjS3HypuDD/7OCNicvi3DpBYUig4uA40wmyQB91nzPvv7c3mH8w4503Wc/gV3GZnSntjRf0 + WDPoIspgLi6pJjjHa6gYGLLpZDZU/DPAu5sIYKAUrAbBXyeceJnynw8wu4Tj07wxbH8qfxyXzykLKqol + 9jFQ//S5aeqdjBKaiPUQA/W1E0OC48ptHLB6neyjs1OGXnhWun9+s7T/4mYZ6u+XkiUnZGdEh4/xT3De + 2/FgWi9TLjCWNcBzRYEnlgGiZIK82kET7gcCvKMIgNpp/m0zzC/ijNhkGcw5G8P21xalAvZPGTP/JIlv + u1pdmJOJAPoBEiiGDeAEtVQMD+atGNa814phGCBRwNtkztTBAzJw/z3S+YObpPW2n0rLzuel5cAB6enr + l8aNmwDUErHNzQ1M71lYAxxwS2tgPDHAgKxyAMfOcq6I48dx/mWcetL9yknVpSbmXIXnTtdZtTWc6BgO + SgySCnJ/l1jljxk/ms9w9kXinr7JpotNduw+wuKibuN8kIBDvbJ0gilFAj+Lz/sB8HVii54uSbzykvTc + +lM5+sPvScsD90nrvj3S2tMvncaV1MzZMv0jl0ndySvSIjRnWyHTPc6d+NZONQNNkDo2qlNIU3tiomsn + 8DfNSN+nd26vl/B+4964vIaTDnCljRk6j11R4GbijWNOxWqHcOvkS5wz98KrxMxdmrH9J7Gj/YjHsFAk + OBAgQW1tdXZq2SS1L/fcBgHgEwnI7CMysP0B6fz3f5Wjt/xYWp57RlqPHpW2oaT0lJZL0SkrZfYVV8mJ + n/6cNK4+XdyioiyEH1EoBrrbxYMyaJgVTOIrHsccZOIofQksgR6gSODLre6Np9ZOx0OfB4+t0mlN40V2 + 9gSaEGPN3OWLtf15S3W9xC79jMawJ536h5WJIkFoHUzJoA99WYbVe329knjzden91c+l7Qffk9Z775SW + XbuktbtHOmB5DzU0Se2558vi6z4ri6+8WhpPXSXF1TWAkzummEqjGijae+xuMdTRjA0Tj+0PMNYry0vB + LOpGE/bkZveGk6s3G9d8DA+WQFvUuW11WkKN/GVjd1biR9JOlqQe5IUnS+zcyzTvb6oRIP1BigS1ah3k + gwSHmltl4fy5UlFeNokuY1/Coe20t1NHW2Xg8e3SdfMPpPXmH0rL009Ka3OzHB0cku6iUnFOOElmX36l + LAW1zzv/Iqmav0BipTZYlkvxG/370ffPPSI+E3NROI7AuGMrgjqMTDXIlHIObX3Kf8q9YUUNkz84b6/L + pEK1/3XRhdG9f/rRwQRJKofWnCPOmk3pkT3vhtadiSKCE0AxZGbRmEgA6isH8BcumDs2tYxRbLZbxGlD + pxM09+Set6Xv17dZar/rdml54zVp7eqWdlwfqGuQ6rM3ycJPfkqWXnWtNK1ZKyX1DTqQI2rnF9QS9LO3 + 5zUxb79sD+MBAoyhCOoIIp5nLIGmva/jBl5yv7qy7hI8eLbG7Mn+qS0SQ8aw/1lo++t6WWiMs3aLOMvW + TIn8H7UYUxASEGAVleWy7MQlElOFaGwESAN7WB0q2zlLWGe7DD79uHTf8iM5+u/fl5bHH5XWQ4ekrX9Q + uuh1W7hEZlxyuVL7/G0fkZrFSyReViYmnONguJOnkG9nUu2eV8V/5WkO2gxyBkdHALXTOIzPcawJmlTi + JrU+796wsvZTuH+5mhLxYmsxBcOOxip2jjxjZ9I6/woxM+e/e8CPlCydYAwkoBm5eME8RQCnkHam3d6+ + mnDJd/ZK/313Svu/3SQtt98qLS+/JK0dndKW9GSgulbKzzhTFlx9rSy95jqZsf4sKWtqEjdMh5tAbGRE + c8I9vYEwB1URdH07nGyMqi0CuLaGRML6eXzZTSuAAaCZygFiwVCUcLmVMfqEU574wdBu54LfFsORwu8B + AmgZhgRMJw+RIPQBVFVWyqaN6zWRRMIrw5o6woBSNp8Sr6tLhl54Tnp+fjOo/SZpfuRBmHD75Sjs9g4X + JvXcBdJ00cWyBNS+8JLfkroTl0kc/aLrC01wOptxC72Hj99l5wzgK4oCU9DP8SEswYSXPscbDCQ4XY1J + DfiHyQunG9uD1qGvXG78efl9L3gRbcyyiuxr6R9+lhoZdcZMdpdoUzSAtESKQHH3PrBdDh1p1rVYaqqr + 5Kx1a2TxwvkKVN47BBHHQSgsRUVxiccDM4nt821AyWs+rEpdz6MPSQ/keg8QoRfEwaSnVGW1VCxbLnM2 + bpJpp58hpQ2NSunBRxYM9IL7jAE31SNCcPmZMEr0TkKVcyVzLkMvCbGdMn7K8xXmvl/D+HCtViIWBdJe + q/Fa6we+atqXbmbe3ailwDF/vaASjvih3C0vKwWQ3MwHT6ZJxrrwbs70tXTxApkxvUmaW1rB7ZLS2FAv + tTVV+u4kKGD33n2y47mdcuiw1aJnTJ8ma1atkAXz59hJonCOVN/8t9+Szh1PSTcUvd6UJ4OcDWzmXKlf + u15mnbNZapacIEWVlQEzGQZ0388LCYbrGuP1WRjpVO+eE8tUEqF85vwp0HWybH/kvMl+gCO+NKofKXPB + yzR+zFZHrjdMV/MvHcAJGkkW/PiTO+TRJ56WDnRmTVWVrD/jNDlz3elSRtMn6JxJDdoqQvnKdqurKqWq + qjLdXvVcgxs88/xOuf3O+6StvUNCWnt71x555dXX5eKLzlNEcMEuPXCI5oMHpaW3TxLQc8qXniQLQO0z + 1p0pZdOmWYdNRuvKakNWV+XZdPZZHxDtMfTX408+k+6zs9Bf689YI6WlJdnilYRXUSVy9JCl2WQAlmTg + nMtvcZViioDItNn+sP04he0JI03hPHxsA7D40Seekp/f9mv9KJajR9vk4OHDeu28zRvTlPZulubmVvn1 + XffJ4SNH7KxhmSisnuO1ubNnqdPIraqWGb/z+1L85htStfREyPWTQO1VwezgY7D4CeQh6CJj6JeHH31C + brv9rrT+wj4jlyKibtpwpnUUsd1AZM5p5HOhqqDrOaOqRL4nq/5c77S7umGraOfZ+Ohtw2b1UJu7t1e2 + P/aUzrwVFQkD+DBi+Bmnr4ZctmndU4kEYd0hMbz59i45ePCQDAUp1ln3ArC8xnumT2tS+dq47izdsm+c + NBeS3Qf6RndPj/YN+yjaZySgRx59UlYsXwZxUKaTWpE7+V3dUorvSAePvdz1j9OAkmzqj5g84z4capxU + RtxoNfiY7l7p6Owc0Qged3Z2S1dXj7I3Le+W1YB2kaJ6OU1b0OnRQvaaGBrSe0IPX2g3T6SNhfKBLgC0 + s7NrZJ/h/W3t7XLkcLPMCOcw9C0A/CBqWPC7I/DLXjWhUPbFSnq7dRJDHfIdlIryUpW/HZ1dIx4pxzVe + z7uxk1GC76JC5cOs4xRx4cSRLKFTiJYA78kqowA/73aP0afh3IVU/OJg76UlJTpBxvDCuYvKSkqC91qN + z9ClGw7TC4lxAiWqvuePAKFxrW1JjXh7RUWFrF2zSkf9Dg5mQsqcm+f001ZJZWXlxFqbbxkl7WrJ4oVq + DRwAq49ygdCZ1NRYr/f4MoaXcIIyPtzstHReMD9hKn1M59Tyk0+Uo6D20DxlKUafnUr2D6LJMhCD/IKc + wM+nicE97o0rav+bMTp6IzPsMA8RYNPA8EQdrIB1F4hNJJW0U4ZylGZMZ3e3zsPDtO1zNqyXczedDY02 + YwVMpgDIsnR8Sy1RtZaIyfe+/tobMLV603MEEgB0FF126Ydl1aqV2t6JOLRCrsJEVQI4oRNeDMkQiGBg + gFu/DGHPc8lEUifdts/4XB1bpjU2qhnbBX2AlgiHxK9bu1rWQWfitDmh0Uxz3Qz2SfHT94jptjOAFdJa + K9V0INBAjAoozri2k/w8B3FEEK/1oE0yKI9cRudVVlbIhy7YArNvrXY2FRg6ZNQPP8lyfzi6EqD9AwPS + DlOPdn9tbY1GAWlPbzl3s049c8+9v5E9e/bo/fPnz5fzt26RNWtOy6t9UYr2AgomMFNEpuBYl7WRkbrG + WIVtPPecM2XN6hVqCZTB9GM/xuhsS0PG1qsu4L6R4qLA0hZDC2FBmmLFCvFyB0EiH64lKgJSdrGlqDzV + PWUqOpMhW24SOZ9V57F+wrD2EQB73tknd97zgOwKRhJPh/K0BabnqStOUUo688x1Sun9gYlKjlRWWqaW + QOjHCB0uyrYVoF4a0LoPOEcuhXJkw8a7nOlXImAdRzrVSPpc9B4TvovzAHE+gDzfkbM9viRi+N1mlH79 + gAHkH5MOEcCDImjyxPS03C2wzfnUS0C9+dbbcvPPbtMcgfBd3RBDdPxUVpSrjGfh1LDFHHlLYEKWDgxo + yrT+tsD20wCWgE2PD8iJlvx7I/S2OF1tYqh8T6wjQzug2XRcNu8hYP4GdaE5YqJjOW30SmQEmgWOBw0G + lVSI//n/JWb1pikPBo1cZ8BPu0cpz996e7fcfMttGgzyhmUzUcnatPFM2QaxpCNmvQwZ+OlPe1dskokX + bZ6n+yLI//KffUdMwg4Vy9Pzx75KO47x5y7TdtHc76FDruMFY/OlsiFoRu7Tjj9FEEeGtn1aEhddGyCA + M+ZEy9HJnoYDaURjPdvWNKiC+/3I79CM2rV3n/zq1/fKgUOHR2XJ1KY/esk2FQNhW3K/eMxmvYfFT2v/ + pff8UEqwMadfEWD04O2wGjIIgO0m03bh3D/DzxuZCeLbpZHyI2HC2LXYMLjmPOm9/A/FL7LDlsarIqPO + HEsJsm3xPbv3viO/uus+OXS4eVTgU7PfdPZ6OX/zBl32JteKHcd9CWBnoHSX3/IdKd5xn+WCqfw5gGQQ + gArf190vL6paiM44DydsdCNCFuP2jxMAOxaXwRUb7FQweT14bMWKZSufGdkbD/hqlkIR3LxxvVQFk0+K + 5I3qx0eJ2LOmv1tKt98mDifsYskb+BKluj70183ulxdXc+TlpeiMssA8zK9bjPWf68xVAMbQsvXiVdZm + 3zCpJdNy1fax7ckT+EwSuWjrZlkwb26QlDNcrr0fip8mXrflgJQ+/itwgv6QlgupxYpwDhMX+Wf3K4tr + eO5ywwmG/bBL8gtk6wTfNJ2AAMlZSyQ5c1Hw6NSwV0v5fpryb787P+B/6Pxz5cSli9MOnvcV5YclwgGK + XtshxS9tt1lbBcj/0JwxVrE6gJ74W4oAzgzIdX5pHwVp8/l1kS677tiUFFL/0JLVmcGNkxU1S8PWzwb+ + Xb+xGT/jAn+zAt9Ne/fyRvHjrASiOzkkJU/fKbF9r2VOFyICJPx2/1l03Y/cP11clTLiLMeZdZKJi+Qp + BsJ0ZM2hkcFlXCqtVEaaDhMtw9g+sJ1OnryBv9UCP1T6CtBujrMS+iBgqXd3StkjP8XeuoBtKCZfH0z0 + yPzSN/6d7vWLa6gNzkSfbMVZZgeZQhh4OEuFAWYm5i2TVMMsyeTF5VvLeA22ThlS/h1335838E9Yuiiy + GOT7lfIl46PA//juF6V0x93iaNq+rxlAEzCn+vHMv6KvnoUOUG0832O//BZ6p0LThU2eRhL1ANdOQSNM + OCyvkaFFK61yEHUmTbgEfi8A+3Bzi/zyznvzk/lp4GcHdY6rYeL5lmiORiohpU/dKfE9L1rPK1n/2Nn7 + mWpCFwAzP33/KJTob6M/DrtfWaSrfqVwYStAOdfebgoQA2L9AShOYkAGl64VT7OETYYTTLCE386AzvbH + n5ZXX38rL23/xCWLNENY3tdsP1ICDuC2N0v5Qz8Wp8dO85pO+My7Dqvm488L6I1/Ru/0AgGqqBYmQSvL + cMMZktYD8icXE676MjQgyaY5kpy+KBv4E+572+r+gUHZ/uQOae/ozP3+NPA3yQlLPiBsnyWN69bbV/zq + Y1L6wgPB2D5cTEgB7D8jT9Edv8CfO7BPuV9eWEU/OTiCXwmMuBB9VhQGhfIWA45VBjVLBdvg0jXix4si + PT8xCKQ5AD6Ya/y2tY9EgDTwz9uUk+2/rylfMuzf6e+WiodulljzO/YccWDs0XvRWvzA+ct9D/78A2D+ + ErkpEKDaptRDiUfHXYzbMkNn8uACaX2P1h9ud/u6JDFjsVUGw0jSMcHAV/u9r3dAp5WPxg9C4F8I4JPt + v6/t/Fwlrfz5UvTWc1L+xK9U2VZzPuGHcaE86smKdO0BrL8Nom0nL4ASWKVMAfcAAQRiwKySEHJ5igHD + zKRY4BVkogLODSxalVkR7BiRgLyIcwEwyYPJpiyxmCszZ0yT8zdv1IEg7387f1hJA9/TqXoqYfrFD75l + z+lInwnFUljhbYD1z9BXCUZ/3C8vqA4UQ2UqRbhwgejEwrlGPYxeTJQLQElJzFwkyfqZkg38QqGSSZTg + cK/5c+fInFkzZe7smXLKSSfImWeskVlAAscZvuT7BwL6oetTine9IBWP3SomMZCmfr8Q71+m0m4c/Q32 + L9qILmD1zV1dcv3iGgUTFIEeUNtWXJwRZEPn7RXUWcpDLgA25Qz1gwustpMds0yQJDNYbmy2DFj+zBnT + ddoXZtGaYV6L9zvo0yUAPtO+qu7/ocQO75KQ+r3Rp24arSo/COG/QvPPNU4XQ/b19+6z4wq+vLBSfBsi + 6EeHzhbrFQzoWfI3oE0gCvDf7WmXVM00WATzI5mGExAFI/jc8BMfMOhH5L4BmZe+9IiU77gTolUnddDl + 5PL2/ftZRjM5/A9w8g7AU7UHEn+6y1rPm8Md1wpcB8L/Ca7MsDlC2db0mIXzHHKOSUUCB2JgsbRd/iUo + hMSpKDP5IEBqKkqE9dPub90vdbf8lZX9ZLGw+3UIWL7ZP2HKlE32PIQDzgT/BC813LdP70mPLPrKohrN + NQACdOGmU4Tr0Vv9K1D084sQqlkYTFZAi4AZQkNzTsqMHnrfa2dTXELWDxFa+dgvpOSNpyQctKvT8ReW + +WN/2u3XgO2/AI66qvg3d1llOo0A9AcECJigg0CsMhik+AS+wXyKF3iCA3jH2w6pMpggF4hW8Z+IkF2i + Ll8PrP/1J6Vy+8/Uuabg5Kovhch+P+tXJ+r93+j0F2nxE6Jk/yxpBOAJ5QK2HAWAqAcskLQxnycXMBmF + UBEhmZBY+yEZmnWCeBU18p+iIFeJav2exI/skZr7bpJYxxHruE0F1O+LjNSJctU2gvofxfYd0HAfqT9k + /yxZEGg9d44EacZ0pP82tr/FcVXBuoDYyQud8mB6FNeV/hPXS/sFn1Mk8I2TefV/dByIRDzpjaOfv/bu + f5LS1x63U70zDN7rFaT5D5P9JPUvYvtRcCwN92cQIGvhyG/u7gp1ARZOn7FWhnOBPEWB2qlO4BtAhbGO + Zq1hcOYSqw/8pzjIDtAD+BzuVfnkrVK+88FA648uxpFvlSOofzu2v8bWq9T/m31Z94/o+dYtc0IEcFHb + 5QDOP4gdp2JzvfP1Dop9wq2wc9nq1GYlFdKx6WrpOXWrneFCnP/kANrXnk7LV/H8vVLz4L+JGeix07on + fLv8br4uX60vjQDUHDtw/HuA2S2iep2MQIARq4crF1hYE7btICB0grqI/YxFkLd3MEhXckJYA6uLD++G + GKiVRL1VCv0Mc/mPgwzD2D5HV5W/sl1qHvqROH2dVolKkfX7ar3n7/L1w6Avu5V5s7/E0d+BZvt1FrVh + wGfJ2eXUBYL4AeeVP8sx5vs4mCNSOBKwOEXAtEqTnu8+VTtN2s77tPQvXhPMMBIZSDKV4sDPVo2jJeut + 71IbFNDU+N/aIXX3fU/c9iOBFeBJqjtYiKPQiv00698HDPgkPuVROnn5SVHZH5ZRv/TolrnMwTOe55Ua + riZizB+LjRE4BXkHg+JwTWMqhY51EhEJ2jd/UvoWn44L8SCYMHUdnwXw0XLootlDU9YSSdv6zPApe+tp + qX3g+wHwbdw/RaWvALkf1Bl+FVn/II6+je3rjuP0c/b/+t+8k/OxUb+zZfMc5SGgfsYIFsAMuAl3nylW + FzAFx9ycAAnKHFsDKD9VUScdGz4uvcs2iM+YgRk2pGwyEcLPDDHj+HyuJcDFplhmzpwus7EVFQVDxqbg + 3dnHno7pK3/lEal55Mfi9rQFKd4cc+llZmEv4AWhISl2xM9j+PMpwG43Ez04LrLxgZHUzzLmV7aeO1fj + 75xIGvVcAu7/93iiMdQHCkYCWoTlRhyuSBdwAq+8WrrWXCzdqy4Ur7gi4C+TG9kLQc9uIvC3P/6UPLnj + eenrs2RWVlYqZ6w5Vc5ev1aRIDN5xWQggZ/9m4s9DfZI5XN3SdWO28Xp7UxTvtcXLsBVUP1htN8PEj5a + 8IfrP96GKynmSDTcn5v6Wcb8wmZwAWIPRQHew0S/r6Hi/yKBKEhXkndPBS7iUmsdpJGgqFR6TzlHOtZe + JqmqRqscmsm1EEIaefW1N3QEMZerjxbOJH7l5ZfISScuDb5pMl8uVtljBk5Xi9Q89Qspf+khjZim2X6P + XX01HQzK65uybgxZ/99h/xf4gB6yfnLxplGon2Xcr2zZNCfwKTBc7M0DQvwjfp4rQbQwqCTvaGEaCUoC + JAjHFUAP6J9zsnScdYUM0FfAWTAnSSSEcwKSm91x593ymwceCmbwyDSLNvKWzefItosuCDKLjgEJcrB8 + Zk2XHHxTah79iZTue1nlv9UDAuCnl2fInwNE7uSeXqP7AfDPO8bZy2tsfeODowOfJa8vbN08VwdjMkCF + btsIgv9/6J1FYteeKQwJIm/mShexyggSoOOTNdOlE+Kg5+RN4pVUBIGFY6NGVYt19hBPbvvVHXLPffen + p3CxTbGz5J9/3rlyyYe32YEk+bO1MV5sXbsO7PqKlx+UarD9WMfhcMkWBX6S2v5AoWw/Anyd/NhwpOzb + +J4voNUPo+EpBuEaHhid9Yclr29s3hSIAk6PIqliR5xPoIO+LhxPKEHqWDqBpLBioHfFKp0ggmg3DjPv + W7BKOldfLIMzgGduXNIxhGPiAr688MKL8t2b/jU9X6C2AXWWl5fLZz51raxcuVyicxhM4EXpcK76PQ69 + LdXP3i5lu5+zgZ1wxpEhAt/T1K4JvMK6TzKTPbTh542Azg8ccQeJwMr6x6F+lry/smXTbEkTjPiVqP/6 + QB9QlS6sakKE41px4JY6GRsDGJyEPtC9bKP0YEvUzAh8BqM0ewyAhYC2cxj3y623/koefPBB6e2zegAn + sNq0aZNceumHs2cwGwsJcpqSAV1Co493HJKKVx6WSmwxyH0JdADV9Ps96+HLO60r67URhV/lPod5/x2a + ymhft14wZP3786pvhCdwtPJ/9nTJl+dViafvNEPYvwJYz0QnnSQB2MJ7C87LZb8N2T4yQRSRtbhDfVJy + 6A0p3f+Kmk2p8hoojCUZYBuRPOc1C28XNxaThQsXyIwZM3S62kULF8qWLefKxo12+jqnQC5j0szYhnFj + nc1S+dIDUvfYzVLx1lPi0q0bUn3SrrKuHr7hM3jnUfxsrGMNQwD+T1HjN9Brbb4d1iWND+UHfNv+Aksr + lUL6qTnHcEoW4O93mEcoRKbovMMF9GKW0Rej1xBCptRaCWGmrwcxMFQ/W3qXrpfexWskUT1d/HAlrLE+ + JWxG2lEWHtp5hVhcN5NUakZ5bmTJECJzIOOdh6X8rR1S/sbjUnR0vzgpO2rDLjphl9gj8P3kiBryLFlu + TAKfk8Lfi19/iJ7fbXCOeX4NebD9aCkYAZo3zlItmXPiOT4XK5Hl6Ly/wqWzxXKU6IrkBdUfhSUVRLfC + UR1BESHQAXxYB4mqeumft1J6F6yWwemLJVVSGREPo3/a8A6P6gBjPzns6YDNuwPdUnz4LSnf/ayU7n1B + 4l1HYSolAz5tAU/OlurxshS9Agk/GuBhUeBj247TX0JbX/SMn+JoKFo5TQ8fKKjqCWk6LRtnQ81MiuOp + BOGfFejFb6OyM31r3R+bcz98gtFELmRa7thFkRxJcwSNKRSXyVDdbJiPp0j/rBPAIebCcqgUL1Y0odeO + WegVZbYzgF509B0pPfA6zLmXpKhtP9SuvrTi5wdy3kvYOH6q38949QqGfPDizOOcl41TQjyG03TN7xQu + g+AAGf2YND6cP+sPy4R7qfUciIKUigLWwaDRGYFlsF4CcRDxrB8bNKgkkiOUG7s2Tjg3UaBt+OpMKpFk + Rb0MNi7ANk8G6+dAcZyu3kWuauIx/OzkqfKQuyUTms1EgMc7j0jx0X1S3LIX226J9RwVBxq90XmK+EAg + 471Au+8NTLsJKHnZJTKkx09T/uPU+PHdT7KlHNJhoPU3PFQY6w/LMQGGSJDSVSi1GoqDFfj9LYmIg4hM + PXaSdALRAK5AhKD/IDMuIFM9l7Lz4sUwJ0slUdEgyco6SZbXSqq0CuICCBEvAWKUZVcNKiZQCXCy9lhv + u8S62yTe06LmmwMl1GQtpOUHcA+cOQO+UrsCvsAZO3IWPwL8KNsX/09MQPkUM2T9EwU+yzEDpfns2UBB + D9TgYO+7aNNyVPsXgAu9hSrBs194DCIhcsyFbrhyNhEivXKmMoXhsYToc0aRQ5NTTDY3YA4+HTRWho/W + kMDG53/G64cswFNcQS3XSN0JsHx/pNYZaPtyP65+DZ/2ovFNKuzzpu2Fs/1omRRB2bKRkcOUBHaZi8bO + R19/DWC5HCdLadFlv3QSBXSQgUwkcIoDZGBGshO8JZqAmu9bo7QXuJFVoUvagRmapjVkjyeF2tOvHQZ8 + X6meS3Tfgiskqj308vGCAwRufHjilB+WSQNEGgnUAw2dIOVNM677Bzj6jHBlstBXkDbhpyD4H+qHDCPE + 7Wqa+tsN/QsZJBj+9mgmncryZGblLSp0Oh4vGJY/MWVu7OJn26k2rCvSjh/f9VOp/ws5f0TPmckDPsuk + AqF5w2ydyMkSHseeCqec4WiUP8EpzkKmmQDRt075YG6T2SxqRo4DvpRe8yLo9jQDmCJgR4ufbd+LbYFu + u6AGfAvd8xM0tQe/deJcLu3c9Mixsf1omfTOp07AxRKonDmuC70pVWQc50yc+ApetkEyekFUP5xaJDhO + S/bQvTTVc/TfI7j4l77nPYY+HEIf6q30Yh6rzB9epqTjiQQsfuYdMBP92fjBRIVP4rg+PJ/VmP8giDAM + 8CyhY/goLn0fP/4eXbE/PB92ymQDn2VKO5yIwAUcghWziQSw5GUrDv4Ql9dIDm6gjfqAIkIOwKepHtsO + 3PAdnLgXn9+rLJ9rCbnulAA+LFPe0YoE/BHkF4rlBpyN7DM4vBp7ziUTkcyRxn1AEGEUwIfAP4DL/4b9 + d/G5DOB7no3xa6dMJfBZ3pUOTosE2w86A5nnpUrAFE7D8RdEp6iTYODgBwcRxgA8L7Tj7934/U8g9mcc + xx0IF3MIP3eqgc/yrnZsFBEUqD7dGVKNRmwRay7SnVwp73NEGBPwvs+YPd2438WJ3zgcuRu4l95NwIfl + PelQFQt0Idv1aTgnAZfobBC7bsE1OHk6GlYtoyACy/GGDDmArqclQ/Ecov00fv8Qh/fhC1ptxj2Lp/Mc + vZuAD8t71omhgihWQdRF7BhJxFaHRnHB3o9h24grXC81FmnrcYMMYwA93CdxSwv2D2P7KU48auwiXZ6v + DJCJip6G15seLSyMO1nlPacijSWEy9ZH/bZMQzeGs5Rsw3aBYRKqCMVD1HTM2f6pQohRAK6XIr/5Md24 + 9W3sKePvwIMvM007c1+oDB27L/9Yy3uOAGEJEcG3OaZpCIJHxnFhOs6cjsPzsWekcYaMRIawTApS5Ans + sCjQsR3CY6T2e7HfgZcehuWTCB/Shbn4hccB4MNy3CBAWBQRwtlAbagX4kGHvJArlIhR4K8Q43Be43W4 + Y7FYfSG9UEEU1qHlMcHm+Dnq0p/CKdch1/GDszc+gUZTsduJK4fQgAFOxeIbjRymvb30hxwvgA/LcYcA + 0dKyYU46b8+Y7Pmr0aVxAKRa6GF0nOW4wkUvKDIWiE1XLw02N3y+kHdHOIBG5IKNa8vvxvYyLr4IRH0R + Fe9H1Z0mMnNvkIKuzzPfsPGRyQncTEU5rhEgWg6vm27XI1Q4kjCHpRlYRbGSk16LHcq+GDfNx342zs3A + byIFFUoOa+PvkmGv4NgcApiTsbQAgm141yH83o/n94il9H2oh6yeWxgb1GL1GDvlKpenn/10y+R2wBSV + /w8xfH7lm1om3QAAAABJRU5ErkJggolQTkcNChoKAAAADUlIRFIAAAEAAAABAAgGAAAAXHKoZgAAQ+NJ + REFUeJztnQecVNX59587ZTtld+kI7C5IEVBAjYXiAvYCYhKlF8VEjTFgmi0RkjeWJJZo/mqiRrCgMdZY + MZZFEU2MirHQYSnS2YXtu9Pe5zkzs8zOPXfmzsy5ZWbO18/1LnNn7pyZOc/vPOc5zzlHAUna0zivYlHo + z+/g0Sf09xA88iKeVoBHjs5bNuPRGvHvejy2hv7ejcd/8NhTuHzr35MqsMQ2KFYXQKIPNPLL8DQKj9Pg + qHF3tbRQRzmMRwseG/D4CI+1UhzSAykANiTUop+LxyA8euJRZG2JkqYBj314fInHKhSFey0ujyQKKQAW + g8Y+AE9X4TERgi27XVp1oyBvgTyF9/B4CEVhu8XlyWqkAFhAqIWfB0GDz7e4OFZD8QYShOXSQzAfp9UF + yAbQ4MfdPKr413j8BY8/QNC974WHW9R7OIo6g1JQBI4u3cDZrTc4uuK5Vz9wHVPBzuHDNWAIuAcOZ+fI + I/z86EPJK2DX6KC/FbcbFJcbAm2t8QulD/oO6Ls4F7+bJXhchccwPKpv+6J2n6g3kfCRAmAQ5NpjJX4Q + j2UQdPHHQLAvn7TXRUbu6FQMzh59g4bdpyxozOXDwNm7DJw90Wi79UJjLQ0eRV2CRht5uPiao3pe6Gi/ + Fx50b/Ye+F5MOPpWgKOkB3tcKejE7q04HamKA31H9F1djd/dz/AYiccmKQbGIAVAICGjv4taevznLXiM + BP1Dbx1gBtcz1GofUw7uIaPQ8Aag8fdh16i1V/Kw9+BwCP0MiRXSwcrAPA8qL5aNyugaMDgoRJ2LwUFl + dDoh0NKczDvQd0ffIYnBtXgMweMLFIMjYj9I9iIFQADUp8eK+ST++Rs42tLrB1tOZ3EPZkBkPO7Bo5jx + O7pgy1uIrn1ugRHFNhTFncvK7qDP1bMf+1zMkyhEr8SBguBBL8HvT+SW9CXQd0vf9Rw8OqMQvG9M6bMH + KQBJEmrtl4UM/3w8ShJ5PRmHs88AdOGPA3fFceDs3ocZPGvVMxT6bI7OXdlndfUbGOw65ARzlQKtCXkI + 9F1Pwu/+FjzG4PGx9AqSQ44CJAgF9PC0DI+BCb2QWvnSnszInSW98N8u4WXzHznEzuRuB1qbgn97vRBo + rOO/wOtpLxsPhbyPUDnJCwmLE30G4WA5fTV72WfwHdp3tGz62YLHdYXLt74uvnCZixQAnYSG7m6FRMbp + Q0bvLEHDL+0ppBxkIEEDb243eP+RGiH3ThRHl5LQGT2X3PxgCy9IHPwoAr6afcmIAeUZLJVDivqQAhAH + NPxH8DQHEgjmOXscE2ztS5I3+gBWemq5ybj9eKa/E3STLYOJAfX/6UCRSFUUfPt3gT8sBvqhL2sFCsHC + lN48w5ECoEHjXDR8hSXr6PLVqdK7+tBQ3DGoAIm798EWPWTsoXMmwYKZhZ2Cgc2SHprDkTHxYTdh3y7w + 7q5ORAyxDwTLCx+XQsBDCkAUaPj34bdyNeg0fBqTp8PROaEYIIMMntzcQF1txhl8PJgg0DAhegjOJDwl + fx1+d7u3s+9PJyQEf0QhuDHhN8tgpACEQMNfhN+Gvj4+tl6u3gPAgYZPLb9u0K331exn7qwfjT6JQFdm + gt8nyxkooXhJD82gJA/mOe3/Frx7tuv9Pg+jECxFIZAxApACQIY/Dr+Ff0AwHTUmZOzBZJy++t18im7X + osEzw9+fYmmzA+oisAzD4p76R0uoe4BC4EMh0Nk9OIhCMA2FYHUqZU13slYA0PBpFt5y/AbOiPdcZvj9 + BrHxa72QsfsOfCuNPkVYfgSNopBnoBPfgd3g27lZnxAEYBX+fx4KQVbOSsxKAQgF+C6HOJ+fGf4xA3Ub + PlU4qnx+PNIlYp8u0G/hwN+Bfgu93S4mBLu26PktAvjf37IxUJhVAsDcfYBX8FPH7OcnavgsILVnB/hr + ZWtvBiy9uHd/3YFXEgLv9vWsOxaTAMshuCibugVZIwBo/FX4aSdArM+M/U1XrwGscunp4wdbmK2ytbeI + oFBX6BNqihGgSHv3bo8nBOQNvIUicK6octqZjBcANPzL8FM+BnEW3qBK5Ow7ECtVXqynsYpEEWf/wT3S + 8G0CE4Je/YNCEE+46ffbvoGJdxyaUQgWoBBk9NqGGS0AoVY/ZpCPhp+cNK8dzzFhhr8DfHt3sL8lNgSN + n4TApcODo2FY7/aNEGiqj33PAKxCEagUV0h7kZECoKuvjxWEFrSg+fYxIcPfS4a/Uxp+usCEoB/7feNB + v6v3262xf9sMjg1knACg8d+On+qXEOOzOYq7g6v/4Ljuvq7KIbEt9PuSd0dLpMUi0NoC3h0bwV97IObT + 8L87My2TMGMEIDSu/w5+Iu1putgy0Nx7Wt8uFv56dA93bEL3sEFwKSVWQEJAy6bRcmqx8B8+CJ6t38Tz + Bmja8eRMyRvICAFonMOy+d7CQzPQR62+GytBrL4hawl2borXEkjSFOb59Ts2tueHxu/Zti52HQiwAOHZ + hU+kf5cg7QUAjX8ROOBu0Pos1Nfvfyw4S2Nn+np3bwPf7mrxBbSYgDcAfk6KvL+l43Jcjjz12oION36p + rrSvIh2h+EDPY8DVpzzm03yH9jIvMIY3QF2CX6Mn8P+El9FE0vrXjRflp8Uq3YNGtC87xcNffxi81etZ + 65+O+JoDzMiDB34eT/BM/xYJCYHiIlEInoP/VsCZn55ViHULyoZit0A7TkxdQA/VjVhdwTQfJUjLXw9b + /QFY8pj9fTY3v3eZ9k0ouo8tPi02kQ4EsMH2N/vB3xYIGb14I0+WsDiQGDhy8Mh3gGLhYsWJQIu3UF2J + 1TX07twcu55QXCAAk7FLkHZxgbQTgJDxr9Ps71Ogr2xIzEBfsNXfAIE2+7b6ZNxk6P6WQHsrn06EvQNH + XvBs564EeYguqjMxvAEWIMQ6o9klCMYFhqWbCNj3V+GAxn8Z9vdpFV6uXDOXH39IJV97Ve6gmn9rVBFT + wtfoBx8ZfGP6GXw8mCAU4kGCUGhP94BtuNJvkOb1QHMDE4EYXQIv+GE2ikDaZA+mjQCEjP9p0CgzLbrp + OmagpitHP563eiM72wVy65nRNwXY2UiUHn0gZ9zZup7btvotCOyPmyqbEiQCzoKgGNipu0CNh2vgcdpx + o1Aqsf/wIa1boNsGP0AReETzCYGguCuK9eZnfQl0gMb/CBr/FVrX2cw9WqRDA1ph1rtri20Serz1Yo3e + XXkBOEq6g3vcOezfrsHD2dlRUAiOZNbeiyDgx7I2BJcr8278mp09q1eCv+YAeKpeS+neYcJi4OpkEyWg + kSOsU7FWcqaFR+jQxA+PoghwpxdLAUiAeMbPdpzRWlOO1HrXVrYEl9WQS+85gsaExh9Iwe5zL5nPDN3R + ozc4u/fC/rW1uwb5mpvAd2Av+PfvYcLQ+sKypO9FnoATRcDdxWGLmAHVK9qDUcurpHpF8wk00RABKQA6 + iWn8FOw7diS6bIXcyxTg825dhy5/o5FFjAu19nRQMC9RyG3PvWgWuE85A4VukOXGrhcSBe/2zeD59ypo + feWppLoTFDwkj8Bqr4Dql6timGaXgOqXZ9OX2t4lRwSkAOgglvGzH4Vy+TWM399wBLzb1lnm8rO+PRo9 + tfiJBvPInc+dMhvcw0enjcHHw9dQD571/4PWfz6ZcLeBPAHyCMgzsCxWQF0CSiUu6sK9TCJA9U1zVMkP + T6MIzGx/vhSA2DTMqXgJf+ypvGtk9O5BI9mOszxoDT6WwWUBZPjeIz48EnPzyejzZl4N7oFYydyp9dnt + jt/jAc+WddCy4sGExICM34VC4OritEwIKKNUc21Cnw88m7/U9DixPrxc9MTWi9nfUgC0QeN/RInR8rsH + ahs/y+O3YBHOZAzfOeIkyL9uSVYYvRYkBm3r1kLLQ7eD76v/6nqN1UJAAkDzCbiQCGyJKQKPoggslAKg + QXzjH8E3fgr27dxs+h55yRh+3vW3Q97E88HZWf8Wg9mAr+4wtLz3OrTcrW+2rZVCwERAK3uQicBXMUWg + 8PEtLCYgBSCCWMbvKOoMrgFDNY2fpnCaHeyjwJ6nVl8f3zn8JMi74nrIHXO6LX50O0OtY+tna6D5/qXg + 37Yh7vNZjKDY/GAha5AqjtMUAVqE1N/A3+0pLAJ2qAvWlwB0GH/FcP4LLTB+Ssv11Pp0RfXJ8Atu+APk + 9I+/Mo1ETduOrdB0x8/B93X87gGNGriLnaZOToonAp6tX8ftDhhcxLhYLgANsysuU5zwDO+akhf+gtUt + f6ClEb9g8yL95OJ7DvlYyx8PafhiSUQIqFtAQmBWt4DqKOWiKDm56otMBL5hdZVHwAfTi560Nm3YUgEI + GT83vddOxk8Ze20HfHH7+c7hJ4YMX3tRIqs5WFML/3z9bdiweSscwr+J0pJiGDKoAqacfyZ0K4mzOKqF + tO3YEhKCT2M+j4w/p7vTvDkHbKWpYazOqogtAgEUgRlWioBlAoDGPwB/qI1Ygpzoa0peAbjLtYy/CTzb + vmFfrNGQwZPhx0vZpYSdoruetLXhE2+9+wE8/dzLMZ8z43tT4exJ400qUXK0blkPjTcsiJtgRAJAQmCK + N4B1leos1V0VJALbvmF1V0UAvFjPBqEIWDKL0BIBCBk/d0qvXYxfb6tfeNcKyD3xNFDsNKOFw5tvr4Kn + nn1R13NnXToNzj0z7paJlhLAH6blo/eg6cbLYz7PVG8gngjQEGFbq/paAJrx4wyzQgSsEYA5FZvxh1E3 + l+wLHMb9AoPGv85w42d9/drg0F4s3JfMh8JrbkmLMfztO3bBTUtuT+g1ty25EQb0P8agEonD19oKTQ/d + Bp44cxBMiw1QHR44ghsTiFWHsd5tKXpiq/ZcZIMwXQCw9X8T+/3n8K65Bw7XVM+2TV8Ybvy02k7rXl/M + oT1y9wvTwN2P5A/3PgD//eyLhF5z0pgT4OeLrjGoROKhbkFTnG4BDRnm9nKyVYuMhHmxZfxhayYCW77m + vi7gg5XoBZi6JZmpAoDGvwiN/x7eNWffcv4qPjSmSuuy8fpPAmHj+odiu/w5V/wMCuf8yPbufjTfm6k5 + mTImz614VHBJjIW6BQ1//QN4Vjyg+Rz66dylTsPzBkgEXBoiQKsL+b7dxn0dfoRfoSdg2kKjpgkAGv84 + NP73ee9Ju7hwp/SyhIoNhhs/9fVjDe+xIN+fXwB3j9grC9uRbdU7YPEvf5XUa++587dQXtZfcImMp5WG + DX82O6Y3QAJAsQEjYSIwYAhfBGhjWf7+hKRjF6IIvG5o4UKYIgD108u7otu1mxf0o1bfSWmVHHyU3lt/ + 2LByUWvfutvLXH8t3NOor39zWvT1eXz59Tq48dfJNSi3/+YWGDl8mOASmQPNM2i46ybwvvkPzedQVyC3 + j8vQuEBS9TsAzVgn+3R6ZptxlT+EKQKgFfQLKuRg/JbUCklr9PuPaC67lDJ6+vsFdz0FeSeONawMZnHe + 1MuSet0bL6fN0naaNK95B5pv0u4CmREXoLkDzp6cPSj9vuAGpRwPFxundegFHGdYoUIYLgDo+t+Orv8N + qgvoFrn6D0ERUC/uS4Zv5CYdzPix5dfq75PL3+nh18HVJTMm7Fz145/Clq38PqcWAyvK4aH77zKoRObi + qTkIDVdN0ewSkAdAnoCRIkBegKNLqerxQEszeHds4I8M+OCOoieN3YvQUAFomFl+vOJW1vLeh9bx4y2w + QNs1Gzmf39sQHN/XwnXGBVB4y70pr6VnJz5c8zH8eultCb3mN7feBGNPP9WgEpkPGy68/XrwrtJeg4Bi + Aq4i4/oDtJ6AUtBJ9TgtYOOjNSvVBAJemFD0lHFbkMXeRD1FFKeyCnjG36033/g9bcHdeA0invHnLr4N + 8i+annZR/niQIZ926snwwYcf6Xr++LGnZZTxE87cXCi69X5ofGIoeB7jezbhumGUCFDddleop7STLQSw + m8BZy4Kq4it4Niw/2zAPQGu8X8nNDw6PREP9IdqRt7XZkPLEM/68Wx+A/MrzDXlvO9DQ0AC/u+OP8MHq + D2M+b/y4sXDzDT+DoiLtvRXSnaaVL0DrHddrXjfSE9Cs/0hwizp1/ceuwCrsClQaUh4jbtowq+IyxcWZ + 4edwsh1YFLcq/R98e3cYtqAHZfZ5DmsP8xUufyetEntS4bU3VsIjf1sGe/bs7fB47969YOHl8+GC87g5 + WhkHTSxqnDdZ87q7azBz0AgcXUrA2Us9vMo8YNp9yM+JB3hhOnYFhEdlDRGAxrkVTbwhP/rQjs4lquf7 + 62qYABhBLONnWX33Pw/uHr0NeW87s3HjJmhoDM5Q692rFxOAbIOJwDUXY4Wt5143UgQStoUANBc+vlX4 + KrHCBUDL9ad+Dm88lFwe784tXNVLlVhuP4v0L3s7Y1belSRHW81BaLpae4TAsO4AecO0jgDPG6Yh8IYj + qseN6AoIFQB0/ceh66/O9nPQJJ+h3PF+o/r9PjL+g9rGX/S3f0njlzBoH4OGy8/SFoFuTnAaIAIsHtCf + s8Con6YPr9fqCowXOSog9lMFI5bcVF9uss+B3dL4JZZDdYHqBNUNHlSXqE6Jhuo+d9cqtBVmMzyCNiYM + YR4Atv73Yev/4+jHHYWdwdl7gOr5bDMFA4b8wkk+PFifXxq/RAPmCVx2qmZMwKhkIVffCu4mN7T3oL9R + vbAoegH3oxdwnYj3FuIBoPEPwH7/j9R3d4KjO0dV0bXx7d8l4q073haNv22vNH5JclDdKFj2LkChOlmH + oLoVa95IsjBb4Lj7zp7HcD1nsjWyORHvLUTOGmZXVGGhVEvIsISfrur0R9/BPbG2V04KNrFnj1czt7/g + 6Y/A3V1fpHvnt3tg7ZfftP97yKByGDxILvCZLbTt3gnNcydwr7G5A73FTyAiOyF7iYbshOwlmoAP/l30 + 5NaUs7VSFoBQ4O8D1Y1p/74+5arnM9d/d2J56Xog49dS5/y/vaNrhd6Nm7fCshXPwaEa9SSs0pKucOm0 + C2HUSMPnZ0hsAC0w0vLD87jX2CzC3uKTaF3HDAIlV70JqXfXZgi0qvcdFBEQTF0A5lTsQTVUNa005Mfr + 17DtlL2eVN+2A22HtIM0ub/6P8g7I36G35r/fAbL0fjjMfmM05kQSDKf5rdegLbf/5R7jUYFckrF5giQ + 8dMcmWio0eRNjkOvd2/RE1tTSmJJSQCw9V+Erb9qhR9Hp67Y9++rer6/9gAeYvfuYxH/Q/yIf85Pfgf5 + F83kXouE3P0HH31S93ue9p0xMH/m93Q/X5K+ND5xP3iX3829RgIgenjQUdwDj+6qx/0HvuWuHYBewGL0 + Au5N9v1S82MccKv6MWfoA3R0xwPY6os2fnL5KdOPh3P8+bqMn3j2xVcTet+P0FsgpAhkPgWzr4XGrevB + 94F6gR6qewp2B0SODNBUeKVTF1CiZqM6SnuBn0YnooOFQRtMWgCSLnn9zPI7HG7ll9GPk/Hz1vbz7dvF + pvqK4mjQT32NjfUvf0/XKj6Jtv6RSE8gO/Bh/7txwZncRCH0gIUHBSlrljd6RmsJkhcdTcAD9xetSG5Y + MGkBaJxT0Yrq0zGPEVXL1U+9sjGteEJjmiJh/f5GjYj/c5+CS+fuu6+++Q68uvLdpMtx2sljYN7M7yb9 + ekl6QIuKNE8/hXuN1hekDUpFQrkzqhWy/X62C7bKCwiAt/DxrUktYJGUADTMKn9EcSmqdZZoGEPhzPP3 + 0mIHAgN/tEGnVo5/7v0vQu6Q43Xf69kXX4N331+TUnmkCGQHLZ99CG03zOVeY5uPCNyYVMnJ48+docVD + eMOC3sCjRU9tS3iz0aRkS3Eq81QPYuvPM35/Xa1Q4w9v0snDNWVuQsZPFOSrh10S5aNPaATh+ZTvI7E3 + eWPGgvMsvtDHW1I+UQJtLczYo2E2xlmtimuTOkg4CEitP/oNqtc5ab2zQJRLji6L/7C6z5IKWl809fvz + r0l8+etSQZthkggQ0hPIbPIW/Q6avvhIFQ8IN0wilxr3YZ/fVdhZ9TjZmu9Qx/UcyCbJNhP1AhL2ABSH + Mkf1oFbrX1/LREAU5PrTwSPvnmexbIk7NCeMPE6IF0BITyDzcbrdkHPbMu61WPUzKWjkrL5W9bBILyAh + D6BhZvkiVeAPQq0/cFr/OnEr/DCFrfFxoxbuH/8W3N2Ty4cg45804fSUAoGRSE8g88ntPxC8l/0QfM/+ + RXWN6qhD4KgARf5pQh1ENW6aXgDaaNGKbbqHBROKWjTMLj+AKtNxjI8i/5xgBS3vJXJd/7YaftQ/OOS3 + KqnWP5K7/+8R2LhZXIqyDAxmNn6fDxrnV3KHBp2FCuSUiOsK0HLitIxYNF7KDoyKr2FDebjoia26+7W6 + rQaVZZzK+IGUSF0w1vpzXJdk8bcGNIf88u5KzvWP5qrLZ0O/vuKWBpPdgczG4XRCzu+Wca9RXaU6Kwot + W+LZHnoeXclW9d5bfxfAAcvUjzlAyStUBf9E9/1pq26eq+KcuxhcPXpHdz6SIh+7Aot/tJB5Aru+VQ+z + JAOJAJVNegKZCS0k67loLgReeVx1jepsTg9BXgDaUqChDpSoacpsjwFKDIq2taCt6tpqXFcXABVlgOJW + qqMfJ7eEt7AhW+hDkAD4Gv3Yr1LfS+neh83yE71nX3NzC9z9wKPCRIA49eTRMG+GFIFMxNvcBC2zxgJw + slzdJQ7sDggKBlBXm7OwDsXZVKtpB1heQFnRim1xs+/0eQAKP9fYQQoU1fqzdF9Bxs8Cf6ikPJlyLb7d + kA07yRO4/porhIrAx598zs5SBDIPV34BuH5xF3iX/kB1jequI98hJiCIff1AY51qZyGyQZUAKBC22Wnx + bquraNj3V81/dZA7wnY4CXQ4fAIj/2yKL2/Mf9gYyBl1mrD3iSYsAscIjAmQCCx/WsYEMpHc71QC0Ca3 + 0fhB6FqCwdmAHe2NbNDBWcGIZ7M84gpA/YzyhbzEHyW/k6ostNEhePlLciUKtf5ejS/Pja2/iMBfLKQI + SPRCddG9+A7uNarDojIEaeMQZmNRdsdsUVWo4JBgvHvG7QLwd/alKVC5+N5RwT9O6mKyaLX+jovmQE4/ + c3bxkd0BiV5yBo8E79hzIfDhmx0vhLwAV2cxDZYfu9iO6FWD0BZpfwESiA444FqIM1U4Zqmw9R+gOBSV + tbGNPaNUiFp+3j7nyRCr9c+ZJWQxVN1IT0CiB0VRwP2Dm7nXhHoBTQ1BLzvaC+DsOky2SzYc636xPQAF + fsULwAWX+uIE/wTBWv8AqIJ/jgvngLNLsZBhv0TIoyFCFIF7DPAE5kpPIGOgLeY84zheQECsF0C2pnTq + ON2d2WR04l0wGEgTZDTnB8QsESrIBarHCopUaYmEn5RJAKz1b9To+5vc+keSHxIB0Z7A49ITyChcV2p4 + AY3ivACurbGcHPWS9zwb7nBd6wK5Do4czth/cXfVG5Hrz1upJBnIXaIkCtX7Yuuff416BTKzoTwBkZ4A + QXkC0hPIHJp+d63aC0BcXRzC9hl0lPZkawZEomWH/rZAWaen+TkB2qUJug5RzyaVoU1/O3ZAAs1iWn9C + a9jEddFsYe+RCtITkMTDPXcx93GRQ4IsFhBlh8w2eaNjPFsOoZmrePMJxY+j+9BBYqjlV3LVyxSpEhGS + xNfkx4Mz4WfoaMj7vjrRwircLhecNGokfLNhM9TVixG/Xbv3wqGaWjhhxDAh95NYB62K7VmNHgAnQ4/W + EHS4Bawc5PeBI7+Ioo8dH6fJQeoFeAbe9r/D3HFKrgdQd1n58YpTUU3w57b+rWIi/wTP+AnXIv4Yq5Uw + T+Dqy8V7As+8IOx+EmugEQHnj37DvaZVxxOG5gcw2+N4AdHlQVsmm+bdhisAihPU0TYF3f+cfH7yjwAC + vkBwZ59g5LL9oOm+7r5lQt5DNFIEJFrkDBsV3GMwqj5THae6LgJuUhDZKCf3mGvToCUAvOg/p/WnDcpE + be/tbeB/KY7vX2V41l8qSBGQ8HA4XeCYfi33mlZdTxRmewFaH1OHF6AxGsDNA+Bt9cUijtETfwS1/oSv + WSP4N3Gq6eP+iUJ5AotQBO598G/C8wTmTL9EyP0k5uOcPA38f7td9TjVdXcXQTkBra2q/QTJVmk7sQ6P + cWyaUJWifnr5LdzknxzVSmDCWn8K/nEn/Zx+btps550fEgHRnsAT0hNIW9xdigFGj1df8IfqvAB4Nsiz + VbJpZttRqCf5OEE1IK24c4N3iPYAODuWJoOvRSP4NyfuXAZbkS89AUkUjsuuAf/nqs2zWZ13CmjbmA1G + r8aNtko2G/C0dnw0aNv/r0P5VHd0KEOiH+JtWSzK+EkNA60BNpoRfbj6qrcXtzvSE5BEQsFAXt2mOs/z + ehMm4FdPAgINL4Bj2x08AHQRBmBfQR1BoCWIo1v/KHVJFl8r/1tQZi6ydfAvFtITSD8CDfXQ+vwz0LL2 + E/BilcwZVwmdv69vc9lYUDAwcP5sUF5X7z9Jdd+ZL6COt7WqlwlnXnvH+Tlk22TjnZ45mhXY8d0VuEp1 + cxr+46xBTjuXiMCv4f47x50r5P5WIT2B9MD3xafQeOdS2H/RRDjw2ENw6NP/woFPP4Ft994JB197Wch7 + uM65lPu4Vt1PFJ4tMpvlLUUUZeMdMgFvPqH4j4pD6RAtVHJyVTnHlIUUEDH5xw/cvH8i58pbWEJFOnM0 + Y3CT0IzBGpkxmBLU2re98gLU/fZmOPKPFVC/aT00+fzQ5PdDc+jc5AtAc90R6H3B1NTfsKgz+J59QF0O + L4oDrRmYajXH8jrY/BxOVqAvaoEeP7hv+/LwI+F/RnkAinolUVpoADqONPo5fY6kyt3GV8B0dv+jafcE + +nBHYZLi4/+ulZ5AEvi3bITG3y+FAzOmwP4//xFqv90FR9Dfr/P62FEfOoePRp+YSL2TbGgSv+umZQOJ + 4vd4VHZKtqvCoQyP/GeHGIDihKLo5yu07l90lFHQZp/tmX/R7zlmnO3H/hOB8gR+giLwJ4oJ7N4b/wU6 + IBGg70jGBGJDrb13zSpofO5paN60AdqwLnv8AXZuC52P/tvPHvOEro2cPkdYPXSccREE3lOLNtmAI0+A + p0sxuSiDJ9tVjQ9E2Xh7M1s/Q2P9MNb/j8r/F+UBaAQAXeWZ597mh0RApCfwb+kJaOLftweaH7gbDmJr + v+/2JVC7YT3U+YKt/RFfx5b+SPvffqjH5xSMPgkmPLwc+k06U1h5nENH88upYQOJErTJKB+AE7sjIm09 + 0s9WR914LgR5A9H9iiSgfOgAb5dv/KKMWO7bDkgRMB7PW69C3fVXwb7pF8GBZ5+CI0eOtBv5kZCRtx9h + IcBzS34h9JsxBy567R0499EnoM/JpwgtFy0fHuimDgiTDQiZG6BlkzwbjrD19i6A4lDvJKJwhv+Euf8a + EVDlwvlC7m9X8g3oDpAIENnaHaDWvm3lq1D/jxXQii5/pIvv4bj2kdeKBg+FEbPmwgBs7XM7qbfiFsqF + 8wCWqWe2ki3QfoIpQ15AVKuvODjdgAhbP+oBOJQ+qhs61csFBAQJQIAJlqI6HGWc9dUzDOkJiMH74Sqo + //XPYR8F9f72EBypO9LBta+LcO2plT8Scv+pte95wVQ4+5kXYcqzL8HgqZcYb/yI84TTgVfnA2JW0kdP + gnMjF2e6j6L0bL/c/hgnAUihcUSjPAAPPwDo7NVfyP3tjvQEkoMN4WFr3/Dc09CydzenZferAn3hv3N6 + 9YERs+fBsVOnmWLw0Th69QM/p84zWxABeQBRQ/ZcDyAiEMg8gLrLyvmrhjrV6hHw8TruiRHs93AuZHD/ + n4dRnsBzL78h7H52wffFZ9Bw51LYN2US7Lv/j1C7e1eHIF5kIC/aA+h54VSofHg5fO/Nd5kAWGH8hKug + MEYcIPX7B3hb8nFsmAjbPLuqKKBeLYTX/yf8AgTAq6F4oydk1PCfHmiI8DoUgfsEegJVH3wExw4sg+PT + PFmIDeGRm7/sr5qtvSd6OC+itR+ELf3wCIO3Rd06eRLAG0+pHiabUJwpxgGoC8CzWRKBqO5B2OaD8uBQ + C0AwESfa/RfTWdFyeZTBJ/A/QIaTn5cL1121AO576DFhIvA8egHHDx8q5F5mQwk7zc8/A42rq6C1Hl3+ + gJ/r0nui3H661nviZBg4ZRqUTYwYwrNRnVJGjeUKANmEI1dAIJAMPSp2R7as8jAcEQKAb1uhLqlDZf/c + IEMSBDTCCI6K9G6xUoG6AyJFoKb2MLuPyO6F0dAQXuPzT0PTpo0dWvujrTw/mq8UdoLBs+bCsWj4nfr2 + tfpjxEQpG8b1RLRsIlEC6KFTv78D0f+GozYf8gAU9b5C7Tv/Rt5d0OQFv0YAsEi1DmlWIVoENm3ZZnsB + CFDCDhp9w5uvQluotecO2XGCfCUnngwjp1wCg6fG3QXbNji6FIOPU/eZTYiAYnSuqHtxRvPCNs+CgNjY + d42+rrAFQKDjEoAiRgAC/IAHBUcyJf8/FcIiIMJwaRMTu0Kt/ZHrr4K9M6bAgX+saE/YCSfpdAzs+dqT + dlryi6D/9Dkw5bV34LxHn0gr4ydcuXkQKFBl3B9d2i9VfJx9Azmtbdjmgx4Ar+thkAeg2f+n4IiEIcoT + KC1R6bqlUGvfsvJVaMLWvnnPbu1AXodWP+gRUMLOmFnzYMCkyZZF8UUROP50UD5+S/U4iwPkpBYHCOB3 + p0TbLX+zEIar7lKNIUCDRgBoCjD3Ix53Uur3ziBEiMCxA+2xohJF8ptp7H51VVSGXlS2XpQAQFEn6FM5 + GY7D/n23oZkTH1KGjOYKgJAVgshGdTbUZPsOdAXU/ghv7FBU/18r77kwvVXdCFLpDtAwYEmxdR4AW2Hn + hWfg0MypsPeWn8GB99/jZ+n5Oubo09/+Hr1gxE9vgGmvvw0Tfnt7Rhk/4RjAz3YVMidAy05DgUDqatDm + JN46P6Ugb3ahtzCSf5+oGwlIAGL3JZXjTQHuW2GPcVqbQXkCPw55At/q9ARIOC6Zcp4l3ycl7LSsfAXq + 0c3n5d4HW31Oth6eB1w0DQZOubjDRJxMrBP+LqXg4AYCxdyfjdaFI//4vQa8XvA3obC2+qLfY5QLHDBQ + dQcDV+LRzHiKXnVI0k7YE3jq2Rfhf1+tj/v8S6acC31NjP5Ta+9Zswoalj3cnrDDM3K10ftZwg4N3w23 + MEPPbBwoADxEZAMysLEOeMjoPdjae2KpaFd+niAvCcgvaMaCxnCHs6izrRI27AYlCy2cOx3e+FcVVK3+ + mBvhJ7f/kotCxm/Cd8nm3D/+MDR+UMVm4Wm19urx/AD0rgwn7Ew+esMs+f2VgiK+TaYwFEgte8ATYIff + 0xj/BSFc2Nirk4Da1xSKeEjQb6OlcnIIUB/nnVUJleNOhc1bqzsEBynrT0+rTwlCNF9g05bqdhEhD4PE + 45STRumOG1A0v+b3v2kP5EVm6/FbfT/bK2/IzLlsMk6nPvZO2DEShzsHeGaQqAfgDxk8O5LrPmAXQAFV + ElBwMc5oi88OdU4HyGBHosGPTCDVl4z9uZdfR+P/nHt94+Yt8NrKd1AERsP3pp7P3iMWe+77I7R6fXGX + 1aLHSsecDCPR6AdPSa8xe6NwYGOXjLffsZUPiDDJrkrDrPJaxaV0kH22EUhU+mCA1h5PMRWYFM5To/7o + gVPPgZyf3Zvw/Q4ePAQv/PN1+PTz/0FTc3CLpIL8fDhx9PHYDz4funXj97WyDdqb4MFHn2Ctvx7IC7j6 + ijkxlzT/fPzoDpNvooVAKSyCsikXw/BZ89K+tTeinrUsngLOnZtUj7tLnDRdt52wsVOqsKhdhSNYpV8A + aCegFPMA6IPwlgH3nzMLcheqti2LyZv/eg+e+vvzMZ8zDX8c+oGymUM1tfCbO+5JOCuQPIBf37AYSkuK + udc/nXsp1GxYpxrPLzp2KAybPRcGTEz/hB2CDP9FPGIx67LvwrlnTUzovs03zwTXRrU35upEGbhKcBtx + rVmz4lhFQUDOvl9g64CMHuMnwj9cNovAo8ufhoYG/UGhMPQaeu0vFl/DvX7sLb+BT5bcDHXr12Hfvgj6 + oMEfNzOzEnb0GD8RrouJiICj9wDsd6kFwEdbhyum2V6ZS3Eqthh/0/uRd+zcpcv4w9APOHTIsTAMj2xj + /cbN8M36DUm/nl67Du8xdLB6u4hOg4fCpBXq38G+zUZirNuwSZfxh6E6SV2CeN0Bj8cD3jYPOGoPpFpE + EQxwcfNyWQzQnkHAF17W/6OEWYkeQzYKwAerP8JeW2rdNroHTwAyHaozifLkM8/Domt/0OExMnhPWxt4 + 6ew5OpmusKUp5TKKgJ8HwDDA4AXccv0GdeAkHp+t/V/qb5yGbNu2A3ze1ASA7pGNJFNnqG5qGXxCmNjW + 8gXAoAKwoAbP4xh2oq7Xk/sfjsImCrl02eYFbKuutsU90o11STQyBNXNdevWQ9/e8fMxfMcMAtcmdQyA + TM/MHTE1PADeTEBBicop0NiUnPFnKz5B8zck+mluakm5ATWzsx2jC2Ai6/4LcHr87cCHpdAXHXBMX1uP + bBgBrQew/8DBlO7Ro3u3rPveWF1JkkEVZbqe59y1Oen3EIlGF4Dzg7M1Aq33AsaMOj7h/ln/fsdAQYFq + 24OMhz733r37Ur5HpkMzX71eL/bdPeg1Bc/kxn+7J7F1GEYcN8SgEhqHqR6A4lLUq5VAYn2ec86sTFgA + 6DXZyHdOGgMfffzvlO+RSRw19jbwYhfJh3/zukoTxp4CTz/3ckL3nnD6KbR1rq7nkh3w6r3ZvpZ+D0BA + 0ZTwbkiqC/rvTmP64/GL/mCNvopNY7Pj2A+TfUyadAa89M9XYeu26qReX1Fexu6Rrt8dDYGSsXtDRk5n + vcOiJ485Ab76ZgN8+U386dfh5+t1/xlatmDyl+3ihR3ZYiAGrgmQKjMv+y47xxMBMv6F82ebUSTbsui6 + a+BHP74+6demCzTkFjZ4X8joVYvaJMj0700BeA7iigAZ/8UXnpPSe3XAzGHAgC/Qgq55x2xAs4M+LYlF + 96k/v3DBbOYNrHy7ig0PRtKttITNA6CWP9upqCiHxYuuhYf+8ojulOCiokK46ocL2WvtRrgVDxp6Yq16 + ouTn5sGCWZfCJ599ASvfXaWaTEVxAnL7SQAYCZiN0tQgsKRJU01dAJolEpUObK4AON5/EeCa3yX8OjJw + Og4eqmEHQcZPh+QoZ581mRnzrbf+Fvbuix0U7NWzJyxd+isYNFC9TISZUCAuEPC3G3rY6K2ADJwOEoDa + w0fYY8Vdu6S05qLzW1uMAmzXDgIaoQGp7n2mQTYaPRO9g4fa/z00TpITGfRTTz4GK1f+C1Z/uAZWr17T + 4fq4cafDuLGnwznnnGVIeXlQvzxs2OSuU2DO7/fbNn+huLgLO8LoDfglhOkxAG4hNDL2UoRGErXu68dK + 4ODtZS7pwPrQJBWa6BNJfn4+G+0458yJMYc8ycCtMHIfGrY/IhBnVWtuB5TmBnPT/WKg1M8o3+vIUXpG + PawOAgY464QlgaeWr+7+h1aDqzN/7rkkyIuvvAEvvRJ762/yhH5yzZXQv595i3BQAI62pg635JFniRrX + 5i+g8/0/4V4TtjCoPlYp9dPLqxy5yhmqS7xRAAHBQe8R1dLEwcfvegPcvfunfP9M5ZHHnoTVH/1H13NL + UQR++6tfCkl+Yq23D1tvv4+15mTYPk/QsJOe7JLl5Hz5IRQ9erP6gsa2eQay1NwYAEELovMSCvfvApAC + wCUR4ycOHaqBFX9/no2UaMFa6JAh07CZH1U50rhFDKNJ+AQDgFp9AHO/cxcqzha/J3CGgoapdFgFzJiC + 0PvwgieB2v2GvF+6k6jxh6HXTLvoPLa0F2vBQ4E1adjWw2IA9uCwy9fofwL/uLzDwyhO2C1oFymHS2H7 + CNMChdSCKxF+A/UUKMVXNxqrfzu2fQ1QeXFixc9QwoGzx554Btb8+79J32fzlq0wsHyAwJJJRKA5BGi+ + Lq9Vas8fUIl/JL78SQyYIERpgoNEQwktbczbHjy/CHwPvM+9n8PpBCdvj3MLCRupFlr9Y9YCR02t5j33 + mef/Cf/9PLWFTM6eNIEdEntRctMUrhfAVv01d77dRGamKALmaU8Mj6Hmtn8yIRCNy+UK7XVwFDu7wiKM + n5h6/tkw/vTvCCiRRBRk+CQAPNiCOSZWyeLXtyvmD7zH+ICub7eAZ+AJwt8yHOxKB555QYzxE3169bTL + Uo6SEFTHNbHgt3KF3ngV/l89FGgUGuseUd+ozQABSBf+LtD48/PyoAL7/9L+7UXM/r+5PxbZfHsmoL4t + Ywwm+OVkZ5X9+wuvCDN+4qyJ4yFbv0s7Y5M5AASz+XBMfq2Z7xwIpxpHHe6tX5hZDNsg2vhHDBsi+/42 + hdVxTt23IB7FbD4sANVmvrPi534H4KrZa6cxUlMQbfx9eveESy+5UNj9JOKgus3qOHDqv/mr7VXT/1gX + IGCyAJDYaQ3q5Wz5AlpHjDWzOJZhhPFfdfls1v+X2A+q21r4ze+tVdP/2kNxNeeZOBSIOHPUuQJE0/jv + QsPU9FmJJlnI+D8VuGEJRfx/KI3f1hS9/AAUfMDZ1g4tz9dmrgKUvLGdWd/RYcAA6xOMMqsAlBCkcNyA + WCqZKUjjz0606jabHGeu/bfH/I56AOcOeAlPU80qAWUgONz8hKBDNz0JvuL4u6ukI0Hj/1LY/YLGP0sa + v81x1u6F0tv4k7P8HtoK3NTivFzy5naWdx+ZmW/qSECslEf3lszcy08af/YSs06bHwBst/VIAagyswSx + 3J7crz80syimII0/u9Gs0wFL9tupCv/RwQfHboCpPRFHTvQU5CA0H2D/0pfMLIqhPPuieOP/wQJp/OlE + j1sv1pgAhA6A2QHAN7e323305FxzE4J8/A9OX1Tu12u419INafwSqsta+S1aNmAgHWw8ejIQ5QebOhKg + tTBK7jeroXX4aWYVxRCeffFVA4x/Jhp/Lsg03/SB6rJWPbfA/V8V+Y9oD6DKvHJAzDXQ8tLcAzDO+GXL + n25o1WVW983X8arIf3SMAZwzgHY6qDWzNGw4MJcvj3WX/hyaTzzbzOIIgbL7/vHSa8LuJ40/fcn/9C3o + /OwfuNf8raYP/xHFJSu3t0/+U1lezTn9PwcTuwFUAmcBf52w1uFj4fDcJaYVRQTNLS1wxz0PQEtLq5D7 + SeNPb7o+vkRzBMDX5Dc9Aahk5Y7RkQ/wBOBePPEXLTcIRx6NBvC9gAO/pKSgntxrdoQy/ES1/mT8V86X + xp+uOGv3Qfc7+ck/FPzzt5ju//8JBWBR5AO8prfKnLIchfWFuFOk0IX67C2zi5MSX6/fKOQ+0vjTH1Z3 + Neq1yev/h6mKfoDb7KIXYLo0OQsd3NJQ63/gF0+YXZyk+euyFbC1ekdK9wga/wxp/GlO99/PYV6ACpr8 + 02h++B9bf5WFaSzSDS8bXBYVbEFEDvQF5n/2L5NLYx3S+DMDqrNc4wftum4wXJvW2ByUuQqmTQwiKBvK + pTE5qODTt6B5jHkbWqZC7149kvYApPFnDlRntXbLYFN/LR7+C8PvApzdvwxP2wwsDBfqBigaPsmhhX+A + 1gr7Lxi6Z+8+uO8vjyX8ut4U7Z83A/Kk8ac9uVu/gNJHfs69Rok/Vrj/SHnJWzuqox/kmlvoiebODkQC + bfy1AukoevdJs4uTFGTIY04YmfBrpPFnDqyuagX/TM77D7GWZ/yE5p5e6AXQcME9RpVIC2eRthdw4NoH + wNN7oLkFSgLKAfjr8hXoDcTf75C6DD+YNxONP9eEkkmMxr1nC3T/M39FK9b6N1jS+i9GAbiXd0ErCEhY + Mh2P7T+oQeGa9JghSMZMRl1RFnu347GnngTX/XCBNP4MIlYdjVW3DUazUJob7t255cjhXw7sQllDQw0p + kgakkg6N9QJJXZvGnG3I9mGioe3IThw1kokAbUtGgb3irl3YcdzQY2HGd6fACSOGWV1MiUAo6l/8/B/5 + F2nor9ma6D+2/g9pXYy3NZipy4QxAsERAa35AV3xCz64kJ9bbUfKUQDKNTwBOZ8vs+iqZfwQmvNvzQ8e + 022Oua/3obP6mz45iEGbhHTiJwYRB6/4A7SWH29umSSSGORu+x90e5Qf+SfD99Sbnvcfprj0Xzs0d/6K + KQAEisAyPM0TWSI9OPMUNkeABwUC91/zgMklkki06fHANayLyoNy/n3m5/0Ty9H458d6gh4BqMTTe4IK + pB/yAjo7NUtYe8lPoWn0meaWSSLhUPD521D8wl38i9T61/msav0nogBUxXpCXAEgUAQoKahMQIESwpnn + 0PQC/HmFsO+ny9lZIrEKR0sj9LxrHjvzCLb+lgz9VaPxl8d7kl4BsCQnIJ4X0HD6xXD4vB+aWyaJJIKu + b/wFirSG/qxt/RejAHDH/iOJlQcQybLUypIkbOhEWz3pi6fgi0RiBVT3NI0fQnXXuqGeZXqepMsDIA6d + aU0wkHB1cWguGOLpVQH7rv4/k0skkQD0fPBH4N67lXuNFvzwHrHE9SeWl74dO/gXJl4eQCTkTlgiAL6m + ALg68QWAfoDOVU9CXeUsk0slyWY6Vz2lafwE1VkLiev6h9HtARDoBdBoQGWipREBzRTUSg4i9l39Z+YN + SCRGQ4bf88FrNa/TYp8WzfgjqrD1n6j3yYl4AMRSsEgAaAFFR452QLDkxbth/4I75aiAxFAo2k91TZNA + aLFP61iayJMT8gAI9AIsGRIkaI4AzRbUonH0WVBz8WITSyTJNkpeugcKP9deoYpm+5m91VcE1dj6xx36 + iyQZAZiPp8RXvBCEq7NDc1tx4tDUxdA4Kj1WD5KkF4Vr/wWlL2uPhtM23946S1v/BSgAyxJ5QcICQFjp + BdBaAe6u2l0B6gLsn3cntMl4gEQgOdjv77H8l5oJP2zM/7DPiq2+wiTc+hPJCcBka70A6gqQJ6AFGf/+ + eXfIeIBECGT0PZbfwERAC2r5LXT9iQWl7yTW+hNJCQCBImCZF0C4irTThImGUWey7oBEkirk9hetfVvz + OqX7eq1Z6SdMNRp/wq0/kegoQCQUbbTMC/A2+sHtdoKisaQJ/WDerj3hyBkzzS2YJKPosmpFTOOnDT68 + 1g35hUko8h9J0h4AYbUXoLgUcHfVXjeAIC+g4QQ5a1CSOEVfvB0z6Bfs9/utWuc/TNKtP5GqAFSCFVOF + I6BugKuTdjyAzRqcc4cMCkoSgvr7PZ+4QTvoh3jr/Vbs7xfNRBSAqmRfnJIAECgClmUHhiEBiBUPCIrA + 7VIEJLoIGv+NMY2f9fvrLXf9q9D4dWf98UhdACZZs4lINO4SB+sSaEEisOfK+1hcQCLRwnV4H/R++LqY + xk8uv6fGcuMnykvf5a/3r5eUBYBAEViCp1tF3CtpaO2AEu2gIEEewF70BOTwoIQHGX0vbPljDfdR0M9T + Y9kc/0iWovEvSfUmogSAFg8lL6CriPslCwsKFscOCkoRkPDQY/ws6FdredCPoEU+qfXXXOxTL0IEgDg4 + ydrkoDCUJMREIAZtPaUISI7Sbvz7Yhg/BI3f4mSfMAu6vZt40g8PYQJAoAhYHhAknPmxMwUJEoH9378Z + vF16mFQqiR1xHdkPPf7xu7jGT5l+Fm3sEU0VGn9Kgb9IRAtAGZ4+B4u7AoQeESAPYO/s25gYSLIPMvpe + T94UM+BH2Mj4yeUf3S3FwF8kQgWAOGiHgGAIvSKwJ4tE4PCROnjv/TWwfuMWaGltZY/l5ebC0MEDYeKE + 06Frl84Wl9AcyPh7p5fxE0u7CQj8RSJcAIiDE/uTFzDKiHsnCgmAszD2xyQROHTmldBw/GSTSmUN76z6 + EN7FIxaTzhgLk/HIZIr+9w6Uvv1wXOP3NVo+vTeStd3e2zFa9E2NEgAy/s+NuHcy6BEB4vD4GVA7PjPn + Drz93vt4fKDruWdOHI/HBINLZA3FH6yArh88Hfd5NjN+YjQKwFrRNzVEAAgUgSVgk64AwboDXeOvgk5e + wKHJV2bUCMGWbdXw4CPLE3rN1QvnwcDyMkPKYwXU2pe+8zBr/ePhPWwrt59Yisa/xIgbGyYABIqALUYF + wpAIuHWIABsm/O5NGTNC8OjyFfDVN+sSes2I44bBFfMywxuiSH+v52+LG+knPPYz/io0fmFR/2iMFoAy + sMmoQBi9IsBWFrrgJ9A4+FQTSmUs1/30xqRed99dtwsuifkUbvwYerz2p7j9fcKGxh+M+r8nLuofjaEC + QKAIXIynF41+n0QIJwspOvZFOnLyRXDwzCuNL5RBbNq0Be69/8GkXrvox1fDsccOFFwi8+j29sPQ5ZNX + 4j6PlvGyUZJPJNPQ+LW3HhKA4QJAoAhQhuB8M95LL7SwKJtAFGPuQJhW7BIcOP86dk43Nm7cDHfd++ek + XvvTRdfC4MGDBJfIeHLR1e/++n3sHI9gbr+fLehpM5ah8S8w+k3MEYDKftQFoHiALYYG20EPIKebM+Yq + w5HUjpsBNXikGwt/8KOkXvfIX9Nvy7WS1U9D8er4UX6CjL7tICqArYL9DIr2T+xWtTPlXP94mCIABIpA + GdgsHhCGugPOAn37pDb3HwGHJi+E1h5JL8JiOnf8/m5Yv2FjQq8ZOmQw3PCL6w0qkXhy92+D0ncegfwd + X+l6Pm3eQW6/DQn2+6t2VpvxZqYJAIEiYLt4QBgSALYJqc79kmvGTofDJ08Bf679hwvXrd8At9/++4Re + c+ONv4BhQ4cYVCJxOFoboesn/4SSD5/R9Xzq79OmnRbv3hOLaWj8hvb7IzFVAAgUgSVgo/yASFhcoFR7 + J+JoPF16wMHJV0DjsfYfKXjoL4/A++/rSwSaMGE8XPXDhQaXKHUKN30M3d55FNxH9ut6Pu3Y6zlky/5+ + mKVo/EvMfEPTBYBAESAv4GIr3jsutPFIFwfbjFQv1C2oOX0GO9uVxsYmWP7441BV9X7M51VWToB5c+dC + YWGBSSVLHHLzS9Y8rdvdJ2izTg9t123bhh9eQuOfZvabWiUA9gwKRuDMxy5BsVN3l4CoHzGJdQ08Nk4g + qqpaBa+++hps21bd4fHy8jK48MILUADOsKJYuqCWnlz9Tl+9q/s1zOWv9YGv2b6WDyYG/aKxRACIkAhQ + ULDMqjLEg1YYorgAiUEi1I+YiB6BvYVg//79eBxgf/fo0R0P+5aVGf4aMvzEFqAmo6f+vg1W8IlFNQSD + fqYbP2GZABAHz+hHHgD9qrYbGYiEeQMliXkDRF0aCIGdCRt+5wQNn7X6NbZv9Qky+ondVu0UPslHL5YK + AIEiUAkW7y2gi9CmpInEBsI09xsBh8Zexs6S+OTv/ApKP/w7OycK6+sftuXYPg8y/iorC2C5ABAoAvPB + BusJ6oFtRIJCQOnEiUKeQM1pl0HDsaekxfChmdBwXtGmf0PJR3/XHdWPhNJ4vWj4NtioQy8L0PiXWV0I + WwgAkU4iQJAnkGiQMJK64ROhEYWgYdApYguWZhRt/jcUouF3/jo5J7A9yGf9/nyJsKC0ascyRbHe/Kwv + QQTpJgLULaBdiZydkhcC8goaB34H6kZMSqvswlSgrL3OX70LhVv+k1RrT5Dh++p9wd150sr2g8ZPf0gB + 4HAg3UQAIeN3ohC4UhACwtO5B3oE34HmfsMzzjOglj5/59d4RqOvS87oCdbio+H70PAD6WX4xILu6PYH + AsFuihQADQ5M6Ge72YN6aBeCzqkJQZjmY4JC0NqjDJrSLIBYsPMrbOmrg4a/6+uU78cMvy5tDZ9Y1v39 + nWx2nxQAHaAIzIc08wTCkPHTOoSuotj7FSZKEwoCdRPCZ/IY7EDugW3MlS9AQyf3vkCAwYdh+/DRyrwN + aWv4xAI0/mXhf0gB0Ek6i0AYEgESg2RGDfRAYkBCQMuXtXQvY6MLLSgOokcZKEqfh8bNzgeq2TJb5MqL + NPZIWFQfDd/bkL5WH6KD8RNSABIgE0SAYPsW0urERfpnHKYKEwMUhTCtOoSBDJxa8TBGGTgPFthDg6cW + 3+bZe3pRGT8hBSBBMkUEwpBX4CxQdK9BkOnQ1FxfUyATWvtIuMZvN9JCAAgUgUoIriVg67ThRGBBw4Ls + FIOw0dM5jfv2PCi9dxoaf5XVBdFD2ggAgSJAcwdIBMosLopwSAwceSExyFOEBg/tALn0vpYAM3p/S8YZ + fZhqCBq/Zbn9iZJ2tezAePtPJRYBBQ0d+Qo701Lm6SYIzOCbAyyYR7vsZEifPhZsSm/3D6yZ1Zcs6VWr + QoREgGIC9lxUxABIABy5JAhBT8HhBtuIAhm33wOsZfe34bk1Kww+ElrCa0G6GT9hjxqUJCgEtLTYEqvL + YRXUbVDIU3CThxAUBCYUBohD2MjpHDyCq+oGsIXPUHdeL0vQ8JdaXYhkSWsBIFAEyAsgbyBjgoMiCYpC + 1GPOoCcRCbXctGZeJGTkWdaSJwK19tTqm7aApxGkvQAQKAJlEAwOZnRcQGIbqL8/DY2/2uqCpEpGCAAR + igvcA2k4h0CSVizDY3E69vd5ZIwAhJFdAolBZITLH03GCQAR6hKQCFRaWxJJhlAFQeOvtrgcwslIAQiT + 7aMEEiGkdZQ/HhktAASKAAUGyRuQAUJJIlCgj1r9tMnqS4aMF4AwIW9gEcjYgCQ21Ne/N5Nb/UiyRgAI + GRuQxKEKMrSvr0VWCUAYFIJ5eLoXpDcgCUKt/iI0/OVWF8RssmsOaojQD01L8C6xuCgS61mCR3k2Gj+R + lR5AJLJbkLXQeP7ibHL3eWS9AIRBIaiEYCahHC3IbCiqT4ZfZXVB7IAUgChC8YElkIGLjmQ51RAc089K + V18LKQAaSCHIGKpBGr4mUgDiIIUgbakGafhxkQKgk1CMgJKJKq0tiSQOVXgslX18fUgBSJBQavFPQE47 + thvL8PhTpqfuikYKQJKE1h+g7gGlF5dZW5qspRqCCV3LM2V+vtlIARBAqHtAYjDf2pJkDcsgaPRVFpcj + 7ZECIJCQVzAVgqsVZ82KxSbxUuh4Wbb24pACYBChDEMSg/kgk4uShfrzy0C6+IYhBcAEQmJwBgS9gkqQ + k5C0ICOvgmBLvyrb03TNQAqABaAgRIpBtnsH1MpX4fESGvwqi8uSdUgBsJhQ3IAEoRKCYlBpZXlMoAqO + Gv0q6dpbixQAGxLyECohOLw4CtLXS1gbOqrxqJItvP2QApAmhBKQBsBRQSDPodLKMkVQBcH+e9jgt8uE + nPRACkAGEPIYiLAwRP9NlIH+hKXq0BEmbNwd/pYtevrz/wGA0nIwAAAA5R34QQAAAABJRU5ErkJggigA + AAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIl/1ICJf + 9Y8iX/XPIl/1/yJf9f8iX/XPIl/1jyJf9SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIl/1jyJf + 9f8iX/X/Il/1/zBp9v8wafb/Il/1/yJf9f8iX/X/Il/1j////wD///8AAAAAAAAAAAAAAAAAIl/1zyJf + 9f8+c/b/rcP7//L2/v////////////L2/v+tw/v/PnP2/yJf9f8iX/XP////AAAAAAAAAAAAIl/1kCJf + 9f9Mfff/8vb+//////////////////////////////////L2/v9Mfff/Il/1/yJf9ZAAAAAAIl/1ICJf + 9f8+c/b/8vb+///////l5eX/////////////////////////////////8vb+/z5z9v8iX/X/Il/1ICJf + 9aAiX/X/rcP7///////l5eX/Pz8//39/f/////////////////////////////////+tw/v/Il/1/yJf + 9aAiX/XPIl/1//L2/v///////////39/f/8zMzP/f39/////////////////////////////8vb+/yJf + 9f8iX/XPIl/1/zBp9v//////////////////////f39//0FBQf9/f3////////////////////////// + //8wafb/Il/1/yJf9f8wafb//////////////////////39/f/8zMzP/f39///////////////////// + ////////MGn2/yJf9f8iX/XQIl/1//L2/v///////////39/f/8zMzP/f39///////////////////// + ////////8vb+/yJf9f8iX/XQIl/1oCJf9f+tw/v//////39/f/8zMzP/f39///////////////////// + /////////////63D+/8iX/X/Il/1oCJf9SAiX/X/PnP2//L2/v9/f3//f39///////////////////// + //////////////L2/v8+c/b/Il/1/yJf9SAAAAAAIl/1kCJf9f9Mfff/8vb+//////////////////// + //////////////L2/v9Mfff/Il/1/yJf9ZAAAAAAAAAAAAAAAAAiX/XQIl/1/z5z9v+tw/v/8vb+//// + ////////8vb+/63D+/8+c/b/Il/1/yJf9dD///8AAAAAAAAAAAAAAAAAAAAAACJf9ZAiX/X/Il/1/yJf + 9f8wafb/MGn2/yJf9f8iX/X/Il/1/yJf9ZD///8A////AAAAAAAAAAAAAAAAAAAAAAAAAAAAIl/1ICJf + 9ZAiX/XQIl/1/yJf9f8iX/XQIl/1kCJf9SAAAAAAAAAAAAAAAAAAAAAA8A///+AH///AA///gAH//wAA + //8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//gAH//8AD///gB///8A///ygAAAAYAAAAMAAAAAEA + IAAAAAAAYAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHDzqABw86QAbPOkAHDzqARw96gIcPOoKGzvqPBw8 + 6okcPOrIHDzq7Bw86vocPOr6HDzq7Bw86sgcPOqJGzvqOxw86gocPOoCHDzqARs86QAcPekAHDzqAAAA + AAAcP+sAHD/rABw+6gAcPeoBHD3qBBw86jQcPOqfHDzq6Bw86vwdPOn9Hj3q/h496v4dPOn+Hj3q/h08 + 6f0cPOn8HDzq6Bw86p8cPOo0HD3qBBw96gEcPuoAHD/rABw/6wAdQOsAHD7qABw+6gEcPeoJHDzqZBw8 + 6t0cPOr7HT3q/R4/6/weQuv9IEnt/iNN7P0iTOz9IEnt/h5C6/0dPur8HT3q/Rw86vscPOrdHDzqZBw9 + 6gkcPuoBHD7qAB1A6wAcP+sAHD/rARw+6wkcPep3HD7q7h0+6/seQOv8Ikns/jxl6v1oieP9i6Lc/pmr + 1/2Zq9f9i6Lc/miJ4/08Zer9Ikns/h5A6/wdPuv7HD7q7hw96nccPusJHD/rARw/6wAcQOsAHEDrBBw/ + 62QcP+vuHUDr+x9D7P0yXOz9fJfg/rrC1P7NztD+z8/P/5SSkP6UkpD+z8/P/83O0P66wtT+fJfg/jJc + 7P0fQ+z9HUDr+xw/6+4cP+tkHEDrBBxA6wAdQuwBHUHrMx1B690eQev7H0Ts/T1l6/6isdv9z9DS/tHS + 0v7R0tH+0tLS/7CurP6vrqv+0tLS/9LS0f7R0tL+z9DS/qKx2/09Zev9H0Ts/R5B6/sdQevdHUHrMx1C + 7AEdQ+wKHULrnx5D7PsfROz9NF3s/qWz3P7S09T909TU/tTU0/3U1NL91tXU/tXV0/3V1NP91tXU/tXV + 0/3U1NP909TU/tLT1P2ls9z9NF3s/h9E7P0eQ+z7HULrnx1D7AodROw8HUTs6B5E7PwjTO39g5ri/tPU + 1v7V1tb+1tbV/87Ny/7U1NL+19fV/9fW1f7W1tT+2NfW/9fX1f7X1tX+19fW/9XW1v7T1Nb+g5ri/iNM + 7f0eROz8HUTs6B1E7DwdRe2JHkbt/B9H7f1BZ+v9xcrb/tfX2P7Y2Nf+zMvJ/oJ8dv6opaD+1dXU/tnY + 1/7Z2Nf+2tnY/tnZ1/7Z2Nf+2tnY/tnY2P7X2Nj+xcrb/kFn6/0fR+39Hkbs/B1F7YkeR+3IH0jt/iBK + 7v11j+b919ja/tna2v7b2tn919bV/p2ZlP1vaGD9qaWh/tfX1v3a2dj929va/tva2v3b2tn93Nva/tva + 2v3Z2tr919ja/nWP5f0gSu79H0jt/h5H7cgfT+/tIFDv/iRT7/2ktOj+5OXk/+Xl5P/l5eT+5eXj/97e + 3P6jn5n+eHFp/8vJxv7e3dz+3t3d/93d3P7d3Nz+3t3c/93d3P7c3Nz+29zc/52t4v4jTu79H0nt/h5J + 7uwjX/T8Il/z/ipf8f2/yu3+uriz/tfV0v7z8/D+8/Pw/vLy8P7s6+j+0c/L/qKfmv6mo6D+7e3r/u7t + 6/7r6+r+6Ojn/uXl5P7Ix8X+rquo/q653/4nUu39IEvu/h9L7vslYfT8I2D0/itf8f3Byu39q6ej/s/N + yf709PL99PTy/vT08f3x8O394eDc/qGdl/2jn5r9wMLg/sXH4v3r6+799PTy/vT08v3OzMj9qqai/rrE + 6P0pV+/9IFDw/h9O7/snY/TvJWH0/idc8v21wvL+8/Ty//T18//29vP+9fXz//Ly7/7Bvbj+kIh//9za + 1v7w8O7+4OHv/7C04v6Yndr+sbTf/97f6/7y8vL+8vPy/7TB8v4mXPL9JF/0/iZe8+4oZPTMKGT1/iJc + 8/2OpPH99PX0/vX19P729vT+8/Px/sK9uP6Ee3L+tK+p/vHw7v729vT+9/f1/vb29f7s7fP+xsnq/p6j + 3v65vOP+8vP0/oyi8f0iW/P9KGT1/ihj9MwnY/SNLWf1/CNe9P1TePH96Ov2/vb29v709PL9wr65/oR8 + cv20r6n98fHv/vj49v34+Pb9+Pj3/vf39v339/b9+fn3/vX19v3s7fT95+r2/lN38f0jXvT9LWf1/Cdi + 9I0jX/Q+MGr16SZi9fwpXfL9qLfz/vP09f7Cv7v+hHxz/7Wwqv7y8vD++Pj3//n59/74+Pf++vr5//n5 + +P75+fj++fn4//f39/729/f+qLf0/ild8v0mYvX8MGr16SNf9D4iXvQLLWf1ojBq9fsjXvT9RWzw/svR + 7v6dl5L+tbCs/vPz8f75+fj++vr5/vr6+v76+vn++/v6/vr6+f76+vn++fn5/vf4+f7S2ff9RW3x/iNe + 9P0wavX7LWf1oyJe9AsybPYBJGD0NjRt9t8rZvX7JF3z/VZ48f7Kz+v99PT1/vn5+v35+vr9+/v7/trZ + 2P3a2dj9+/v7/vr6+v35+vr9+Pn6/tXb+P1XePH9JF3z/Stm9fs0bfbfJGD0NjJs9gErZvUAKmT1BCpl + 9Wc4cfbvKmT1+yRd8/1IbvH9r7z1/u7x+/75+vv++vr7/7Gtq/6xrar++vr7//n6+/7v8fv+sL32/khu + 8f0kXfP9KmX1+zhx9u8pZfVoKmT1BCtm9QAjYPQALWf1ASZi9AotZ/V6O3P37y5o9fsjXvP8K13y/lx8 + 8v2crfb9xs74/tHX9P7R1/T+xs74/pyt9v1cfPL9K13y/iNe9PwuaPX7O3P37y1n9XomYvQKLWf1ASRg + 9AA3b/YAIV30AC9p9QEnY/QKLGb1aDxz9985cfb7KWX1/SNe9PwjW/P9K13y/jJh8f0yYPH9K13y/iNb + 8/0jXvT8KWX1/Tlx9vs8c/ffLGb1aCdj9AovafUBIV30ADZv9gAhXvMAQXj3ACFe9AAwavYBLmj1BCdi + 9DY1bvajP3b36j119/w1bvb9Lmj2/itm9f4rZfX+Lmj2/jVu9v09dff8P3b36jVu9qInYvQ2Lmj1BDBq + 9gEhXvQAQXj3ACFe8wAAAAAAIV70ADx09wAiX/QAL2n1AUB39wIjYPQLJmL0PjJr9o07c/fLQHb370F4 + 9/xBd/f8P3b37jtz98sya/aNJmL0PyNg9As/d/cCL2n1ASJf9AA7c/cAIV70AAAAAADwAA//4AAH/8AA + A/+AAAH/gAAB/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/gAAB/4AAAf/AAAP/4AAH//AAD/8oAAAAIAAAAEAAAAABACAAAAAAAIAQAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAABIqrwAMKtQAFjflABQwzAETLNMDEzHbFBY14z8XN+V0FzfmrBg4 + 5tQZOObtGDjn+Bg55/gZOObtGDjm1Bc45qwXN+V0FjXjPxMx2xQULNMDFDDMARY35QAMKtQAEiqvAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARKssAEjHbABQz1QARKssEFTDdEhY15EMXN+WLFzflxxg4 + 5ugYOOb3GDjn+xk55/sZOef8GTnn/Bk55/sZOef7GDnm9xg45ugXN+XHFzflixY15EMUMd0SESrLBBQz + 1QASMdsAESrLAAAAAAAAAAAAAAAAAAAAAAAAAAAAEy65ABM03gAVNNYBDizPBxU14S8XN+aFFzjm0xg4 + 5u8ZOeb3GTnn+hk55/saO+f7Gjvn/Bo75/waO+f8Gjvm/Bo75/sZOeb7GTnm+hk55vcYOObvGDjm0xc3 + 5YUVNeEvDizPBxU01gETNN4AEy65AAAAAAAAAAAAAAAAABE04wAOM9MAFDPbABIx2wsWNuRBFzjlrBg4 + 5ukYOef3GTrn+ho75/ocPej7HkLo/CFI6fwlT+n8J1Ho/SdR6PwlT+n8IUjp/B5C6PwcPef7Gjvn+hk6 + 5/oZOef3GDjm6Rc45awWNuRBEjHbCxQz2wAOM9MAETTjAAAAAAAYPecAFC/RABU10gATM9sKFjjkUhc5 + 5sAYOubwGTrn+Bk75/ocP+j8IUnp/DJc5/xPdeP8aonf/H6X2vyIndb9h53X/H+X2v1qid/8T3Xj/DJc + 5/whSen8HD/o/Bk75/oZOuf4GDrm8Bc55sAWOORSEzPbChU10gAUL9AAGD3nABc62wATNuEADzTTBhY4 + 5EEXOubAGTrn8xk75/kaPej7H0Xo+zFb6PxfgeD8kaTX/LK60PzAxM39w8TH/aamp/2mpqf9w8TH/cDE + zfyxutD9kaTX/F+B4PwxW+j8H0Xo+xo96PsZO+f5GTrn8xc65sAWOORBDzTTBhM24QAXOtsAFzvlABMy + 2QMWOeMvGDvnrBg85/AZPej5Gz/o+yFJ6fxJb+X8jKDa/LvB0fzIys79y8zM/MvLzP2/v779f3t4/X97 + eP3Av779y8vM/cvMzP3Iys78u8HR/Iyg2vxJb+X8IUnp/Bs/6PsZPej5GDzn8Bg75qwWOeMvEzLZAxc7 + 5QAXO+ABFTjeEhc85YQZPejpGT3o+Rs/6fsiS+n8TXHk/J+u1vzHydD9zc3P/c7Ozv3Pz879zs7N/cvK + yf2ysK39srCt/cvKyf3Ozs39z8/O/c7Ozv3Nzc/9x8nQ/aCu1vxNceT8Ikvp/Bs/6fsZPej5GT3n6Rc8 + 5YQVON4SFzvgARYy0wMXPeVDGD7n0hk/6PcaQOj7IEjp+0tv5fyisNf8yczR/M/P0f3Pz9D80NDP/dDQ + 0P3Q0M/90NDP/c7Ozf3Ozs390NDP/dHRz/3R0dD90dHQ/dDR0P3Pz9H9yczR/KKw1/xLb+X8IEjp+xpA + 6PsZP+j3GD7n0hc95UMWMtMDFjnhFBg+54sZP+jvGkDp+R1D6fs0W+f8kqTb/MrM0/3Q0dH90dHR/dHQ + 0P3Qz87+0dDP/dLS0P7S0tD90tLQ/tLR0P3T09H+09PR/dPS0f7S0tH90tLR/dLS0v3Q0dL9yszT/ZKk + 2/w0W+f8HUPp+xpA6PkZP+jvGD7nixY54RQYPuU/GUDoyBpB6PcbQun7Ikzp+2eE4fzCxtX80tHT/NPT + 0vzS0tH9xMPA/bGtqv3GxMH90tHQ/dPT0v3U1NL91NTT/dTU0/3U1NP91NTT/dTU0/3U1NP91NTT/dTU + 0/3S0dT8wsbV/GeE4fwiTOn7G0Lp+xpB6PcZQOjIGD7lPxlB53QaQenoGkLp+RxF6fw3Xuj8nKvc/NDR + 1f3U1NT91NTT/dHR0P6rqKP9eHFq/pSPiv3Dwb/+0tLR/dXU0/7V1dT91dXU/tXV1P3W1tT+1tbU/dXV + 1P7V1dT91dXU/dTU1f3Q0dX9nKvc/Dde6PwcRen8GkLp+RlB6egZQed0GkPprBtE6fcbROr7H0nq/Fp6 + 5Py/xdn91NXW/dbW1v3W1tX91dTT/cG/vf2Jg339bWZf/Y6KhP3Ew8D91dTT/dbW1f3X19b919fW/dfX + 1v3X19b919fW/dfX1v3W1tb91tbW/dTV1v2/xdn9Wnrk/B9J6fwbROn7GkTp9xlD6awbRurVHEbq+xxH + 6vslT+r8e5Lj/NHT2v3Y2Nj92trZ/dra2f3a2tj+19bV/b+9uv6Gf3n9b2dg/puXkv3Ozsz+1tbV/dnY + 1/7Z2Nj92djX/tjY1/3Y2Nf+2NjX/djX1/7Y19f91tbX/c/R2P16keH8JE7p/BxG6fsbRer7GkTq1RxS + 7vAdU+38HlTt+ytb7PyesOn94uPj/d/f3f3o5+X96unn/enp5/3p6eb95OTh/cvKxv2Yk439npmT/cTD + wP3Pzcv9397d/eDf3v3f3t393d3c/dzc2/3b2tn92tnY/djX1v3R0ND909PW/ZKk4P0pUun8HEjq+xtG + 6vwbRuruHlrv+x5b7/wfWu/8MGDs/a686v3JyMb9pqOe/dfW0v3u7uv97+/s/u/v7P3v7+z+7Ovp/d3b + 2P7IxsH9j4uG/pGNif3W1dT+6Ojo/erq6f7p6ej96Ojm/ubl4/3i4d/+ysnG/Z2Zlf27urn9nqzf/SxV + 6f0dSer8G0fr/BtH6/kgXO/7IVzv/B9b7/wwYO39r73r/cG/vv2Yk4/90tHM/e/v7P3x8e798fHu/fDv + 7v3u7uz95uTi/c/MyP2OiYP9j4mE/cTEzf27vtz909Xj/enp6/3u7uz97e3s/ezs6v3Qzsv9mJON/b27 + uf2mtOT9Lljq/R5M6/wcSuv8G0nr+SFd8PIiXvD8IFvw+y5f7vyot+395ebm/d7c2/3s7On98fHv/fHx + 7/7x8e797+7s/t/d2v20sKr+tbGr/c3Kxv7V09D92Nnl/qSo2v2Tl9T+qKvY/czO4/7k5Or96+zt/urq + 6f3d3Nr95ebm/ae27f0tXe38Hlfu+x9V7vweUu7vI17w2SRg8PsgXPD7KV3u/JGl7fzq6+/97+/v/fHx + 7/3x8fD98fHv/fDv7f3e3Nn9pqCZ/YR7cv2ppJ395OPg/e/v7P3w8O/96Ont/dfY6v2ztuH9lZrY/Zqd + 1v22ud392tvn/ezs7f3q6+79j6Tt/Chc7vwfXO/7JF/w+yJd8NgjXvCwJ2Lw9yBd8fsjW+/8bovt/N/i + 8P3w8fH98vLx/fLy8P3w7+3+393a/aqln/6CeXD9mpOL/tfV0f3w8O7+8vLw/fPz8f7y8vH98fHx/u/v + 8f3e4O3+vcDl/aCk3P2prd394eLs/d3h8P1tiu38I1vv/CBd8PsnYvD3I17wsCFc73gqZPDqI17x+SBb + 7/xGbe38vsnw/O/v8v3y8vH98fHv/eDe2/2moJr9gnpx/Z2Ykf3X1NH97+/t/fPz8v309PP99PTz/fT0 + 8/3z8/P98/Pz/fLy8v3v8PL96erw/ePl7/3r7PH9vcfw/EVt7fwgW+/8I17w+Spk8OohXO94HVnsQilj + 8MooYvH3IFvw+ylc7fuGnO785Ofy/PHx8P3h4N39q6ag/YN6cf2ak4z+19XR/fHw7/709PP99fTz/vX1 + 9P319fT+9fX0/fX19P719fT99fX0/fT09P3z8/P98vLz/eXo8vyGm+78KVvt+yBb8PsoYvH3KWPwyh1Z + 7EIZVOUVJF/vji5m8fAjX/H5IFnv+0Rq7fy7xfD85ebo/aynov2DenH9n5mS/djW0v3x8O/99PTz/fX1 + 9f319fT99fX0/fb29f329vX99vb1/fb29f329vX99fX1/fT09P3v8PP9u8bx/ERr7fwgWe/7I1/x+S5m + 8fAkX++OGVTlFRxN0wQhXO1GLWfx1Ctl8vchXPD7J1vu+2mE7fzDyOX8op2c/J+Zk/3Z19T98vLx/fX1 + 9f329vX+9vb1/fT08/709PP99vb2/vb29f329vb99vb1/fb29f319fX98PH0/M/W8vxphe78J1vu+yFc + 8PsrZfL3Lmfx1SFc7UYcTNMEJ1/rAR5X6BMmYfCIMWry6ydi8fkgW/D7K1zt/G2H7PzCx+H84uLk/fLy + 8v319fX99vf2/fb29v309PP93Nza/dzc2v309PP99vb2/ff39v329vb99fb2/fHy9f3R1/P8b4ju/Ctc + 7vwgW/D7J2Lx+TFq8usnYe+IHlfmEydf6wEhW+0AHFDkBB5Z7TErZfGvMmvy8SVg8fkhW+/7LFzt/GyF + 7fzAyfL86uz1/PT09v329vf89vb2/eno5/2gnZn9oJ2Z/eno5/329vb99vb3/fT19/zq7Pb8wcrz/G2H + 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 + AAAZPeEAGDnhABY53wcZPehKGj3pyxo96fcaPun7Gz7q/Bs/6vwcP+r9HUbr/SdY7f1Od+j9kqfa/sHF + 0f7Lzc/+zs7P/s/Pzv7Pz87+zs/N/s7Ny/6ioZ7+V1JN/ldSTf6ioZ7+zs3L/s7Pzf7Pz87+zs/O/s7O + z/7Lzc/+wcXR/pKn2v5Od+j9J1jt/R1G6/0cP+r9Gz/q/Bs+6vwaPun7Gj3p9xo96MsZPedKFjnfBxg4 + 4QAZPeEAAAAAAAAAAAAJN9UAFzfOAho95SkaPumvGz7q9Bo+6fsbP+r8HD/r/BxA6/0eR+v9KFjt/WOF + 5P2suNb+y8zR/s/P0P7P0ND+0NDQ/tDQz/7Q0M/+0NDP/s/Pzv61s7H+enVw/np1cP61s7H+z8/O/tDQ + z/7Q0M/+0NDP/tDQ0P7P0ND+z8/Q/svM0f6suNb+Y4Xk/ShY7f0eR+v9HEDq/Rw/6/0bP+r8Gj7p+xs+ + 6fQaPumvGj3lKRc3zgIJN9UAAAAAABY2wgAZPuMAFjfeDBk/6HobP+nsGz/q+hs/6vwcQOr8HEHr/B5H + 6/0pWOz9Z4fj/ba+1f3Nz9H90dHR/dHR0f3R0dD90tLR/dHR0P3R0dD90dHQ/dHRz/3Ozsz9v768/b++ + vP3Ozsz90dHP/dHR0P3R0dD90tLQ/dLS0f3R0dD90dHR/dHR0f3Nz9H9tr7V/WeH4/0pWOz9Hkfr/RxB + 6/0cQOr8Gz/q/Bs/6fobP+nsGT/oehY33gwZPuMAFjbCABMv1AAQNsoBGj7nOhpA6dIbQOr5G0Dq/BtB + 6vwcQer9HkXs/SlX7f1mh+T+uMDW/c/Q0v7S0tL+0tLS/tLS0f7S0tH+0tLS/tLS0f7S0tH+0tLR/tLS + 0f7S0tD+0dHQ/tHR0P7S0tD+09PR/tLS0f7S0tH+09PR/tLS0v7S0tH+09PR/tLS0v7S0tL+z9DS/rjA + 1v5mh+T+KVft/R5F7P0cQer9G0Hq/BtA6vwbQOr5GkDp0hk+5zoQNsoBEy/UABY4xwAXPuMMGkDqiRpB + 6vIbQer6G0Hq/BxC6/0dQ+v9I0/s/VN45/2yvNj+z9DT/dPT0/7T09L+09PS/tPT0v7T09L+09PS/tPT + 0v7T09L+09PS/tPT0v7T09L+09PS/tPT0v7T09L+1NTS/tPT0v7T09L+1NTT/tPT0/7T09L+1NTT/tPT + 0/7T09P+09PT/s/Q0/6yvNj+U3jn/SNP7P0dQ+v9HELr/RtB6vwbQer6GkHq8hpA6okXPuMMFjjHABQy + 1QIaQOg2G0HqyBtB6vgbQev7HELr/BxC6/0fSev9NmDq/Zus3P7P0NX+1NPU/dTU1P7U1NP+1NTT/tTU + 0v7U09L+1NPR/tTU0v7V1NL+1dTT/tXV0/7V1dP+1dXT/tXV0/7V1dP+1dXU/tXV0/7V1dP+1dXU/tXV + 0/7V1dP+1dXU/tXU0/7U1NP+1NTU/tTT1P7P0NX+m6zc/jZg6v0fSev9HELr/RxC6/wbQev7G0Hq+BtB + 6sgaQOg2FDLVAhY93AwbQul2G0Lq6xtC6vobQ+v8HEPr/R1F6/0kUuz9cIzj/cjM1v7U09X+1dXV/dXV + 1P7V1dT+1dXT/tPT0f7HxsT+xMK//tHQzv7V1dP+1dXU/tXV1P7W1tT+1tbV/tXV1P7W1tX+1dXU/tXV + 1P7W1tX+1dXU/tXV1P7W1tX+1dXU/tXV1P7W1tX+1dXU/tXV1f7U09X+yMzW/nCM4/4kUuz9HUXr/RxD + 6v0bQ+v8G0Lq+htC6usbQul2Fj3cDBlC5iIbQ+qvG0Pr+BtD6/scROv8HETr/R5K7P09Z+r9qbXc/dPT + 1v7W1db+1tbV/dbW1f7W1tX+1dXU/sTDwP6Mh4L+gHpz/q2qpv7Rz87+1dXU/tbW1f7W1tX+19fV/tbW + 1f7X19b+19fV/tbW1f7X19b+19fV/tbW1f7X19b+19fV/tbW1f7X19b+1tbV/tbW1v7W1db+09PW/qm1 + 3P49Z+r9Hkrs/RxE6/0cROv8G0Pr+xtD6/gbQ+qvGULmIhpD6kUbROvZHETr+hxE7PwcRev8HUbs/SNR + 7P1riOX+x8zZ/dXW1/7X19f+19fW/dfX1v7X19b+1dXU/ru5tv50bWX+ZV1U/nZvaP6sqKT+0M/O/tbW + 1f7X19X+19fW/tjY1v7Y2Nf+2NjW/tjY1v7Y2Nf+2NjW/tjY1v7Y2Nf+2NjW/tjY1v7Y2Nf+2NfW/tfX + 1v7X19f+1dbX/sfM2f5riOX+I1Hs/R1G7P0cRev8HETr/BtE6/obROvZGkPqRRtE63UcRevyHEXr+xxF + 6/wcRuz9Hknr/TRe6v6bq9/+1NXZ/djY2P7Y2Nf+2NjX/djY1/7Y2Nf+2NfW/s7Ny/6alpH+bGVd/mNc + U/5xamT+p6Sg/tHQzv7Y19b+2NjX/tjY1/7Y2Nf+2dnY/tnZ2P7Y2Nf+2dnY/tnZ2P7Y2Nf+2dnY/tnZ + 2P7Y2Nf+2dnY/tjY2P7Y2Nf+2NjY/tTV2f6bq9/+NF7q/h5J6/0cRuv9HEXr/BtF6/scRevyG0TrdRxF + 66QdRuz7HUbs/B1G7P0dR+z9H0vs/Ulu6f67w9z+19jZ/dnZ2f7Z2dj+2tnZ/drZ2P7Z2dj+2dnY/tjX + 1/7My8n+mZSP/mlhWf5jW1L+cmtk/q2qpv7U09H+2dnY/tnZ2P7Z2dj+2trZ/tra2f7Z2dj+2trZ/tra + 2f7Z2dj+2trZ/tra2f7Z2dj+2dnZ/tnZ2f7Z2dj+2dnZ/tfY2f67w9z+SW7o/h9L7P0dR+v9HUbr/RxG + 7PwcRuz7G0XrpBxH7ModR+z8HUfs/B1I7f0eSOz9IU7t/WKA5v7P0t3+2trb/dva2/7b29r+3Nzb/dzc + 2/7c3Nr+3Nza/tva2v7a2dj+zMvJ/paRi/5pYlr+ZFxU/nt0bf67urf+2NjX/tnZ2P7a2tn+29rZ/tva + 2v7b2tn+29ra/tva2v7a2tn+29ra/tva2v7a2tn+29ra/tra2f7a2dn+2tnZ/tjZ2v7O0dv+YYDm/iFN + 7P0dR+z9HUfs/RxG7PwcR+z8HEbsyRxQ7+geUu/8HlLv/B5U7/0fVe/9JVXu/YWd6/7m5+j+6eno/enp + 5/7r6uj+7Ovp/ezr6f7r6+j+6+vp/uvq6P7q6uf+5+bk/tjX1P6inpj+cmtj/nZvZ/63tLD+2trZ/tzb + 2f7f3t3+4N/e/t7e3P7e3dz+3d3b/tzc2v7d29r+3dza/tzc2v7c29r+3Nva/tzb2v7b2tn+2trZ/tnZ + 2v7X2Nv+e5Lk/iRP7P0dSev9HUjs/RxH7PwcR+z8HEfs5R9b8fkgXPH9H1zx/R9b8f0gW/D9KFju/pyv + 7f7u7u7+5OTh/dHPy/7i4N3+8PDt/fHx7v7x8e7+8vLv/vHx7v7y8u7+8fHu/u/v7P7i4t7+urey/rez + rv7OzMj+q6ml/quopf7a2db+7Ovp/uvq6f7q6uj+6Ojm/uXl5P7k4+L+4uHg/uDf3v7e3t3+3dzb/tzb + 2v7Ozcz+wL68/tHQ0P7Z2tz+jJ/j/iZR7P4eSuz9HUns/RxI7P0cSO39HEjs9CBc8f0iXvH9IF3x/R9c + 8f0gW/H9Klnu/qa37f7r6+r+r6un/X14cP6koJr+5eXh/fHx7v7y8u/+8vLv/vLy7/7y8u/+8vLv/vHx + 7/7x8e7+7Ovo/uXk4f6npJ/+YVtV/mFbVf6opaH+6enn/u/v7f7w8O7+8PDu/vDw7f7v7+3+7u7s/u3s + 6v7r6uj+5ubl/tnY1v6dmJP+enRt/qShnf7X19j+labi/idS7P4eS+z9Hknt/R1J7f0dSe39HEnt/CFd + 8v0kYPL9IV3y/R9c8v0hXPH9Kljv/qm47v7p6ej+oJyX/W9oYf6WkYv+4uHd/fHx7/7y8vD+8/Pw/vPz + 8P7y8vH+8vLw/vLy8P7x8e/+7+/r/ufm4v6jnpn+aGBZ/mhgWf6jn5r+29vh/tLT5P7k5er+7+/u/vDw + 7/7x8e/+8fHv/vHx7/7x8e/+8PDt/uHg3P6VkIr+b2hg/p2ZlP7e3t7+m6vl/ihS7P4fTOz9Hkrt/R1K + 7f0dS+39HUrt/CFd8vkmYfL9Il7y/SBc8v0hXPH9KFfv/qKy7v7v8O/+1tXT/bSxrf7Rzsv+7+/s/fPz + 8f7z8/H+9PTx/vPz8f7z8/H+8vLx/vLy8P7t7ev+2dfU/tXTz/7OzMj+oZyW/qGblv7S0M3+wsXi/nR6 + zP6Ijc7+t7rb/uHh6f7v8PD+8PHw/vHx8P7x8fD+8vLw/u7u7P7Rzsv+tLGt/tbV0/7u7u7+nq/t/ihV + 7f4fVe/9HlLv/R5P7/0eTe79HUvt9CFd8uooY/P9Il/z/CBd8v0gXPH9J1fv/ZSn7v7w8PH+8PHv/e/u + 7P7y8e/+8/Px/fPz8f7z8/L+9PTy/vT08f7z8/H+8vLw/uzr6f7Fwr3+k4yE/pSNhf7OzMf+5eTi/ufm + 4/7w8O7+5ufv/ru+5f6Lkdn+bHPO/nl+y/6oq9f+1dbm/uvr7v7w8PD+8vLx/vLy8f7x8e/+7+7s/vDx + 7/7v8PH+kqXu/idW7v0gW/H9H1rx/SFb8vwkXPH9H1Xw5iFd8s8pZPP8JGDz/CBd8v0gXPL9JVfv/XmS + 7v7s7fL+8vLx/fPz8f709PL+9PTy/fT08v709PL+9PTy/vT08f7z8/D+7Ovo/sG9t/6Hf3b+eXBm/od/ + df7KyML+8PDu/vLy8P709PH+9PTy/vHx8f7r7PD+1tfs/qSo4f51fNP+bHLK/pCU0f6+wN/+3t/q/u/v + 8P7z8/H+8/Px/vLy8f7r7fH+d5Du/iRX7/0gXPH9H1zy/SRg8vwpZPL8IV3yzyFd8qopZPP7J2Pz/CBd + 8v0gXPL9Iljw/Vx67v7e4vL+8vLy/fT08v709PP+9PTz/fT08/709PL+9PTy/vTz8f7u7ev+xcK9/od/ + dv53bWP+fXRr/rGrpf7p6OX+9PTx/vT08v709PP+9fXy/vT08v709PL+8/Py/vHx8v7h4u/+uLzn/oSK + 2P5qcM3+eoDN/qyv2v7g4ev+8/Py/vLy8v7d4fH+Wnnu/iJY8P0gXPH9IF3y/Sdj8/wpZPP7IVzyqiBc + 8XspZPP0K2bz+yFd8/wgXPL9IVrx/UBl7v6/yvH+8vLz/fT08/719fT+9fXz/fX18/719fP+9PTy/u3s + 6v7Gwr7+i4N7/nhuZP59dGr+q6af/uXk4f709PL+9fXz/vX18/719fT+9vb0/vX19P719fP+9fX0/vX1 + 8/7z8/P+8fLz/uTm8f7KzOv+n6Th/n6F1v67v+P+8PDy/vHx8/6+yfH+QGXt/iFa8f0gXPL9IV7z/Cxm + 8/spZPP0IFzxex9a8EkmYvPcMGnz+iJe8/wgXfL8IVzx/StY7v2Noe/+6uzz/fT09P719fT+9fX0/fX1 + 9P719fL+7ezr/sG9uP6Hf3b+eG9l/oB3b/6wq6X+5uTh/vPz8f719fT+9fX0/vb29f729vT+9vb1/vb2 + 9f729vT+9vb1/vb29f719fT+9fX1/vPz9P7y8vP+7/D0/ufo8v7s7fL+8/P0/uns8/6MoO/+Klju/SFc + 8f0gXfL8Il7z/DBp8/omYvPcH1rwSR5Y7SUjX/KzMWrz+Shj8/sgXfL8IFzy/SFW7/1Xde790tnz/fPz + 9P719fX+9vb0/fX19P7v7+3+xsO+/od/dv53bWP+fXRq/rCspf7o5uT+9PTz/vb29P729vX+9vb1/vf3 + 9f739/b+9/f2/vf39v739/X+9/f2/vf39v739/X+9vb2/vb29f729vX+9fX1/vX19f719fT+8/P0/tHY + 8/5WdO79IVbv/SBc8v0gXfL8KGPz+zFq8/kjX/KzHljtJR9W4w0fXPF6LWfz7TBq9PohXvP8IF3y/SBa + 8P0tV+39nKzx/e3v9P719fX+9vb0/e/u7P7Hw77+jIR7/nhuZP59dGv+rKeg/ufl4/719fP+9vb1/vf3 + 9f739/b+9/f2/vf39v74+Pf+9/f3/vf39/74+Pf+9/f3/vf39/74+Pf+9/f2/vf39v739/b+9/f2/vb2 + 9f719fX+7e/0/pur8f4tV+39IFrw/SBd8v0hXvP8MGr0+i1n8+0fXPF7H1jjDRxZ2wIeW+85JmHyyzZu + 9PkoY/P7IF3y/CBb8f0hVe/9TW3u/c7V9P7z9PX+8/Lx/cXCvv6If3f+eG9l/oF4b/6yrKb+5+bj/vb1 + 8/739/X+9/f2/vj49v74+Pf+9/f3/vf39/74+Pf+9/f3/vf39/74+Pf+9/f3/vf39/74+Pf+9/f3/vf3 + 9/739/b+9/f2/vf39v7z9PX+zdT0/k1s7f0hVe/9IFvx/SBd8vwoY/P7Nm70+SZh8sseW+85HFrbAh9W + 3wEbW+cOIV3xjjJr9PMzbPT6I1/z/CBc8v0gW/H9KFTu/X6S8P3j5vT+6unp/ZmSi/54bmT+fnVr/rKs + pv7q6Ob+9vb1/vf39/739/f++Pj3/vj49/74+Pj+9/f3/vf39/74+Pj++Pj3/vj4+P75+fj++Pj4/vj4 + +P75+fj++Pj4/vj49/739/f+9/f3/vX19v7k5/X+fZLw/ShU7v0gW/H9IFzy/SNf8/wzbPT6Mmr08yFd + 8Y4bW+cOH1bfASBc8AAbWdMCH13vPShk89U5cfT5K2b0/CFe8/wgXPL9IVnw/Tdc7f2aqfD+4OLt/aun + pP6Kgnr+sKul/ujn5f739vX++Pj3/vj4+P74+Pj++Pj4/vj4+P74+Pj+9/f2/vf39v74+Pj++Pj4/vj4 + +P75+fj++Pj4/vj4+P75+fj++Pj4/vj4+P74+Pf+9fb3/ujr9v6aqfH+N1zt/SFZ8P0gXPL9IV7z/Ctm + 9Pw5cPT5KWTz1iBd7z0bWdMCIFzwAB1PygAkXusAH1rnDSJe8X8xavTuOXH1+iZi8/wgXfL8IFzx/CJX + 8P07Xuz9navx/d7g6v7h4N/+7+7t/vf39v74+Pf++fn4/vn5+P75+fj++fn4/vn5+P739/f+7e3s/u3t + 7P739/f++fn4/vn5+P75+fj++fn4/vn5+P75+fj++fn4/vj5+P729/f+6ev3/p2s8v06Xu39IVfw/SBc + 8f0gXfL8JmLz/Dpx9foxavTuIl7xgB9a6A0kXusAHU/KACFd8gATX90AIFTXAyBa7iwkYPO0OHD19TZu + 9PsjX/P8IF3y/CBb8f0hVe/9PF7s/Z6s8f3n6fX+9vb3/vj5+P75+fj++fn4/vr6+f75+fn++fn5/vn5 + 9/7j4+H+op+c/qKfnP7j4+H++fn3/vn5+f75+fn++vr5/vr6+P75+fn++Pn4/vf3+P7n6vf+nqzy/Tte + 7f0hVe/9H1vx/SBd8v0jX/P8Nm70+zlw9fUkYPK0IFrtLCBU1wMTX90AIV3yAAAAAAAlXusAHljpAB1V + 6QcgW/FOKWTzzj519fcybPT7I1/z/CFc8vwgW/H9Ilfv/Tpd7f2FlvD91dv2/vLz+f74+Pn++fn5/vr6 + +f76+vn++vr5/vf39v7GxMH+b2lj/m9pY/7GxMH+9/f2/vr6+f76+vn++vr5/vn5+f74+Pn+8vP5/tXa + 9v6El/H9Ol3s/SJX7/0gW/H9IFzy/CNf8/wzbPT7PnX19ylk8s4gW/BOHVXnBx9Y6gAlXusAAAAAAAAA + AAAAAAAAIVHMABtQ1AEdWucLI13yYi1o891Ad/X5NG30+yNf8/wgXfL9IFzy/CFY8P0pU+39VXHu/ai0 + 9P3d4fj+8/T6/vj5+v75+fr++fn6/vn4+f7g397+oZyX/qGcl/7g397++fj5/vn5+v75+fr++Pn6/vL0 + +v7d4fj+qLT0/VRw7v0pVO39IVjw/SBc8v0gXfL9I1/z/DRt9PtBd/X5LWfz3SNd8WIdWucLG1DUASFR + zAAAAAAAAAAAAAAAAAAAAAAAH1fqAB5X6wAfU9YBI1nsESNe8XsvafTjQnj1+TVu9PskX/P8IF3y/CBc + 8v0gWvH9IlTv/TJY7f1je+/9n6zz/tHX9/7t7/n+9vf6/vj5+v73+Pn+7e3t/u3t7f73+Pn++Pn6/vb3 + +v7t7/n+0df3/p+s8/5je+/9Mljt/SFU7/0gW/H9IFzy/SBd8vwkX/P8NW70+0J49fkvafTjI17xeyNZ + 7BEfU9UBHlboAB9X6gAAAAAAAAAAAAAAAAAAAAAAAAAAABhIxAAbWugAHlHhAiFb7BsjX/F6MGnz3UN6 + 9vc7cvX6KGPz/CFe8/wgXfL8IFvy/SFZ8f0jVe79MFjt/Uxp7v1uhPD+kqHy/q+79f7Ayfb+yM/2/sjP + 9v7Ayfb+sLv1/pKh8v5uhPD+TGnu/TBY7f0jVe79IVnx/SBb8v0gXfL8IV7z/Chj8/w7cvX6Q3r29zBp + 890jX/F6IVvsGx5R4QIbWucAGEjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdRq0AIVntAB9T + 4wIjWegRI17xYixn889Ad/b1RHr2+jFr9PskYPP8IV3z/CBd8/0hXPL9IVzx/SJZ8f0kVu/9JlTu/StU + 7f0uVu39M1ft/TNX7f0uVu39K1Tt/SZU7v0kVu/9Ilnx/SFc8f0hXPL9IF3z/SFd8/wkYfP8MWv0+0R6 + 9vpAd/b1LGfzzyNe8WMiWOgSH1PjAiFZ7QAdRq0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAHEnVACFd8QAbSdcCHVvnCyBc8E8nYvO0OXH17Uh99vlBd/X6L2n0+yNf8/whXfP8IV3y/SFd + 8/0iXfL9Il3y/SJc8v0iW/L9Ilvy/SJb8v0iW/L9Ilzy/SJd8v0iXfL9IV3z/SFd8v0hXfP8I1/z/C9p + 9PtBd/X6SH32+Tlx9e0nYvO0IFzwTx1b5wsbSdcCIV3xABxJ1QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAHVTmABtJ1wAgWOkAHFTWARxX5gggW+4tJGDxgDBp89VAdvXySn/2+EN5 + 9vozbPT7J2Pz/CNf8/wiXvP9IV7z/SFd8/0hXfP9IV7z/SFe8v0hXfP9IV3y/SFe8/0iXvP9I1/z/Cdj + 8/wzbPT7Q3n2+kp/9/hAdvXyL2nz1iRg8YAgW+4tHFfmCBxU1gEgV+kAG0nXAB1U5gAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARMrwAGlrrAB9Z6gEiV9wDIVvmDSFe + 8T0kYPKNLWf0yz519u1Kf/b4S4D3+kR69vo8dPX8NG71/C9p9fwtZ/X8LGb1/Sxm9f0tZ/X8L2n0/DRu + 9fw8dPX8RHr2+kuA9/pKf/b4PnX27S1n9MskYPKOIF7xPSFb5g0iV9wDH1nqARpa6wARMrwAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEkWwABpZ + 7QAgXfIAH07BASNdzQIiXOkOH13wOSFf8nsoY/O0M2z03D519fRGfPb7S4D2/E2B9/xNgff8ToL3/U6C + 9/1Ngff8TYH3/EuA9vxGfPb7PnX29DNs9NwoY/OzIV/yex9d8DkhWukOI1zNAh9OwQEgXfIAGlntABJF + sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAwkgwAaR8UAHFfpACBd8gAfU9ABIV/eAyJb5w0gXO4mIV7xSSNf8nsmYfKqKWTzzyxn + 8+ouaPT5MGr0/DBq9PwuaPT4LGfz6Spk888mYfKqIl/yeyBe8EkgXu4lIlvkDSFf3gMfU9ABIF3yABtX + 6QAaR8UADCSDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/4AAAf/////+AAAAf/ + ////gAAAAf////8AAAAA/////gAAAAB////8AAAAAD////gAAAAAH///8AAAAAAP///gAAAAAAf//+AA + AAAAB///wAAAAAAD///AAAAAAAP//4AAAAAAAf//gAAAAAAB//8AAAAAAAD//wAAAAAAAP//AAAAAAAA + //8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAA + AAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA//8AAAAAAAD//wAAAAAAAP//AAAAAAAA + //8AAAAAAAD//wAAAAAAAP//AAAAAAAA//+AAAAAAAH//8AAAAAAA///wAAAAAAD///gAAAAAAf//+AA + AAAAB///8AAAAAAP///4AAAAAB////wAAAAAP////gAAAAB/////AAAAAP////+AAAAB/////+AAAAf/ + ////+AAAH////w== + + + + 371, 17 + + + 480, 17 + + + 769, 19 + + + 17, 56 + + + 240, 56 + + \ No newline at end of file diff --git a/App/MiteDesk.WinForms/MiteDesk.WinForms.csproj b/App/MiteDesk.WinForms/MiteDesk.WinForms.csproj new file mode 100644 index 0000000..eb442ac --- /dev/null +++ b/App/MiteDesk.WinForms/MiteDesk.WinForms.csproj @@ -0,0 +1,343 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {352CDDB3-9A72-43FE-84B4-7021C6CBCAD4} + WinExe + Properties + SixtyNineDegrees.MiteDesk.WinForms + mite.desk + v3.5 + 512 + D6A9395B773CD057B51D8A60BAF73F15E7B078BE + WinMite.WinForms_TemporaryKey.pfx + true + false + false + true + SixtyNineDegrees.MiteDesk.WinForms.Program + false + + + mite.desk.ico + + + 3.5 + + C:\Temp\WinMiteDeploy\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 2 + 0.9.0.%2a + false + true + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AllRules.ruleset + + + + False + ..\..\Library\Shared\StructureMap\StructureMap.dll + + + + 3.5 + + + + 3.5 + + + + + + + + + Form + + + Activities.cs + + + + Form + + + Customers.cs + + + Form + + + DebugConsole.cs + + + True + True + ActivitiesLabels.resx + + + True + True + CustomersLabels.resx + + + True + True + DebugConsoleLabels.resx + + + True + True + FeedbackLabels.resx + + + True + True + MainLabels.resx + + + True + True + ProjectsLabels.resx + + + True + True + SettingsLabels.resx + + + True + True + UpdateCheckLabels.resx + + + Form + + + LoginHelp.cs + + + True + True + Resources.resx + + + + + Form + + + Main.cs + + + + Form + + + Projects.cs + + + + Activities.cs + + + Customers.cs + + + DebugConsole.cs + + + + ResXFileCodeGenerator + ActivitiesLabels.Designer.cs + + + + ResXFileCodeGenerator + CustomersLabels.Designer.cs + + + + ResXFileCodeGenerator + DebugConsoleLabels.Designer.cs + + + + ResXFileCodeGenerator + FeedbackLabels.Designer.cs + + + + ResXFileCodeGenerator + MainLabels.Designer.cs + + + + ResXFileCodeGenerator + ProjectsLabels.Designer.cs + + + + ResXFileCodeGenerator + SettingsLabels.Designer.cs + + + + ResXFileCodeGenerator + UpdateCheckLabels.Designer.cs + + + LoginHelp.cs + + + Main.cs + + + Projects.cs + + + ResXFileCodeGenerator + Designer + Resources.Designer.cs + + + Settings.cs + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + Form + + + Settings.cs + + + + + {AD8740D8-14A7-4A31-8F96-7DF850D3D957} + MiteDesk.Core.Data + + + {733C8DE9-36B5-4C9F-9701-FDD19B64B966} + MiteDesk.Core.Infrastructure + + + {AE69F9FC-F9D4-41D8-9B00-1261F492FB89} + MiteDesk.Core.Model + + + {B7B67B5C-47F1-4E3D-8759-EA8E31AC951E} + MiteDesk.Core.Services + + + {1B74B3CE-8969-4157-8B86-AEDA27494109} + MiteDesk.Tools.Connector + + + + + + + + + + + + + + + + + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 2.0 %28x86%29 + false + + + False + .NET Framework 3.0 %28x86%29 + false + + + False + .NET Framework 3.5 + true + + + False + .NET Framework 3.5 SP1 + false + + + False + Windows Installer 3.1 + true + + + + + + + + + + + + \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Program.cs b/App/MiteDesk.WinForms/Program.cs new file mode 100644 index 0000000..b878527 --- /dev/null +++ b/App/MiteDesk.WinForms/Program.cs @@ -0,0 +1,138 @@ +using System; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Threading; +using System.Windows.Forms; +using SixtyNineDegrees.MiteDesk.Tools.Connector; + +namespace SixtyNineDegrees.MiteDesk.WinForms +{ + + static class Program + { + + private static Main MainForm; + + [STAThread] + static void Main() + { + + if (!SingleInstance.Start()) + { + SingleInstance.ShowFirstInstance(); + return; + } + + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Bootstrapper.Initialize(); + AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; + Application.ThreadException += Application_ThreadException; + Application.ApplicationExit += Application_ApplicationExit; + MainForm = new Main(); + try + { + MainForm.Initialize(); + } + catch(MiteConnectorException e) + { + HandleException(e); + } + Application.Run(MainForm); + + } + + static void Application_ApplicationExit(object sender, EventArgs e) + { + SingleInstance.Stop(); + } + + private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) + { + HandleException((Exception)e.ExceptionObject); + } + + static void Application_ThreadException(object sender,ThreadExceptionEventArgs e) + { + HandleException(e.Exception); + } + + private static void HandleException(Exception e) + { + if (e is MiteConnectorException) + { + MainForm.NotifyAboutNetworkErrorAndDisableForm(e as MiteConnectorException); + } + else + { + new DebugConsole(e).ShowDialog(); + } + } + + } + + static public class WinApi + { + [DllImport("user32")] + public static extern int RegisterWindowMessage(string message); + + public static int RegisterWindowMessage(string format, params object[] args) + { + string message = String.Format(format, args); + return RegisterWindowMessage(message); + } + + public const int HWND_BROADCAST = 0xffff; + public const int SW_SHOWNORMAL = 1; + + [DllImport("user32.dll")] + public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam); + + [DllImport("User32.dll")] + public static extern int ShowWindow(IntPtr hWnd, int swCommand); + + } + + static public class ProgramInfo + { + static public string AssemblyGuid + { + get + { + object[] attributes = Assembly.GetEntryAssembly().GetCustomAttributes(typeof(GuidAttribute), false); + if (attributes.Length == 0) + { + return String.Empty; + } + return ((GuidAttribute)attributes[0]).Value; + } + } + } + + static public class SingleInstance + { + + public static readonly int WM_SHOWFIRSTINSTANCE = WinApi.RegisterWindowMessage("WM_SHOWFIRSTINSTANCE|{0}", ProgramInfo.AssemblyGuid); + static Mutex mutex; + + static public bool Start() + { + bool onlyInstance; + string mutexName = String.Format("Local\\{0}", ProgramInfo.AssemblyGuid); + mutex = new Mutex(true, mutexName, out onlyInstance); + return onlyInstance; + } + + static public void ShowFirstInstance() + { + WinApi.PostMessage((IntPtr)WinApi.HWND_BROADCAST, WM_SHOWFIRSTINSTANCE, IntPtr.Zero, IntPtr.Zero); + } + + static public void Stop() + { + mutex.ReleaseMutex(); + } + + } + +} diff --git a/App/MiteDesk.WinForms/Projects.Designer.cs b/App/MiteDesk.WinForms/Projects.Designer.cs new file mode 100644 index 0000000..c3a6cc3 --- /dev/null +++ b/App/MiteDesk.WinForms/Projects.Designer.cs @@ -0,0 +1,360 @@ +namespace SixtyNineDegrees.MiteDesk.WinForms +{ + partial class Projects + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.TabCustomers = new System.Windows.Forms.TabControl(); + this.ActiveProjectsTabPage = new System.Windows.Forms.TabPage(); + this.ActiveProjectsTree = new System.Windows.Forms.TreeView(); + this.TreeContextMenu = new System.Windows.Forms.ContextMenuStrip(this.components); + this.DeleteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.ArchivedCustomersTabPage = new System.Windows.Forms.TabPage(); + this.ArchivedProjectsTree = new System.Windows.Forms.TreeView(); + this.FormGroup = new System.Windows.Forms.GroupBox(); + this.BtnOpenCustomers = new System.Windows.Forms.Button(); + this.ListBudgetType = new System.Windows.Forms.ComboBox(); + this.BoxBudget = new System.Windows.Forms.TextBox(); + this.label4 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.ListCustomers = new System.Windows.Forms.ComboBox(); + this.CheckBoxArchived = new System.Windows.Forms.CheckBox(); + this.label2 = new System.Windows.Forms.Label(); + this.BoxNote = new System.Windows.Forms.TextBox(); + this.BoxName = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.BtnNewProject = new System.Windows.Forms.Button(); + this.BtnCancel = new System.Windows.Forms.Button(); + this.BtnSave = new System.Windows.Forms.Button(); + this.ErrorProvider = new System.Windows.Forms.ErrorProvider(this.components); + this.TreeDataBackgroundWorker = new System.ComponentModel.BackgroundWorker(); + this.TabCustomers.SuspendLayout(); + this.ActiveProjectsTabPage.SuspendLayout(); + this.TreeContextMenu.SuspendLayout(); + this.ArchivedCustomersTabPage.SuspendLayout(); + this.FormGroup.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.ErrorProvider)).BeginInit(); + this.SuspendLayout(); + // + // TabCustomers + // + this.TabCustomers.Controls.Add(this.ActiveProjectsTabPage); + this.TabCustomers.Controls.Add(this.ArchivedCustomersTabPage); + this.TabCustomers.Location = new System.Drawing.Point(9, 7); + this.TabCustomers.Name = "TabCustomers"; + this.TabCustomers.SelectedIndex = 0; + this.TabCustomers.Size = new System.Drawing.Size(189, 277); + this.TabCustomers.TabIndex = 10; + // + // ActiveProjectsTabPage + // + this.ActiveProjectsTabPage.Controls.Add(this.ActiveProjectsTree); + this.ActiveProjectsTabPage.Location = new System.Drawing.Point(4, 22); + this.ActiveProjectsTabPage.Name = "ActiveProjectsTabPage"; + this.ActiveProjectsTabPage.Padding = new System.Windows.Forms.Padding(3); + this.ActiveProjectsTabPage.Size = new System.Drawing.Size(181, 251); + this.ActiveProjectsTabPage.TabIndex = 0; + this.ActiveProjectsTabPage.Text = "Aktive"; + this.ActiveProjectsTabPage.UseVisualStyleBackColor = true; + // + // ActiveProjectsTree + // + this.ActiveProjectsTree.ContextMenuStrip = this.TreeContextMenu; + this.ActiveProjectsTree.Dock = System.Windows.Forms.DockStyle.Fill; + this.ActiveProjectsTree.FullRowSelect = true; + this.ActiveProjectsTree.HideSelection = false; + this.ActiveProjectsTree.Location = new System.Drawing.Point(3, 3); + this.ActiveProjectsTree.Name = "ActiveProjectsTree"; + this.ActiveProjectsTree.ShowLines = false; + this.ActiveProjectsTree.ShowPlusMinus = false; + this.ActiveProjectsTree.ShowRootLines = false; + this.ActiveProjectsTree.Size = new System.Drawing.Size(175, 245); + this.ActiveProjectsTree.TabIndex = 2; + // + // TreeContextMenu + // + this.TreeContextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.DeleteToolStripMenuItem}); + this.TreeContextMenu.Name = "TreeContextMenu"; + this.TreeContextMenu.Size = new System.Drawing.Size(114, 26); + // + // DeleteToolStripMenuItem + // + this.DeleteToolStripMenuItem.Name = "DeleteToolStripMenuItem"; + this.DeleteToolStripMenuItem.Size = new System.Drawing.Size(113, 22); + this.DeleteToolStripMenuItem.Text = "Löschen"; + this.DeleteToolStripMenuItem.Click += new System.EventHandler(this.DeleteToolStripMenuItem_Click_1); + // + // ArchivedCustomersTabPage + // + this.ArchivedCustomersTabPage.Controls.Add(this.ArchivedProjectsTree); + this.ArchivedCustomersTabPage.Location = new System.Drawing.Point(4, 22); + this.ArchivedCustomersTabPage.Name = "ArchivedCustomersTabPage"; + this.ArchivedCustomersTabPage.Padding = new System.Windows.Forms.Padding(3); + this.ArchivedCustomersTabPage.Size = new System.Drawing.Size(181, 251); + this.ArchivedCustomersTabPage.TabIndex = 1; + this.ArchivedCustomersTabPage.Text = "Archivierte"; + this.ArchivedCustomersTabPage.UseVisualStyleBackColor = true; + // + // ArchivedProjectsTree + // + this.ArchivedProjectsTree.ContextMenuStrip = this.TreeContextMenu; + this.ArchivedProjectsTree.Dock = System.Windows.Forms.DockStyle.Fill; + this.ArchivedProjectsTree.FullRowSelect = true; + this.ArchivedProjectsTree.HideSelection = false; + this.ArchivedProjectsTree.Location = new System.Drawing.Point(3, 3); + this.ArchivedProjectsTree.Name = "ArchivedProjectsTree"; + this.ArchivedProjectsTree.ShowLines = false; + this.ArchivedProjectsTree.ShowPlusMinus = false; + this.ArchivedProjectsTree.ShowRootLines = false; + this.ArchivedProjectsTree.Size = new System.Drawing.Size(175, 245); + this.ArchivedProjectsTree.TabIndex = 1; + // + // FormGroup + // + this.FormGroup.Controls.Add(this.BtnOpenCustomers); + this.FormGroup.Controls.Add(this.ListBudgetType); + this.FormGroup.Controls.Add(this.BoxBudget); + this.FormGroup.Controls.Add(this.label4); + this.FormGroup.Controls.Add(this.label3); + this.FormGroup.Controls.Add(this.ListCustomers); + this.FormGroup.Controls.Add(this.CheckBoxArchived); + this.FormGroup.Controls.Add(this.label2); + this.FormGroup.Controls.Add(this.BoxNote); + this.FormGroup.Controls.Add(this.BoxName); + this.FormGroup.Controls.Add(this.label1); + this.FormGroup.Enabled = false; + this.FormGroup.Location = new System.Drawing.Point(215, 8); + this.FormGroup.Name = "FormGroup"; + this.FormGroup.Size = new System.Drawing.Size(313, 276); + this.FormGroup.TabIndex = 11; + this.FormGroup.TabStop = false; + // + // BtnOpenCustomers + // + this.BtnOpenCustomers.Location = new System.Drawing.Point(209, 174); + this.BtnOpenCustomers.Name = "BtnOpenCustomers"; + this.BtnOpenCustomers.Size = new System.Drawing.Size(77, 21); + this.BtnOpenCustomers.TabIndex = 9; + this.BtnOpenCustomers.Text = "Kunden ..."; + this.BtnOpenCustomers.UseVisualStyleBackColor = true; + this.BtnOpenCustomers.Click += new System.EventHandler(this.BtnOpenCustomers_Click); + // + // ListBudgetType + // + this.ListBudgetType.FormattingEnabled = true; + this.ListBudgetType.Location = new System.Drawing.Point(116, 215); + this.ListBudgetType.Name = "ListBudgetType"; + this.ListBudgetType.Size = new System.Drawing.Size(87, 21); + this.ListBudgetType.TabIndex = 7; + this.ListBudgetType.SelectedIndexChanged += new System.EventHandler(this.EnableBtnSave); + // + // BoxBudget + // + this.BoxBudget.Location = new System.Drawing.Point(10, 216); + this.BoxBudget.Name = "BoxBudget"; + this.BoxBudget.Size = new System.Drawing.Size(100, 20); + this.BoxBudget.TabIndex = 6; + this.BoxBudget.Text = "0:00"; + this.BoxBudget.TextChanged += new System.EventHandler(this.EnableBtnSave); + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(7, 199); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(41, 13); + this.label4.TabIndex = 8; + this.label4.Text = "Budget"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(7, 158); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(38, 13); + this.label3.TabIndex = 7; + this.label3.Text = "Kunde"; + // + // ListCustomers + // + this.ListCustomers.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.Suggest; + this.ListCustomers.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.ListItems; + this.ListCustomers.DisplayMember = "Text"; + this.ListCustomers.FormattingEnabled = true; + this.ListCustomers.Location = new System.Drawing.Point(10, 174); + this.ListCustomers.Name = "ListCustomers"; + this.ListCustomers.Size = new System.Drawing.Size(193, 21); + this.ListCustomers.TabIndex = 5; + this.ListCustomers.ValueMember = "Value"; + this.ListCustomers.SelectedIndexChanged += new System.EventHandler(this.EnableBtnSave); + // + // CheckBoxArchived + // + this.CheckBoxArchived.AutoSize = true; + this.CheckBoxArchived.Location = new System.Drawing.Point(10, 242); + this.CheckBoxArchived.Name = "CheckBoxArchived"; + this.CheckBoxArchived.Size = new System.Drawing.Size(70, 17); + this.CheckBoxArchived.TabIndex = 8; + this.CheckBoxArchived.Text = "Archiviert"; + this.CheckBoxArchived.UseVisualStyleBackColor = true; + this.CheckBoxArchived.CheckedChanged += new System.EventHandler(this.EnableBtnSave); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(7, 63); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(61, 13); + this.label2.TabIndex = 3; + this.label2.Text = "Bemerkung"; + // + // BoxNote + // + this.BoxNote.Location = new System.Drawing.Point(10, 79); + this.BoxNote.Multiline = true; + this.BoxNote.Name = "BoxNote"; + this.BoxNote.Size = new System.Drawing.Size(276, 75); + this.BoxNote.TabIndex = 4; + this.BoxNote.TextChanged += new System.EventHandler(this.EnableBtnSave); + // + // BoxName + // + this.BoxName.Location = new System.Drawing.Point(10, 37); + this.BoxName.Name = "BoxName"; + this.BoxName.Size = new System.Drawing.Size(276, 20); + this.BoxName.TabIndex = 3; + this.BoxName.TextChanged += new System.EventHandler(this.EnableBtnSave); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(7, 20); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(35, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Name"; + // + // BtnNewProject + // + this.BtnNewProject.Location = new System.Drawing.Point(10, 294); + this.BtnNewProject.Name = "BtnNewProject"; + this.BtnNewProject.Size = new System.Drawing.Size(87, 23); + this.BtnNewProject.TabIndex = 12; + this.BtnNewProject.Text = "Neues Projekt"; + this.BtnNewProject.UseVisualStyleBackColor = true; + this.BtnNewProject.Click += new System.EventHandler(this.BtnNewProject_Click); + // + // BtnCancel + // + this.BtnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.BtnCancel.Location = new System.Drawing.Point(445, 294); + this.BtnCancel.Name = "BtnCancel"; + this.BtnCancel.Size = new System.Drawing.Size(83, 23); + this.BtnCancel.TabIndex = 10; + this.BtnCancel.Text = "Schließen"; + this.BtnCancel.UseVisualStyleBackColor = true; + this.BtnCancel.Click += new System.EventHandler(this.BtnCancel_Click_1); + // + // BtnSave + // + this.BtnSave.Enabled = false; + this.BtnSave.Location = new System.Drawing.Point(356, 295); + this.BtnSave.Name = "BtnSave"; + this.BtnSave.Size = new System.Drawing.Size(83, 23); + this.BtnSave.TabIndex = 9; + this.BtnSave.Text = "Übernehmen"; + this.BtnSave.UseVisualStyleBackColor = true; + this.BtnSave.Click += new System.EventHandler(this.BtnSave_Click); + // + // ErrorProvider + // + this.ErrorProvider.BlinkStyle = System.Windows.Forms.ErrorBlinkStyle.NeverBlink; + this.ErrorProvider.ContainerControl = this; + // + // Projects + // + this.AcceptButton = this.BtnSave; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.BtnCancel; + this.ClientSize = new System.Drawing.Size(537, 330); + this.Controls.Add(this.TabCustomers); + this.Controls.Add(this.FormGroup); + this.Controls.Add(this.BtnNewProject); + this.Controls.Add(this.BtnCancel); + this.Controls.Add(this.BtnSave); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MaximumSize = new System.Drawing.Size(543, 355); + this.MinimizeBox = false; + this.MinimumSize = new System.Drawing.Size(543, 355); + this.Name = "Projects"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Projekte"; + this.TabCustomers.ResumeLayout(false); + this.ActiveProjectsTabPage.ResumeLayout(false); + this.TreeContextMenu.ResumeLayout(false); + this.ArchivedCustomersTabPage.ResumeLayout(false); + this.FormGroup.ResumeLayout(false); + this.FormGroup.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.ErrorProvider)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TabControl TabCustomers; + private System.Windows.Forms.TabPage ActiveProjectsTabPage; + private System.Windows.Forms.TreeView ActiveProjectsTree; + private System.Windows.Forms.TabPage ArchivedCustomersTabPage; + private System.Windows.Forms.TreeView ArchivedProjectsTree; + private System.Windows.Forms.GroupBox FormGroup; + private System.Windows.Forms.CheckBox CheckBoxArchived; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox BoxNote; + private System.Windows.Forms.TextBox BoxName; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Button BtnNewProject; + private System.Windows.Forms.Button BtnCancel; + private System.Windows.Forms.Button BtnSave; + private System.Windows.Forms.ContextMenuStrip TreeContextMenu; + private System.Windows.Forms.ToolStripMenuItem DeleteToolStripMenuItem; + private System.Windows.Forms.ErrorProvider ErrorProvider; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.ComboBox ListCustomers; + private System.Windows.Forms.ComboBox ListBudgetType; + private System.Windows.Forms.TextBox BoxBudget; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Button BtnOpenCustomers; + public System.ComponentModel.BackgroundWorker TreeDataBackgroundWorker; + } +} \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Projects.cs b/App/MiteDesk.WinForms/Projects.cs new file mode 100644 index 0000000..51d62d0 --- /dev/null +++ b/App/MiteDesk.WinForms/Projects.cs @@ -0,0 +1,345 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; +using SixtyNineDegrees.MiteDesk.Core.Model; +using SixtyNineDegrees.MiteDesk.Core.Services; +using SixtyNineDegrees.MiteDesk.WinForms.Localization; +using StructureMap; + +namespace SixtyNineDegrees.MiteDesk.WinForms +{ + public partial class Projects : Form + { + + #region Setup + + public Projects() + { + InitializeComponent(); + LocalizeForm(); + ProjectService = ObjectFactory.GetInstance(); + + TreeDataBackgroundWorker.DoWork += TreeDataBackgroundWorker_DoWork; + TreeDataBackgroundWorker.RunWorkerCompleted += TreeDataBackgroundWorker_RunWorkerCompleted; + Helper.StartBackgroundWorker(TreeDataBackgroundWorker, null); + + ActiveProjectsTree.NodeMouseClick += Tree_NodeMouseClick; + ArchivedProjectsTree.NodeMouseClick += Tree_NodeMouseClick; + + ListCustomers.TextUpdate += Helper.ValidateTextEnteredInComboBox; + PrepareFormForNewProject(); + } + + private IList ActiveProjects; + private IList ArchivedProjects; + private IList ActiveCustomers; + private IList ArchivedCustomers; + private readonly IProjectService ProjectService; + + private Project CurrentProject; + private bool CreateNewProject; + + #endregion + + #region Lokalisierung + + private void LocalizeForm() + { + Text = ProjectsLabels.FormTitle; + ActiveProjectsTabPage.Text = ProjectsLabels.TabActiveTitle; + ArchivedCustomersTabPage.Text = ProjectsLabels.TabArchivedTitle; + BtnNewProject.Text = ProjectsLabels.ButtonNewActivity; + BtnSave.Text = ProjectsLabels.ButtonApply; + BtnCancel.Text = ProjectsLabels.ButtonCancel; + label1.Text = ProjectsLabels.LabelName; + label2.Text = ProjectsLabels.LabelNote; + label3.Text = ProjectsLabels.LabelCustomer; + label4.Text = ProjectsLabels.LabelBudget; + BtnOpenCustomers.Text = ProjectsLabels.ButtonCustomers; + CheckBoxArchived.Text = ProjectsLabels.LabelArchived; + DeleteToolStripMenuItem.Text = ProjectsLabels.ButtonDelete; + ListBudgetType.Items.Add(ProjectsLabels.BudgetHours); + ListBudgetType.Items.Add(ProjectsLabels.BudgetEuro); + } + + #endregion + + #region Treeviews befüllen + + void TreeDataBackgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) + { + if (TreeDataBackgroundWorker.CancellationPending) + { + e.Cancel = true; + return; + } + ActiveProjects = ProjectService.GetAllActiveProjects(); + ArchivedProjects = ProjectService.GetAllArchivedProjects(); + + var customerService = ObjectFactory.GetInstance(); + ActiveCustomers = customerService.GetAllActiveCustomers(); + ArchivedCustomers = customerService.GetAllArchivedCustomers(); + + } + + void TreeDataBackgroundWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) + { + + if (ActiveProjects == null || ArchivedProjects == null || ActiveCustomers == null || e.Cancelled) + return; + + FillTree(ActiveProjectsTree, ActiveProjects); + FillTree(ArchivedProjectsTree, ArchivedProjects); + + ListItem selected = ListCustomers.SelectedItem != null ? (ListItem) ListCustomers.SelectedItem : null; + ListCustomers.Items.Clear(); + ListCustomers.Items.Add(new ListItem {Value = 0, Text = string.Empty}); + + foreach (var customer in ActiveCustomers) + { + ListCustomers.Items.Add(new ListItem {Value = customer.ID, Text = customer.Name}); + if (selected != null && selected.Value == customer.ID) + ListCustomers.SelectedItem = ListCustomers.Items[ListCustomers.Items.Count - 1]; + } + + foreach (var customer in ArchivedCustomers) + { + ListCustomers.Items.Add(new ListItem { Value = customer.ID, Text = customer.Name + " (" + ProjectsLabels.LabelArchived + ")"}); + if (selected != null && selected.Value == customer.ID) + ListCustomers.SelectedItem = ListCustomers.Items[ListCustomers.Items.Count - 1]; + } + + } + + void FillTree(TreeView tree, IList projects) + { + + tree.Nodes.Clear(); + + var customers = new Dictionary(); + + foreach (var project in projects) + { + if (!customers.ContainsKey(project.CustomerID)) + customers.Add(project.CustomerID, project.CustomerName); + } + + customers.OrderBy(c => c.Value).ToDictionary(c => c.Key, c => c.Value); + + foreach (var customer in customers) + { + foreach (var project in projects.Where(p => p.CustomerID == customer.Key)) + { + tree.Nodes.Add(project.ID.ToString(), !string.IsNullOrEmpty(customer.Value) ? customer.Value + ": " + project.Name : project.Name); + } + } + + } + + #endregion + + #region Projekt darstellen + + private void LoadSingleProjectInForm(int projectID) + { + + FormGroup.Text = ProjectsLabels.GroupBoxTitleEditMode; + CreateNewProject = false; + CheckBoxArchived.Visible = true; + CurrentProject = ProjectService.GetProjectByID(projectID); + BoxName.Text = CurrentProject.Name; + BoxNote.Text = CurrentProject.Note; + CheckBoxArchived.Checked = CurrentProject.Archived; + + ListBudgetType.SelectedIndex = CurrentProject.BudgetType == "minutes" ? 0 : 1; + + foreach (var customerItem in ListCustomers.Items) + { + if (((ListItem)customerItem).Value == CurrentProject.CustomerID) + { + ListCustomers.SelectedItem = customerItem; + break; + } + } + + if (CurrentProject.Budget == 0) + { + BoxBudget.Text = CurrentProject.BudgetType == "minutes" ? "0:00" : "0,00"; + } + else + { + if(CurrentProject.BudgetType == "minutes") + { + BoxBudget.Text = Helper.GetFormattedTimeText(CurrentProject.Budget); + } + else + { + if (CurrentProject.Budget % 100 == 0) + { + BoxBudget.Text = (CurrentProject.Budget / 100).ToString(); + } + else + { + BoxBudget.Text = (CurrentProject.Budget / 100) + "," + + (CurrentProject.Budget % 100).ToString().PadRight(2, '0'); + } + + if (BoxBudget.Text == "0") + BoxBudget.Text = "0,00"; + else if (BoxBudget.Text.IndexOf(',') < 0) + BoxBudget.Text = BoxBudget.Text + ",00"; + } + } + + BtnSave.Enabled = false; + FormGroup.Enabled = true; + BoxName.Focus(); + } + + private void BtnOpenCustomers_Click(object sender, EventArgs e) + { + new Customers().ShowDialog(this); + } + + #endregion + + #region Formular zurücksetzen + + private void PrepareFormForNewProject() + { + ResetForm(); + FormGroup.Text = ProjectsLabels.GroupBoxTitleCreateMode; + FormGroup.Enabled = true; + CreateNewProject = true; + CheckBoxArchived.Visible = false; + BoxName.Focus(); + } + + private void ResetForm() + { + FormGroup.Enabled = false; + FormGroup.Text = null; + CheckBoxArchived.Visible = true; + CurrentProject = null; + CreateNewProject = false; + ActiveProjectsTree.SelectedNode = null; + ArchivedProjectsTree.SelectedNode = null; + ListCustomers.SelectedItem = null; + ListBudgetType.SelectedItem = null; + BoxName.Text = null; + BoxNote.Text = null; + BoxBudget.Text = "0:00"; + CheckBoxArchived.Checked = false; + BtnSave.Enabled = false; + ErrorProvider.Clear(); + } + + #endregion + + #region Projekt erstellen/aktualisieren + + private void BtnSave_Click(object sender, EventArgs e) + { + + Cursor = Cursors.WaitCursor; + + var project = CurrentProject ?? new Project(); + project.Name = BoxName.Text; + project.Note = BoxNote.Text; + project.Archived = CheckBoxArchived.Checked; + project.BudgetType = ListBudgetType.SelectedIndex == 0 ? "minutes" : "cents"; + project.CustomerID = ListCustomers.SelectedItem != null ? ((ListItem) ListCustomers.SelectedItem).Value : 0; + + var result = CurrentProject != null + ? ProjectService.UpdateProject(project, BoxBudget.Text) + : ProjectService.CreateProject(project, BoxBudget.Text); + + ErrorProvider.Clear(); + + if(result.Count == 0) + { + Helper.StartBackgroundWorker(TreeDataBackgroundWorker, null); + if (CreateNewProject) + PrepareFormForNewProject(); + else + BtnSave.Enabled = false; + Cursor = Cursors.Default; + return; + } + + if (result.ContainsKey("Name")) + ErrorProvider.SetError(BoxName, result["Name"]); + + Cursor = Cursors.Default; + + } + + #endregion + + #region Projekt löschen + + private void DeleteProject(int projectID) + { + + var timeEntries = ObjectFactory.GetInstance().GetTimeEntriesByProjectID(projectID); + + if (timeEntries.Count > 0) + { + MessageBox.Show(ProjectsLabels.MsgBoxDeleteErrorText, ProjectsLabels.MsgBoxDeleteErrorTitle, MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + + var question = MessageBox.Show(ProjectsLabels.MsgBoxDeleteQuestionText, ProjectsLabels.MsgBoxDeleteQuestionTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Question); + if (question == DialogResult.Yes) + { + ProjectService.DeleteProject(projectID); + ResetForm(); + Helper.StartBackgroundWorker(TreeDataBackgroundWorker, null); + } + } + + #endregion + + #region Event-Handler + + private void BtnNewProject_Click(object sender, EventArgs e) + { + PrepareFormForNewProject(); + } + + private void BtnCancel_Click_1(object sender, EventArgs e) + { + Close(); + } + + void Tree_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) + { + e.Node.TreeView.SelectedNode = e.Node; + LoadSingleProjectInForm(int.Parse(e.Node.Name)); + } + + private void DeleteToolStripMenuItem_Click_1(object sender, EventArgs e) + { + if (ActiveProjectsTabPage.Visible) + DeleteProject(int.Parse(ActiveProjectsTree.SelectedNode.Name)); + else + DeleteProject(int.Parse(ArchivedProjectsTree.SelectedNode.Name)); + } + + protected override void OnClosed(EventArgs e) + { + Helper.StartBackgroundWorker(((Main)Owner).InitializationBackgroundWorker, null); + base.OnClosed(e); + } + + private void EnableBtnSave(object sender, EventArgs e) + { + if (FormGroup.Enabled) + BtnSave.Enabled = true; + } + + #endregion + + } +} diff --git a/App/MiteDesk.WinForms/Projects.resx b/App/MiteDesk.WinForms/Projects.resx new file mode 100644 index 0000000..42ba458 --- /dev/null +++ b/App/MiteDesk.WinForms/Projects.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 475, 17 + + + 624, 17 + + + 747, 17 + + \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Properties/AssemblyInfo.cs b/App/MiteDesk.WinForms/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..09a1d89 --- /dev/null +++ b/App/MiteDesk.WinForms/Properties/AssemblyInfo.cs @@ -0,0 +1,16 @@ +using System.Reflection; +using System.Runtime.InteropServices; +using System.Resources; + +[assembly: AssemblyTitle("mite.desk")] +[assembly: AssemblyDescription("mite.desk")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("mite.desk")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] +[assembly: Guid("698529cc-b794-49f9-ac63-6d7fe9ae3f24")] + +[assembly: AssemblyVersion("1.3.0")] +[assembly: AssemblyFileVersion("1.3.0")] +[assembly: NeutralResourcesLanguageAttribute("de-DE")] diff --git a/App/MiteDesk.WinForms/Properties/Resources.Designer.cs b/App/MiteDesk.WinForms/Properties/Resources.Designer.cs new file mode 100644 index 0000000..c3d80b7 --- /dev/null +++ b/App/MiteDesk.WinForms/Properties/Resources.Designer.cs @@ -0,0 +1,231 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30128.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace SixtyNineDegrees.MiteDesk.WinForms.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SixtyNineDegrees.MiteDesk.WinForms.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + internal static System.Drawing.Bitmap Connected { + get { + object obj = ResourceManager.GetObject("Connected", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap Disconnected { + get { + object obj = ResourceManager.GetObject("Disconnected", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap faviconico { + get { + object obj = ResourceManager.GetObject("faviconico", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap hilfe { + get { + object obj = ResourceManager.GetObject("hilfe", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap Locked { + get { + object obj = ResourceManager.GetObject("Locked", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap loginhelp { + get { + object obj = ResourceManager.GetObject("loginhelp", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap logo { + get { + object obj = ResourceManager.GetObject("logo", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap mini_clock_animated_15x15 { + get { + object obj = ResourceManager.GetObject("mini_clock_animated_15x15", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap mite_desk_icon_16x16_1 { + get { + object obj = ResourceManager.GetObject("mite_desk_icon_16x16_1", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap mite_desk_icon_16x16_2 { + get { + object obj = ResourceManager.GetObject("mite_desk_icon_16x16_2", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap mite_desk_icon_16x16_3 { + get { + object obj = ResourceManager.GetObject("mite_desk_icon_16x16_3", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap mite_desk_icon_16x16_4 { + get { + object obj = ResourceManager.GetObject("mite_desk_icon_16x16_4", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap mite_desk_icon_16x16_5 { + get { + object obj = ResourceManager.GetObject("mite_desk_icon_16x16_5", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap mite_desk_icon_16x16_6 { + get { + object obj = ResourceManager.GetObject("mite_desk_icon_16x16_6", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap mite_desk_icon_16x16_7 { + get { + object obj = ResourceManager.GetObject("mite_desk_icon_16x16_7", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap mite_desk_icon_16x16_8 { + get { + object obj = ResourceManager.GetObject("mite_desk_icon_16x16_8", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Icon mitedesk_clock_1 { + get { + object obj = ResourceManager.GetObject("mitedesk_clock_1", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + internal static System.Drawing.Icon mitedesk_clock_2 { + get { + object obj = ResourceManager.GetObject("mitedesk_clock_2", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + internal static System.Drawing.Icon mitedesk_clock_3 { + get { + object obj = ResourceManager.GetObject("mitedesk_clock_3", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + internal static System.Drawing.Icon mitedesk_clock_4 { + get { + object obj = ResourceManager.GetObject("mitedesk_clock_4", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + internal static System.Drawing.Icon mitedesk_clock_5 { + get { + object obj = ResourceManager.GetObject("mitedesk_clock_5", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + internal static System.Drawing.Icon mitedesk_clock_6 { + get { + object obj = ResourceManager.GetObject("mitedesk_clock_6", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + internal static System.Drawing.Icon mitedesk_clock_7 { + get { + object obj = ResourceManager.GetObject("mitedesk_clock_7", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + internal static System.Drawing.Icon mitedesk_clock_8 { + get { + object obj = ResourceManager.GetObject("mitedesk_clock_8", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + } +} diff --git a/App/MiteDesk.WinForms/Properties/Resources.resx b/App/MiteDesk.WinForms/Properties/Resources.resx new file mode 100644 index 0000000..361cb6b --- /dev/null +++ b/App/MiteDesk.WinForms/Properties/Resources.resx @@ -0,0 +1,193 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\mitedesk_clock_5.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\mite.desk_icon_16x16_2.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\logo.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\mite.desk_icon_16x16_8.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\favicon.ico.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\connected.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\mite.desk_icon_16x16_1.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\mite.desk_icon_16x16_7.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\mitedesk_clock_3.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\mini_clock_animated_15x15.gif;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\mitedesk_clock_6.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\mite.desk_icon_16x16_6.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\mitedesk_clock_7.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\mitedesk_clock_1.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\mite.desk_icon_16x16_5.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\loginhelp.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\mitedesk_clock_4.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\mite.desk_icon_16x16_4.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\mitedesk_clock_8.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\disconnected.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\mite.desk_icon_16x16_3.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\mitedesk_clock_2.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\hilfe.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Locked.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Properties/Settings.Designer.cs b/App/MiteDesk.WinForms/Properties/Settings.Designer.cs new file mode 100644 index 0000000..108fb03 --- /dev/null +++ b/App/MiteDesk.WinForms/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30128.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace SixtyNineDegrees.MiteDesk.WinForms.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/App/MiteDesk.WinForms/Properties/Settings.settings b/App/MiteDesk.WinForms/Properties/Settings.settings new file mode 100644 index 0000000..8e615f2 --- /dev/null +++ b/App/MiteDesk.WinForms/Properties/Settings.settings @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Resources/Locked.png b/App/MiteDesk.WinForms/Resources/Locked.png new file mode 100644 index 0000000..6eaba3c Binary files /dev/null and b/App/MiteDesk.WinForms/Resources/Locked.png differ diff --git a/App/MiteDesk.WinForms/Resources/Thumbs.db b/App/MiteDesk.WinForms/Resources/Thumbs.db new file mode 100644 index 0000000..357d69a Binary files /dev/null and b/App/MiteDesk.WinForms/Resources/Thumbs.db differ diff --git a/App/MiteDesk.WinForms/Resources/connected.png b/App/MiteDesk.WinForms/Resources/connected.png new file mode 100644 index 0000000..45a2f10 Binary files /dev/null and b/App/MiteDesk.WinForms/Resources/connected.png differ diff --git a/App/MiteDesk.WinForms/Resources/disconnected.png b/App/MiteDesk.WinForms/Resources/disconnected.png new file mode 100644 index 0000000..967d674 Binary files /dev/null and b/App/MiteDesk.WinForms/Resources/disconnected.png differ diff --git a/App/MiteDesk.WinForms/Resources/favicon.ico.png b/App/MiteDesk.WinForms/Resources/favicon.ico.png new file mode 100644 index 0000000..7ba63ef Binary files /dev/null and b/App/MiteDesk.WinForms/Resources/favicon.ico.png differ diff --git a/App/MiteDesk.WinForms/Resources/hilfe.png b/App/MiteDesk.WinForms/Resources/hilfe.png new file mode 100644 index 0000000..a724b0b Binary files /dev/null and b/App/MiteDesk.WinForms/Resources/hilfe.png differ diff --git a/App/MiteDesk.WinForms/Resources/loginhelp.png b/App/MiteDesk.WinForms/Resources/loginhelp.png new file mode 100644 index 0000000..2de252b Binary files /dev/null and b/App/MiteDesk.WinForms/Resources/loginhelp.png differ diff --git a/App/MiteDesk.WinForms/Resources/logo.png b/App/MiteDesk.WinForms/Resources/logo.png new file mode 100644 index 0000000..bea2e7c Binary files /dev/null and b/App/MiteDesk.WinForms/Resources/logo.png differ diff --git a/App/MiteDesk.WinForms/Resources/mini_clock_animated_15x15.gif b/App/MiteDesk.WinForms/Resources/mini_clock_animated_15x15.gif new file mode 100644 index 0000000..3f8b97e Binary files /dev/null and b/App/MiteDesk.WinForms/Resources/mini_clock_animated_15x15.gif differ diff --git a/App/MiteDesk.WinForms/Resources/mite.desk_icon_16x16_1.png b/App/MiteDesk.WinForms/Resources/mite.desk_icon_16x16_1.png new file mode 100644 index 0000000..52a51c4 Binary files /dev/null and b/App/MiteDesk.WinForms/Resources/mite.desk_icon_16x16_1.png differ diff --git a/App/MiteDesk.WinForms/Resources/mite.desk_icon_16x16_2.png b/App/MiteDesk.WinForms/Resources/mite.desk_icon_16x16_2.png new file mode 100644 index 0000000..ebac7eb Binary files /dev/null and b/App/MiteDesk.WinForms/Resources/mite.desk_icon_16x16_2.png differ diff --git a/App/MiteDesk.WinForms/Resources/mite.desk_icon_16x16_3.png b/App/MiteDesk.WinForms/Resources/mite.desk_icon_16x16_3.png new file mode 100644 index 0000000..59755d6 Binary files /dev/null and b/App/MiteDesk.WinForms/Resources/mite.desk_icon_16x16_3.png differ diff --git a/App/MiteDesk.WinForms/Resources/mite.desk_icon_16x16_4.png b/App/MiteDesk.WinForms/Resources/mite.desk_icon_16x16_4.png new file mode 100644 index 0000000..4922672 Binary files /dev/null and b/App/MiteDesk.WinForms/Resources/mite.desk_icon_16x16_4.png differ diff --git a/App/MiteDesk.WinForms/Resources/mite.desk_icon_16x16_5.png b/App/MiteDesk.WinForms/Resources/mite.desk_icon_16x16_5.png new file mode 100644 index 0000000..852e2e3 Binary files /dev/null and b/App/MiteDesk.WinForms/Resources/mite.desk_icon_16x16_5.png differ diff --git a/App/MiteDesk.WinForms/Resources/mite.desk_icon_16x16_6.png b/App/MiteDesk.WinForms/Resources/mite.desk_icon_16x16_6.png new file mode 100644 index 0000000..70c861f Binary files /dev/null and b/App/MiteDesk.WinForms/Resources/mite.desk_icon_16x16_6.png differ diff --git a/App/MiteDesk.WinForms/Resources/mite.desk_icon_16x16_7.png b/App/MiteDesk.WinForms/Resources/mite.desk_icon_16x16_7.png new file mode 100644 index 0000000..94cdc70 Binary files /dev/null and b/App/MiteDesk.WinForms/Resources/mite.desk_icon_16x16_7.png differ diff --git a/App/MiteDesk.WinForms/Resources/mite.desk_icon_16x16_8.png b/App/MiteDesk.WinForms/Resources/mite.desk_icon_16x16_8.png new file mode 100644 index 0000000..6280eff Binary files /dev/null and b/App/MiteDesk.WinForms/Resources/mite.desk_icon_16x16_8.png differ diff --git a/App/MiteDesk.WinForms/Resources/mitedesk_clock_1.ico b/App/MiteDesk.WinForms/Resources/mitedesk_clock_1.ico new file mode 100644 index 0000000..4f9743a Binary files /dev/null and b/App/MiteDesk.WinForms/Resources/mitedesk_clock_1.ico differ diff --git a/App/MiteDesk.WinForms/Resources/mitedesk_clock_2.ico b/App/MiteDesk.WinForms/Resources/mitedesk_clock_2.ico new file mode 100644 index 0000000..2059edb Binary files /dev/null and b/App/MiteDesk.WinForms/Resources/mitedesk_clock_2.ico differ diff --git a/App/MiteDesk.WinForms/Resources/mitedesk_clock_3.ico b/App/MiteDesk.WinForms/Resources/mitedesk_clock_3.ico new file mode 100644 index 0000000..b3fedcf Binary files /dev/null and b/App/MiteDesk.WinForms/Resources/mitedesk_clock_3.ico differ diff --git a/App/MiteDesk.WinForms/Resources/mitedesk_clock_4.ico b/App/MiteDesk.WinForms/Resources/mitedesk_clock_4.ico new file mode 100644 index 0000000..8e376ab Binary files /dev/null and b/App/MiteDesk.WinForms/Resources/mitedesk_clock_4.ico differ diff --git a/App/MiteDesk.WinForms/Resources/mitedesk_clock_5.ico b/App/MiteDesk.WinForms/Resources/mitedesk_clock_5.ico new file mode 100644 index 0000000..0cee071 Binary files /dev/null and b/App/MiteDesk.WinForms/Resources/mitedesk_clock_5.ico differ diff --git a/App/MiteDesk.WinForms/Resources/mitedesk_clock_6.ico b/App/MiteDesk.WinForms/Resources/mitedesk_clock_6.ico new file mode 100644 index 0000000..ff25fca Binary files /dev/null and b/App/MiteDesk.WinForms/Resources/mitedesk_clock_6.ico differ diff --git a/App/MiteDesk.WinForms/Resources/mitedesk_clock_7.ico b/App/MiteDesk.WinForms/Resources/mitedesk_clock_7.ico new file mode 100644 index 0000000..b715ebb Binary files /dev/null and b/App/MiteDesk.WinForms/Resources/mitedesk_clock_7.ico differ diff --git a/App/MiteDesk.WinForms/Resources/mitedesk_clock_8.ico b/App/MiteDesk.WinForms/Resources/mitedesk_clock_8.ico new file mode 100644 index 0000000..bf277ba Binary files /dev/null and b/App/MiteDesk.WinForms/Resources/mitedesk_clock_8.ico differ diff --git a/App/MiteDesk.WinForms/Settings.Designer.cs b/App/MiteDesk.WinForms/Settings.Designer.cs new file mode 100644 index 0000000..1071dbf --- /dev/null +++ b/App/MiteDesk.WinForms/Settings.Designer.cs @@ -0,0 +1,733 @@ +namespace SixtyNineDegrees.MiteDesk.WinForms +{ + partial class Settings + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.ButtonCancel = new System.Windows.Forms.Button(); + this.ButtonOK = new System.Windows.Forms.Button(); + this.CheckConnectionButton = new System.Windows.Forms.Button(); + this.ErrorProvider = new System.Windows.Forms.ErrorProvider(this.components); + this.checkBox1 = new System.Windows.Forms.CheckBox(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.label8 = new System.Windows.Forms.Label(); + this.ExtendedTab = new System.Windows.Forms.TabPage(); + this.groupBox5 = new System.Windows.Forms.GroupBox(); + this.MinimizeByClosing = new System.Windows.Forms.CheckBox(); + this.StopStopwatchByClosing = new System.Windows.Forms.CheckBox(); + this.AskForStoppingStopwatchByClosing = new System.Windows.Forms.CheckBox(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.EnableAutostart = new System.Windows.Forms.CheckBox(); + this.StartMinimized = new System.Windows.Forms.CheckBox(); + this.groupBox8 = new System.Windows.Forms.GroupBox(); + this.SortDescending = new System.Windows.Forms.CheckBox(); + this.LanguageTab = new System.Windows.Forms.TabPage(); + this.groupBox6 = new System.Windows.Forms.GroupBox(); + this.ListLanguages = new System.Windows.Forms.ComboBox(); + this.NetworkTab = new System.Windows.Forms.TabPage(); + this.groupBox7 = new System.Windows.Forms.GroupBox(); + this.label9 = new System.Windows.Forms.Label(); + this.ProxyServer = new System.Windows.Forms.TextBox(); + this.label10 = new System.Windows.Forms.Label(); + this.ProxyPort = new System.Windows.Forms.NumericUpDown(); + this.BoxUseProxyServer = new System.Windows.Forms.CheckBox(); + this.LblProxyPassword = new System.Windows.Forms.Label(); + this.LblProxyUser = new System.Windows.Forms.Label(); + this.ProxyPassword = new System.Windows.Forms.TextBox(); + this.ProxyUser = new System.Windows.Forms.TextBox(); + this.checkBox2 = new System.Windows.Forms.CheckBox(); + this.AccountTab = new System.Windows.Forms.TabPage(); + this.GroupBoxAuthenticateByAPIKey = new System.Windows.Forms.GroupBox(); + this.label4 = new System.Windows.Forms.Label(); + this.APIKey = new System.Windows.Forms.TextBox(); + this.GroupBoxAuthenticateByEmailAndPassword = new System.Windows.Forms.GroupBox(); + this.label3 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.Password = new System.Windows.Forms.TextBox(); + this.Email = new System.Windows.Forms.TextBox(); + this.CheckBoxShowPasswordInClearText = new System.Windows.Forms.CheckBox(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.AccountName = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.AuthenticateByEmailAndPassword = new System.Windows.Forms.RadioButton(); + this.AuthenticateByAPIKey = new System.Windows.Forms.RadioButton(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.label11 = new System.Windows.Forms.Label(); + this.label12 = new System.Windows.Forms.Label(); + this.TabContainer = new System.Windows.Forms.TabControl(); + ((System.ComponentModel.ISupportInitialize)(this.ErrorProvider)).BeginInit(); + this.ExtendedTab.SuspendLayout(); + this.groupBox5.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.groupBox8.SuspendLayout(); + this.LanguageTab.SuspendLayout(); + this.groupBox6.SuspendLayout(); + this.NetworkTab.SuspendLayout(); + this.groupBox7.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.ProxyPort)).BeginInit(); + this.AccountTab.SuspendLayout(); + this.GroupBoxAuthenticateByAPIKey.SuspendLayout(); + this.GroupBoxAuthenticateByEmailAndPassword.SuspendLayout(); + this.groupBox1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.TabContainer.SuspendLayout(); + this.SuspendLayout(); + // + // ButtonCancel + // + this.ButtonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.ButtonCancel.Location = new System.Drawing.Point(365, 324); + this.ButtonCancel.Name = "ButtonCancel"; + this.ButtonCancel.Size = new System.Drawing.Size(75, 23); + this.ButtonCancel.TabIndex = 11; + this.ButtonCancel.Text = "Abbrechen"; + this.ButtonCancel.UseVisualStyleBackColor = true; + this.ButtonCancel.Click += new System.EventHandler(this.ButtonCancel_Click); + // + // ButtonOK + // + this.ButtonOK.Location = new System.Drawing.Point(283, 324); + this.ButtonOK.Name = "ButtonOK"; + this.ButtonOK.Size = new System.Drawing.Size(75, 23); + this.ButtonOK.TabIndex = 10; + this.ButtonOK.Text = "OK"; + this.ButtonOK.UseVisualStyleBackColor = true; + this.ButtonOK.Click += new System.EventHandler(this.ButtonOK_Click); + // + // CheckConnectionButton + // + this.CheckConnectionButton.Location = new System.Drawing.Point(9, 324); + this.CheckConnectionButton.Name = "CheckConnectionButton"; + this.CheckConnectionButton.Size = new System.Drawing.Size(107, 23); + this.CheckConnectionButton.TabIndex = 9; + this.CheckConnectionButton.Text = "Verbindung testen"; + this.CheckConnectionButton.UseVisualStyleBackColor = true; + this.CheckConnectionButton.Click += new System.EventHandler(this.button1_Click); + // + // ErrorProvider + // + this.ErrorProvider.BlinkStyle = System.Windows.Forms.ErrorBlinkStyle.NeverBlink; + this.ErrorProvider.ContainerControl = this; + // + // checkBox1 + // + this.checkBox1.AutoSize = true; + this.checkBox1.Location = new System.Drawing.Point(10, 23); + this.checkBox1.Name = "checkBox1"; + this.checkBox1.Size = new System.Drawing.Size(297, 17); + this.checkBox1.TabIndex = 8; + this.checkBox1.Text = "Sichere Verbindung mit SSL-Verschlüsselung verwenden."; + this.checkBox1.UseVisualStyleBackColor = true; + // + // textBox1 + // + this.textBox1.Location = new System.Drawing.Point(111, 26); + this.textBox1.Name = "textBox1"; + this.textBox1.Size = new System.Drawing.Size(276, 20); + this.textBox1.TabIndex = 7; + // + // label8 + // + this.label8.AutoSize = true; + this.label8.Location = new System.Drawing.Point(7, 29); + this.label8.Name = "label8"; + this.label8.Size = new System.Drawing.Size(52, 13); + this.label8.TabIndex = 7; + this.label8.Text = "Schlüssel"; + // + // ExtendedTab + // + this.ExtendedTab.Controls.Add(this.groupBox8); + this.ExtendedTab.Controls.Add(this.groupBox2); + this.ExtendedTab.Controls.Add(this.groupBox5); + this.ExtendedTab.Location = new System.Drawing.Point(4, 22); + this.ExtendedTab.Name = "ExtendedTab"; + this.ExtendedTab.Padding = new System.Windows.Forms.Padding(3); + this.ExtendedTab.Size = new System.Drawing.Size(423, 279); + this.ExtendedTab.TabIndex = 2; + this.ExtendedTab.Text = "Erweitert"; + this.ExtendedTab.UseVisualStyleBackColor = true; + // + // groupBox5 + // + this.groupBox5.Controls.Add(this.AskForStoppingStopwatchByClosing); + this.groupBox5.Controls.Add(this.StopStopwatchByClosing); + this.groupBox5.Controls.Add(this.MinimizeByClosing); + this.groupBox5.Location = new System.Drawing.Point(8, 83); + this.groupBox5.Name = "groupBox5"; + this.groupBox5.Size = new System.Drawing.Size(406, 96); + this.groupBox5.TabIndex = 10; + this.groupBox5.TabStop = false; + this.groupBox5.Text = "Beenden"; + // + // MinimizeByClosing + // + this.MinimizeByClosing.AutoSize = true; + this.MinimizeByClosing.Location = new System.Drawing.Point(10, 23); + this.MinimizeByClosing.Name = "MinimizeByClosing"; + this.MinimizeByClosing.Size = new System.Drawing.Size(376, 17); + this.MinimizeByClosing.TabIndex = 4; + this.MinimizeByClosing.Text = "mite.desk beim Schließen des Fensters nicht beenden, sondern minimieren"; + this.MinimizeByClosing.UseVisualStyleBackColor = true; + // + // StopStopwatchByClosing + // + this.StopStopwatchByClosing.AutoSize = true; + this.StopStopwatchByClosing.Location = new System.Drawing.Point(10, 47); + this.StopStopwatchByClosing.Name = "StopStopwatchByClosing"; + this.StopStopwatchByClosing.Size = new System.Drawing.Size(262, 17); + this.StopStopwatchByClosing.TabIndex = 5; + this.StopStopwatchByClosing.Text = "Beim Beenden die Stoppuhr automatisch anhalten"; + this.StopStopwatchByClosing.UseVisualStyleBackColor = true; + this.StopStopwatchByClosing.CheckedChanged += new System.EventHandler(this.StopStopwatchByClosing_CheckedChanged); + // + // AskForStoppingStopwatchByClosing + // + this.AskForStoppingStopwatchByClosing.AutoSize = true; + this.AskForStoppingStopwatchByClosing.Location = new System.Drawing.Point(10, 71); + this.AskForStoppingStopwatchByClosing.Name = "AskForStoppingStopwatchByClosing"; + this.AskForStoppingStopwatchByClosing.Size = new System.Drawing.Size(337, 17); + this.AskForStoppingStopwatchByClosing.TabIndex = 6; + this.AskForStoppingStopwatchByClosing.Text = "Vor dem Beenden fragen, ob die Stoppuhr angehalten werden soll"; + this.AskForStoppingStopwatchByClosing.UseVisualStyleBackColor = true; + this.AskForStoppingStopwatchByClosing.CheckedChanged += new System.EventHandler(this.AskForStoppingStopwatchByClosing_CheckedChanged); + // + // groupBox2 + // + this.groupBox2.Controls.Add(this.StartMinimized); + this.groupBox2.Controls.Add(this.EnableAutostart); + this.groupBox2.Location = new System.Drawing.Point(8, 6); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(406, 71); + this.groupBox2.TabIndex = 11; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "Start"; + // + // EnableAutostart + // + this.EnableAutostart.AutoSize = true; + this.EnableAutostart.Location = new System.Drawing.Point(10, 23); + this.EnableAutostart.Name = "EnableAutostart"; + this.EnableAutostart.Size = new System.Drawing.Size(355, 17); + this.EnableAutostart.TabIndex = 1; + this.EnableAutostart.Text = "mite.desk bei der Windows-Anmeldung automatisch starten (Autostart)"; + this.EnableAutostart.UseVisualStyleBackColor = true; + // + // StartMinimized + // + this.StartMinimized.AutoSize = true; + this.StartMinimized.Location = new System.Drawing.Point(10, 46); + this.StartMinimized.Name = "StartMinimized"; + this.StartMinimized.Size = new System.Drawing.Size(102, 17); + this.StartMinimized.TabIndex = 2; + this.StartMinimized.Text = "Minimiert starten"; + this.StartMinimized.UseVisualStyleBackColor = true; + // + // groupBox8 + // + this.groupBox8.Controls.Add(this.SortDescending); + this.groupBox8.Location = new System.Drawing.Point(8, 185); + this.groupBox8.Name = "groupBox8"; + this.groupBox8.Size = new System.Drawing.Size(406, 49); + this.groupBox8.TabIndex = 11; + this.groupBox8.TabStop = false; + this.groupBox8.Text = "Zeiteinträge"; + // + // SortDescending + // + this.SortDescending.AutoSize = true; + this.SortDescending.Location = new System.Drawing.Point(10, 23); + this.SortDescending.Name = "SortDescending"; + this.SortDescending.Size = new System.Drawing.Size(319, 17); + this.SortDescending.TabIndex = 4; + this.SortDescending.Text = "Einträge in umgekehrter Reihenfolge sortieren (neueste zuerst)"; + this.SortDescending.UseVisualStyleBackColor = true; + // + // LanguageTab + // + this.LanguageTab.Controls.Add(this.groupBox6); + this.LanguageTab.Location = new System.Drawing.Point(4, 22); + this.LanguageTab.Name = "LanguageTab"; + this.LanguageTab.Size = new System.Drawing.Size(423, 279); + this.LanguageTab.TabIndex = 3; + this.LanguageTab.Text = "Sprache"; + this.LanguageTab.UseVisualStyleBackColor = true; + // + // groupBox6 + // + this.groupBox6.Controls.Add(this.ListLanguages); + this.groupBox6.Location = new System.Drawing.Point(6, 6); + this.groupBox6.Name = "groupBox6"; + this.groupBox6.Size = new System.Drawing.Size(406, 58); + this.groupBox6.TabIndex = 1; + this.groupBox6.TabStop = false; + this.groupBox6.Text = "Sprache"; + // + // ListLanguages + // + this.ListLanguages.FormattingEnabled = true; + this.ListLanguages.Items.AddRange(new object[] { + "Deutsch", + "English"}); + this.ListLanguages.Location = new System.Drawing.Point(10, 23); + this.ListLanguages.Name = "ListLanguages"; + this.ListLanguages.Size = new System.Drawing.Size(383, 21); + this.ListLanguages.TabIndex = 0; + // + // NetworkTab + // + this.NetworkTab.Controls.Add(this.groupBox7); + this.NetworkTab.Location = new System.Drawing.Point(4, 22); + this.NetworkTab.Name = "NetworkTab"; + this.NetworkTab.Padding = new System.Windows.Forms.Padding(3); + this.NetworkTab.Size = new System.Drawing.Size(423, 279); + this.NetworkTab.TabIndex = 1; + this.NetworkTab.Text = "Netzwerk"; + this.NetworkTab.UseVisualStyleBackColor = true; + // + // groupBox7 + // + this.groupBox7.Controls.Add(this.checkBox2); + this.groupBox7.Controls.Add(this.ProxyUser); + this.groupBox7.Controls.Add(this.ProxyPassword); + this.groupBox7.Controls.Add(this.LblProxyUser); + this.groupBox7.Controls.Add(this.LblProxyPassword); + this.groupBox7.Controls.Add(this.BoxUseProxyServer); + this.groupBox7.Controls.Add(this.ProxyPort); + this.groupBox7.Controls.Add(this.label10); + this.groupBox7.Controls.Add(this.ProxyServer); + this.groupBox7.Controls.Add(this.label9); + this.groupBox7.Location = new System.Drawing.Point(6, 6); + this.groupBox7.Name = "groupBox7"; + this.groupBox7.Size = new System.Drawing.Size(406, 158); + this.groupBox7.TabIndex = 1; + this.groupBox7.TabStop = false; + this.groupBox7.Text = "Proxy"; + // + // label9 + // + this.label9.AutoSize = true; + this.label9.Location = new System.Drawing.Point(9, 49); + this.label9.Name = "label9"; + this.label9.Size = new System.Drawing.Size(67, 13); + this.label9.TabIndex = 7; + this.label9.Text = "Proxy-Server"; + // + // ProxyServer + // + this.ProxyServer.Location = new System.Drawing.Point(113, 46); + this.ProxyServer.Name = "ProxyServer"; + this.ProxyServer.Size = new System.Drawing.Size(174, 20); + this.ProxyServer.TabIndex = 10; + // + // label10 + // + this.label10.AutoSize = true; + this.label10.Location = new System.Drawing.Point(293, 49); + this.label10.Name = "label10"; + this.label10.Size = new System.Drawing.Size(26, 13); + this.label10.TabIndex = 8; + this.label10.Text = "Port"; + // + // ProxyPort + // + this.ProxyPort.Location = new System.Drawing.Point(325, 46); + this.ProxyPort.Maximum = new decimal(new int[] { + 65535, + 0, + 0, + 0}); + this.ProxyPort.Name = "ProxyPort"; + this.ProxyPort.Size = new System.Drawing.Size(64, 20); + this.ProxyPort.TabIndex = 11; + // + // BoxUseProxyServer + // + this.BoxUseProxyServer.AutoSize = true; + this.BoxUseProxyServer.Location = new System.Drawing.Point(10, 20); + this.BoxUseProxyServer.Name = "BoxUseProxyServer"; + this.BoxUseProxyServer.Size = new System.Drawing.Size(142, 17); + this.BoxUseProxyServer.TabIndex = 9; + this.BoxUseProxyServer.Text = "Proxy-Server verwenden"; + this.BoxUseProxyServer.UseVisualStyleBackColor = true; + this.BoxUseProxyServer.CheckedChanged += new System.EventHandler(this.BoxUseProxy_CheckedChanged); + // + // LblProxyPassword + // + this.LblProxyPassword.AutoSize = true; + this.LblProxyPassword.Location = new System.Drawing.Point(9, 107); + this.LblProxyPassword.Name = "LblProxyPassword"; + this.LblProxyPassword.Size = new System.Drawing.Size(50, 13); + this.LblProxyPassword.TabIndex = 13; + this.LblProxyPassword.Text = "Passwort"; + // + // LblProxyUser + // + this.LblProxyUser.AutoSize = true; + this.LblProxyUser.Location = new System.Drawing.Point(9, 77); + this.LblProxyUser.Name = "LblProxyUser"; + this.LblProxyUser.Size = new System.Drawing.Size(55, 13); + this.LblProxyUser.TabIndex = 12; + this.LblProxyUser.Text = "Username"; + // + // ProxyPassword + // + this.ProxyPassword.Location = new System.Drawing.Point(113, 104); + this.ProxyPassword.Name = "ProxyPassword"; + this.ProxyPassword.Size = new System.Drawing.Size(276, 20); + this.ProxyPassword.TabIndex = 15; + this.ProxyPassword.UseSystemPasswordChar = true; + // + // ProxyUser + // + this.ProxyUser.Location = new System.Drawing.Point(113, 74); + this.ProxyUser.Name = "ProxyUser"; + this.ProxyUser.Size = new System.Drawing.Size(276, 20); + this.ProxyUser.TabIndex = 14; + // + // checkBox2 + // + this.checkBox2.AutoSize = true; + this.checkBox2.Location = new System.Drawing.Point(113, 130); + this.checkBox2.Name = "checkBox2"; + this.checkBox2.Size = new System.Drawing.Size(115, 17); + this.checkBox2.TabIndex = 16; + this.checkBox2.Text = "Passwort anzeigen"; + this.checkBox2.UseVisualStyleBackColor = true; + this.checkBox2.CheckedChanged += new System.EventHandler(this.checkBox2_CheckedChanged); + // + // AccountTab + // + this.AccountTab.Controls.Add(this.groupBox1); + this.AccountTab.Controls.Add(this.GroupBoxAuthenticateByEmailAndPassword); + this.AccountTab.Controls.Add(this.GroupBoxAuthenticateByAPIKey); + this.AccountTab.Location = new System.Drawing.Point(4, 22); + this.AccountTab.Name = "AccountTab"; + this.AccountTab.Padding = new System.Windows.Forms.Padding(3); + this.AccountTab.Size = new System.Drawing.Size(423, 279); + this.AccountTab.TabIndex = 0; + this.AccountTab.Text = "Account"; + this.AccountTab.UseVisualStyleBackColor = true; + // + // GroupBoxAuthenticateByAPIKey + // + this.GroupBoxAuthenticateByAPIKey.Controls.Add(this.APIKey); + this.GroupBoxAuthenticateByAPIKey.Controls.Add(this.label4); + this.GroupBoxAuthenticateByAPIKey.Location = new System.Drawing.Point(6, 207); + this.GroupBoxAuthenticateByAPIKey.Name = "GroupBoxAuthenticateByAPIKey"; + this.GroupBoxAuthenticateByAPIKey.Size = new System.Drawing.Size(406, 61); + this.GroupBoxAuthenticateByAPIKey.TabIndex = 0; + this.GroupBoxAuthenticateByAPIKey.TabStop = false; + this.GroupBoxAuthenticateByAPIKey.Text = "Anmeldung per API-Schlüssel"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(7, 29); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(52, 13); + this.label4.TabIndex = 7; + this.label4.Text = "Schlüssel"; + // + // APIKey + // + this.APIKey.Location = new System.Drawing.Point(111, 26); + this.APIKey.Name = "APIKey"; + this.APIKey.Size = new System.Drawing.Size(276, 20); + this.APIKey.TabIndex = 7; + // + // GroupBoxAuthenticateByEmailAndPassword + // + this.GroupBoxAuthenticateByEmailAndPassword.Controls.Add(this.CheckBoxShowPasswordInClearText); + this.GroupBoxAuthenticateByEmailAndPassword.Controls.Add(this.Email); + this.GroupBoxAuthenticateByEmailAndPassword.Controls.Add(this.Password); + this.GroupBoxAuthenticateByEmailAndPassword.Controls.Add(this.label2); + this.GroupBoxAuthenticateByEmailAndPassword.Controls.Add(this.label3); + this.GroupBoxAuthenticateByEmailAndPassword.Location = new System.Drawing.Point(6, 91); + this.GroupBoxAuthenticateByEmailAndPassword.Name = "GroupBoxAuthenticateByEmailAndPassword"; + this.GroupBoxAuthenticateByEmailAndPassword.Size = new System.Drawing.Size(406, 110); + this.GroupBoxAuthenticateByEmailAndPassword.TabIndex = 0; + this.GroupBoxAuthenticateByEmailAndPassword.TabStop = false; + this.GroupBoxAuthenticateByEmailAndPassword.Text = "Anmeldung per E-Mail/Passwort"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(7, 58); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(50, 13); + this.label3.TabIndex = 2; + this.label3.Text = "Passwort"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(7, 28); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(77, 13); + this.label2.TabIndex = 1; + this.label2.Text = "E-Mail-Adresse"; + // + // Password + // + this.Password.Location = new System.Drawing.Point(111, 55); + this.Password.Name = "Password"; + this.Password.Size = new System.Drawing.Size(276, 20); + this.Password.TabIndex = 5; + this.Password.UseSystemPasswordChar = true; + // + // Email + // + this.Email.Location = new System.Drawing.Point(111, 25); + this.Email.Name = "Email"; + this.Email.Size = new System.Drawing.Size(276, 20); + this.Email.TabIndex = 4; + // + // CheckBoxShowPasswordInClearText + // + this.CheckBoxShowPasswordInClearText.AutoSize = true; + this.CheckBoxShowPasswordInClearText.Location = new System.Drawing.Point(111, 81); + this.CheckBoxShowPasswordInClearText.Name = "CheckBoxShowPasswordInClearText"; + this.CheckBoxShowPasswordInClearText.Size = new System.Drawing.Size(115, 17); + this.CheckBoxShowPasswordInClearText.TabIndex = 6; + this.CheckBoxShowPasswordInClearText.Text = "Passwort anzeigen"; + this.CheckBoxShowPasswordInClearText.UseVisualStyleBackColor = true; + this.CheckBoxShowPasswordInClearText.CheckedChanged += new System.EventHandler(this.CheckBoxShowPasswordInClearText_CheckedChanged_1); + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.label12); + this.groupBox1.Controls.Add(this.label11); + this.groupBox1.Controls.Add(this.pictureBox1); + this.groupBox1.Controls.Add(this.AuthenticateByAPIKey); + this.groupBox1.Controls.Add(this.AuthenticateByEmailAndPassword); + this.groupBox1.Controls.Add(this.label5); + this.groupBox1.Controls.Add(this.label1); + this.groupBox1.Controls.Add(this.AccountName); + this.groupBox1.Location = new System.Drawing.Point(6, 6); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(406, 79); + this.groupBox1.TabIndex = 0; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Account"; + // + // AccountName + // + this.AccountName.Location = new System.Drawing.Point(152, 18); + this.AccountName.Name = "AccountName"; + this.AccountName.Size = new System.Drawing.Size(173, 20); + this.AccountName.TabIndex = 1; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(7, 22); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(33, 13); + this.label1.TabIndex = 4; + this.label1.Text = "Login"; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(7, 51); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(71, 13); + this.label5.TabIndex = 5; + this.label5.Text = "Anmelden via"; + // + // AuthenticateByEmailAndPassword + // + this.AuthenticateByEmailAndPassword.AutoSize = true; + this.AuthenticateByEmailAndPassword.Location = new System.Drawing.Point(111, 49); + this.AuthenticateByEmailAndPassword.Name = "AuthenticateByEmailAndPassword"; + this.AuthenticateByEmailAndPassword.Size = new System.Drawing.Size(102, 17); + this.AuthenticateByEmailAndPassword.TabIndex = 2; + this.AuthenticateByEmailAndPassword.TabStop = true; + this.AuthenticateByEmailAndPassword.Text = "E-Mail/Passwort"; + this.AuthenticateByEmailAndPassword.UseVisualStyleBackColor = true; + this.AuthenticateByEmailAndPassword.Click += new System.EventHandler(this.RadioAuthByEmailAndPassword_Click); + // + // AuthenticateByAPIKey + // + this.AuthenticateByAPIKey.AutoSize = true; + this.AuthenticateByAPIKey.Location = new System.Drawing.Point(235, 49); + this.AuthenticateByAPIKey.Name = "AuthenticateByAPIKey"; + this.AuthenticateByAPIKey.Size = new System.Drawing.Size(90, 17); + this.AuthenticateByAPIKey.TabIndex = 3; + this.AuthenticateByAPIKey.TabStop = true; + this.AuthenticateByAPIKey.Text = "API-Schlüssel"; + this.AuthenticateByAPIKey.UseVisualStyleBackColor = true; + this.AuthenticateByAPIKey.Click += new System.EventHandler(this.RadioAuthByAPIKey_Click); + // + // pictureBox1 + // + this.pictureBox1.Cursor = System.Windows.Forms.Cursors.Hand; + this.pictureBox1.Image = global::SixtyNineDegrees.MiteDesk.WinForms.Properties.Resources.hilfe; + this.pictureBox1.Location = new System.Drawing.Point(41, 22); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(16, 16); + this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; + this.pictureBox1.TabIndex = 10; + this.pictureBox1.TabStop = false; + this.pictureBox1.Click += new System.EventHandler(this.pictureBox1_Click); + // + // label11 + // + this.label11.AutoSize = true; + this.label11.Location = new System.Drawing.Point(108, 22); + this.label11.Name = "label11"; + this.label11.Size = new System.Drawing.Size(43, 13); + this.label11.TabIndex = 11; + this.label11.Text = "https://"; + // + // label12 + // + this.label12.AutoSize = true; + this.label12.Location = new System.Drawing.Point(326, 22); + this.label12.Name = "label12"; + this.label12.Size = new System.Drawing.Size(54, 13); + this.label12.TabIndex = 12; + this.label12.Text = ".mite.yo.lk"; + // + // TabContainer + // + this.TabContainer.Controls.Add(this.AccountTab); + this.TabContainer.Controls.Add(this.NetworkTab); + this.TabContainer.Controls.Add(this.LanguageTab); + this.TabContainer.Controls.Add(this.ExtendedTab); + this.TabContainer.Location = new System.Drawing.Point(9, 10); + this.TabContainer.Name = "TabContainer"; + this.TabContainer.SelectedIndex = 0; + this.TabContainer.Size = new System.Drawing.Size(431, 305); + this.TabContainer.TabIndex = 12; + // + // Settings + // + this.AcceptButton = this.ButtonOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.ButtonCancel; + this.ClientSize = new System.Drawing.Size(449, 356); + this.Controls.Add(this.TabContainer); + this.Controls.Add(this.CheckConnectionButton); + this.Controls.Add(this.ButtonOK); + this.Controls.Add(this.ButtonCancel); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "Settings"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Einstellungen"; + ((System.ComponentModel.ISupportInitialize)(this.ErrorProvider)).EndInit(); + this.ExtendedTab.ResumeLayout(false); + this.groupBox5.ResumeLayout(false); + this.groupBox5.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.groupBox2.PerformLayout(); + this.groupBox8.ResumeLayout(false); + this.groupBox8.PerformLayout(); + this.LanguageTab.ResumeLayout(false); + this.groupBox6.ResumeLayout(false); + this.NetworkTab.ResumeLayout(false); + this.groupBox7.ResumeLayout(false); + this.groupBox7.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.ProxyPort)).EndInit(); + this.AccountTab.ResumeLayout(false); + this.GroupBoxAuthenticateByAPIKey.ResumeLayout(false); + this.GroupBoxAuthenticateByAPIKey.PerformLayout(); + this.GroupBoxAuthenticateByEmailAndPassword.ResumeLayout(false); + this.GroupBoxAuthenticateByEmailAndPassword.PerformLayout(); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.TabContainer.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Button ButtonCancel; + private System.Windows.Forms.Button ButtonOK; + private System.Windows.Forms.Button CheckConnectionButton; + private System.Windows.Forms.ErrorProvider ErrorProvider; + private System.Windows.Forms.CheckBox checkBox1; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.Label label8; + private System.Windows.Forms.TabControl TabContainer; + private System.Windows.Forms.TabPage AccountTab; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.Label label12; + private System.Windows.Forms.Label label11; + private System.Windows.Forms.PictureBox pictureBox1; + private System.Windows.Forms.RadioButton AuthenticateByAPIKey; + private System.Windows.Forms.RadioButton AuthenticateByEmailAndPassword; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox AccountName; + private System.Windows.Forms.GroupBox GroupBoxAuthenticateByEmailAndPassword; + private System.Windows.Forms.CheckBox CheckBoxShowPasswordInClearText; + private System.Windows.Forms.TextBox Email; + private System.Windows.Forms.TextBox Password; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.GroupBox GroupBoxAuthenticateByAPIKey; + private System.Windows.Forms.TextBox APIKey; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.TabPage NetworkTab; + private System.Windows.Forms.GroupBox groupBox7; + private System.Windows.Forms.CheckBox checkBox2; + private System.Windows.Forms.TextBox ProxyUser; + private System.Windows.Forms.TextBox ProxyPassword; + private System.Windows.Forms.Label LblProxyUser; + private System.Windows.Forms.Label LblProxyPassword; + private System.Windows.Forms.CheckBox BoxUseProxyServer; + private System.Windows.Forms.NumericUpDown ProxyPort; + private System.Windows.Forms.Label label10; + private System.Windows.Forms.TextBox ProxyServer; + private System.Windows.Forms.Label label9; + private System.Windows.Forms.TabPage LanguageTab; + private System.Windows.Forms.GroupBox groupBox6; + private System.Windows.Forms.ComboBox ListLanguages; + private System.Windows.Forms.TabPage ExtendedTab; + private System.Windows.Forms.GroupBox groupBox8; + private System.Windows.Forms.CheckBox SortDescending; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.CheckBox StartMinimized; + private System.Windows.Forms.CheckBox EnableAutostart; + private System.Windows.Forms.GroupBox groupBox5; + private System.Windows.Forms.CheckBox AskForStoppingStopwatchByClosing; + private System.Windows.Forms.CheckBox StopStopwatchByClosing; + private System.Windows.Forms.CheckBox MinimizeByClosing; + } +} \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Settings.cs b/App/MiteDesk.WinForms/Settings.cs new file mode 100644 index 0000000..b1ba037 --- /dev/null +++ b/App/MiteDesk.WinForms/Settings.cs @@ -0,0 +1,309 @@ +using System; +using System.Windows.Forms; +using SixtyNineDegrees.MiteDesk.Core.Infrastructure; +using SixtyNineDegrees.MiteDesk.Core.Model; +using SixtyNineDegrees.MiteDesk.Core.Services; +using SixtyNineDegrees.MiteDesk.Tools.Connector; +using SixtyNineDegrees.MiteDesk.WinForms.Localization; +using StructureMap; + +namespace SixtyNineDegrees.MiteDesk.WinForms +{ + public partial class Settings : Form + { + + public bool ReLocalize; + public AppSettings AppSettings; + + public Settings(bool firstSetup) + { + + ReLocalize = false; + FirstSetup = firstSetup; + + InitializeComponent(); + LocalizeForm(); + + Config = ObjectFactory.GetInstance(); + + TabContainer.SelectedIndexChanged += TabContainer_SelectedIndexChanged; + + AppSettings = Config.GetAppSettings(); + MinimizeByClosing.Checked = AppSettings.MinimizeByClosing; + EnableAutostart.Checked = AppSettings.Autostart; + AccountName.Text = AppSettings.AccountName; + StartMinimized.Checked = AppSettings.StartMinimized; + BoxUseProxyServer.Checked = AppSettings.UseProxy; + StopStopwatchByClosing.Checked = AppSettings.StopStopwatchByClosing; + AskForStoppingStopwatchByClosing.Checked = AppSettings.AskForStoppingStopwatchByClosing; + SortDescending.Checked = AppSettings.SortTimeEntriesDescending; + + SetProxyPanel(); + if (BoxUseProxyServer.Checked) + { + ProxyServer.Text = AppSettings.ProxyServer; + ProxyPort.Value = AppSettings.ProxyPort; + ProxyUser.Text = AppSettings.ProxyUser; + ProxyPassword.Text = AppSettings.ProxyPassword; + } + + if (!string.IsNullOrEmpty(AppSettings.APIKey)) + { + APIKey.Text = AppSettings.APIKey; + SetAuthentiactionByAPIKey(); + } + else + { + SetAuthenticationByEmailAndPassword(); + Email.Text = AppSettings.Email; + Password.Text = AppSettings.Password; + } + + ListLanguages.SelectedIndex = AppSettings.Culture == "en-US" ? 1 : 0; + + if (firstSetup) + ShowFirstSetupMessage(); + + } + + void LocalizeForm() + { + + #region Lokalisierung + + Text = SettingsLabels.FormTitle; + + AccountTab.Text = SettingsLabels.TabAccount; + LanguageTab.Text = SettingsLabels.TabLanguage; + ExtendedTab.Text = SettingsLabels.TabAdvanced; + NetworkTab.Text = SettingsLabels.TabNetwork; + + CheckConnectionButton.Text = SettingsLabels.ButtonCheckConnection; + ButtonOK.Text = SettingsLabels.ButtonOK; + ButtonCancel.Text = SettingsLabels.ButtonCancel; + + groupBox1.Text = SettingsLabels.AccountGroupboxTitle; + GroupBoxAuthenticateByEmailAndPassword.Text = SettingsLabels.LoginByAccountGroupboxTitle; + GroupBoxAuthenticateByAPIKey.Text = SettingsLabels.LoginByApiKeyGroupboxTitle; + groupBox6.Text = SettingsLabels.LanguageGroupboxTitle; + groupBox2.Text = SettingsLabels.StartGroupboxTitle; + groupBox5.Text = SettingsLabels.AdvancedGroupboxTitle; + + label1.Text = SettingsLabels.AccountLabelAccountName; + label5.Text = SettingsLabels.AccountLabelConnectBy; + AuthenticateByEmailAndPassword.Text = SettingsLabels.AccountLabelConnectByEmailAndPassword; + AuthenticateByAPIKey.Text = SettingsLabels.AccountLabelConnectByApiKey; + + label2.Text = SettingsLabels.LoginByAccountLabelEmail; + label3.Text = SettingsLabels.LoginByAccountLabelPassword; + CheckBoxShowPasswordInClearText.Text = SettingsLabels.LoginByAccountLabelShowPassword; + checkBox2.Text = SettingsLabels.LoginByAccountLabelShowPassword; + label4.Text = SettingsLabels.AccountLabelAPIKey; + + EnableAutostart.Text = SettingsLabels.StartLabelAutostart; + StartMinimized.Text = SettingsLabels.StartLabelStartMinimized; + MinimizeByClosing.Text = SettingsLabels.AdvancedLabelMinimizeByClose; + + BoxUseProxyServer.Text = SettingsLabels.ProxyServerLabel; + label9.Text = SettingsLabels.ProxyServer; + + LblProxyPassword.Text = SettingsLabels.ProxyPassword; + LblProxyUser.Text = SettingsLabels.ProxyUser; + + StopStopwatchByClosing.Text = SettingsLabels.StopStopwatchByClosing; + AskForStoppingStopwatchByClosing.Text = SettingsLabels.AskForStoppingStopwatchByClosing; + + groupBox8.Text = SettingsLabels.TimeEntriesGroupboxTitle; + SortDescending.Text = SettingsLabels.SortDescending; + + #endregion + + } + + void TabContainer_SelectedIndexChanged(object sender, EventArgs e) + { + CheckConnectionButton.Visible = TabContainer.SelectedIndex <= 1; + } + + private readonly bool FirstSetup; + private readonly IConfigurationService Config; + + private void SetAuthentiactionByAPIKey() + { + ErrorProvider.Clear(); + AuthenticateByAPIKey.Checked = true; + AuthenticateByEmailAndPassword.Checked = false; + GroupBoxAuthenticateByAPIKey.Enabled = true; + GroupBoxAuthenticateByEmailAndPassword.Enabled = false; + } + + private void SetAuthenticationByEmailAndPassword() + { + ErrorProvider.Clear(); + AuthenticateByAPIKey.Checked = false; + AuthenticateByEmailAndPassword.Checked = true; + GroupBoxAuthenticateByAPIKey.Enabled = false; + GroupBoxAuthenticateByEmailAndPassword.Enabled = true; + } + + private void ButtonCancel_Click(object sender, EventArgs e) + { + Close(); + } + + private void ButtonOK_Click(object sender, EventArgs e) + { + if(SaveFormData()) + { + //if (!FirstSetup) + //{ + Cursor = Cursors.WaitCursor; + ((Main)Owner).InitializeForm(false); + Cursor = Cursors.Default; + //} + Close(); + } + } + + private bool SaveFormData() + { + + var settings = new AppSettings(); + settings.AccountName = AccountName.Text; + settings.MinimizeByClosing = MinimizeByClosing.Checked; + settings.Autostart = EnableAutostart.Checked; + settings.StartMinimized = StartMinimized.Checked; + settings.UseProxy = BoxUseProxyServer.Checked; + settings.ProxyServer = ProxyServer.Text; + settings.ProxyPort = (int)ProxyPort.Value; + settings.ProxyPassword = ProxyPassword.Text; + settings.ProxyUser = ProxyUser.Text; + settings.AskForStoppingStopwatchByClosing = AskForStoppingStopwatchByClosing.Checked; + settings.StopStopwatchByClosing = StopStopwatchByClosing.Checked; + settings.SortTimeEntriesDescending = SortDescending.Checked; + + settings.Culture = ListLanguages.SelectedIndex == 1 ? "en-US" : "de-DE"; + ReLocalize = AppSettings.Culture != settings.Culture; + + if (AuthenticateByAPIKey.Checked) + { + settings.AuthenticationType = AuthenticationType.APIKey; + settings.APIKey = APIKey.Text; + } + else + { + settings.AuthenticationType = AuthenticationType.EmailAndPassword; + settings.Email = Email.Text; + settings.Password = Password.Text; + } + + var result = Config.UpdateAppSettings(settings); + + if (result.Count == 0) + { + Helper.SetCulture(settings.Culture); + return true; + } + + ErrorProvider.Clear(); + + if (result.ContainsKey("AccountName")) + ErrorProvider.SetError(AccountName, result["AccountName"]); + + if (result.ContainsKey("Email")) + ErrorProvider.SetError(Email, result["Email"]); + + if (result.ContainsKey("Password")) + ErrorProvider.SetError(Password, result["Password"]); + + if (result.ContainsKey("APIKey")) + ErrorProvider.SetError(APIKey, result["APIKey"]); + + return false; + + } + + private void RadioAuthByEmailAndPassword_Click(object sender, EventArgs e) + { + SetAuthenticationByEmailAndPassword(); + } + + private void RadioAuthByAPIKey_Click(object sender, EventArgs e) + { + SetAuthentiactionByAPIKey(); + } + + private void CheckBoxShowPasswordInClearText_CheckedChanged_1(object sender, EventArgs e) + { + Password.UseSystemPasswordChar = !CheckBoxShowPasswordInClearText.Checked; + } + + private void button1_Click(object sender, EventArgs e) + { + if (SaveFormData()) + { + Cursor = Cursors.WaitCursor; + try + { + var user = ObjectFactory.GetInstance().GetAuthenticatedUser(); + MessageBox.Show(string.Format(SettingsLabels.MsgBoxConnectionTestSuccessText, user.Name, user.Email), SettingsLabels.MsgBoxConnectionTestSuccessTitle, MessageBoxButtons.OK, MessageBoxIcon.Information); + } + catch(MiteConnectorException ex) + { + ((Main)Owner).EnableOrDisableForm(false); + MessageBox.Show(string.Format(SettingsLabels.MsgBoxConnectionTestFailureText, ex.Message), SettingsLabels.MsgBoxConnectionTestFailureTitle, MessageBoxButtons.OK, MessageBoxIcon.Error); + } + Cursor = Cursors.Default; + } + } + + private static void ShowFirstSetupMessage() + { + MessageBox.Show(SettingsLabels.MsgBoxFirstSetupMessageText, SettingsLabels.MsgBoxFirstSetupMessageTitle, MessageBoxButtons.OK); + } + + protected override void OnClosed(EventArgs e) + { + ((Main)Owner).SetAppSettings(); + if (ReLocalize) + ((Main)Owner).LocalizeForm(); + base.OnClosed(e); + } + + private void BoxUseProxy_CheckedChanged(object sender, EventArgs e) + { + SetProxyPanel(); + } + + private void SetProxyPanel() + { + ProxyServer.Enabled = BoxUseProxyServer.Checked; + ProxyPort.Enabled = BoxUseProxyServer.Checked; + ProxyUser.Enabled = BoxUseProxyServer.Checked; + ProxyPassword.Enabled = BoxUseProxyServer.Checked; + } + + private void checkBox2_CheckedChanged(object sender, EventArgs e) + { + ProxyPassword.UseSystemPasswordChar = !checkBox2.Checked; + } + + private void pictureBox1_Click(object sender, EventArgs e) + { + new LoginHelp().ShowDialog(this); + } + + private void StopStopwatchByClosing_CheckedChanged(object sender, EventArgs e) + { + if (StopStopwatchByClosing.Checked) + AskForStoppingStopwatchByClosing.Checked = false; + } + + private void AskForStoppingStopwatchByClosing_CheckedChanged(object sender, EventArgs e) + { + if (AskForStoppingStopwatchByClosing.Checked) + StopStopwatchByClosing.Checked = false; + } + + } +} diff --git a/App/MiteDesk.WinForms/Settings.resx b/App/MiteDesk.WinForms/Settings.resx new file mode 100644 index 0000000..c496b4e --- /dev/null +++ b/App/MiteDesk.WinForms/Settings.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/App/MiteDesk.WinForms/Stuff/Helper.cs b/App/MiteDesk.WinForms/Stuff/Helper.cs new file mode 100644 index 0000000..6e1f52e --- /dev/null +++ b/App/MiteDesk.WinForms/Stuff/Helper.cs @@ -0,0 +1,90 @@ +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Reflection; +using System.Threading; +using System.Windows.Forms; + +namespace SixtyNineDegrees.MiteDesk.WinForms +{ + public static class Helper + { + + public static string CurrentVersion + { + get + { + return Assembly.GetExecutingAssembly().GetName().Version.Major + "." + + Assembly.GetExecutingAssembly().GetName().Version.Minor; + } + } + + public static string CurrentBuild + { + get { return Assembly.GetExecutingAssembly().GetName().Version.Build.ToString(); } + } + + public static void OpenBrowser(string url) + { + try + { + Process.Start(url); + } + catch + { + } + } + + public static void ValidateTextEnteredInComboBox(object sender, EventArgs e) + { + + var comboBox = (ComboBox)sender; + + string text = comboBox.Text; + + if (string.IsNullOrEmpty(text) || text.Length == 0) + return; + + foreach (ListItem item in comboBox.Items) + { + if (item.Text.ToLower().StartsWith(text.ToLower())) + return; + } + + comboBox.Text = text.Substring(0, text.Length - 1); + comboBox.Select(comboBox.Text.Length, 0); + + } + + public static string GetFormattedTimeText(int minutes) + { + return (minutes / 60).ToString().PadLeft(2, '0') + ":" + (minutes % 60).ToString().PadLeft(2, '0'); + } + + public static void StartBackgroundWorker(BackgroundWorker worker, object argument) + { + + if(worker.IsBusy) + { + worker.CancelAsync(); + while (worker.IsBusy) + Application.DoEvents(); + } + + if (argument != null) + worker.RunWorkerAsync(argument); + else + worker.RunWorkerAsync(); + + } + + public static void SetCulture(string culture) + { + var ci = new System.Globalization.CultureInfo(culture, false); + Thread.CurrentThread.CurrentCulture = ci; + Thread.CurrentThread.CurrentUICulture = ci; + } + + } + +} diff --git a/App/MiteDesk.WinForms/Stuff/ListItem.cs b/App/MiteDesk.WinForms/Stuff/ListItem.cs new file mode 100644 index 0000000..9f5c664 --- /dev/null +++ b/App/MiteDesk.WinForms/Stuff/ListItem.cs @@ -0,0 +1,8 @@ +namespace SixtyNineDegrees.MiteDesk.WinForms +{ + public class ListItem + { + public int Value { get; set; } + public string Text { get; set; } + } +} diff --git a/App/MiteDesk.WinForms/mite.desk.ico b/App/MiteDesk.WinForms/mite.desk.ico new file mode 100644 index 0000000..ff25fca Binary files /dev/null and b/App/MiteDesk.WinForms/mite.desk.ico differ diff --git a/Core/MiteDesk.Core.Configuration/MiteDesk.Core.Configuration.csproj b/Core/MiteDesk.Core.Configuration/MiteDesk.Core.Configuration.csproj new file mode 100644 index 0000000..8536b26 --- /dev/null +++ b/Core/MiteDesk.Core.Configuration/MiteDesk.Core.Configuration.csproj @@ -0,0 +1,59 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {DD1B5B7C-2272-4D57-A0CF-190193689373} + Library + Properties + MiteDesk.Core.Configuration + MiteDesk.Core.Configuration + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + \ No newline at end of file diff --git a/Core/MiteDesk.Core.Configuration/Properties/AssemblyInfo.cs b/Core/MiteDesk.Core.Configuration/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..1ecd41f --- /dev/null +++ b/Core/MiteDesk.Core.Configuration/Properties/AssemblyInfo.cs @@ -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 mit einer Assembly verknüpft sind. +[assembly: AssemblyTitle("MiteDesk.Core.Configuration")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("MiteDesk.Core.Configuration")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("b198584e-c66a-45b8-b961-63ca6672ace0")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Core/MiteDesk.Core.Data/Contracts/IActivityRepository.cs b/Core/MiteDesk.Core.Data/Contracts/IActivityRepository.cs new file mode 100644 index 0000000..4af502b --- /dev/null +++ b/Core/MiteDesk.Core.Data/Contracts/IActivityRepository.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using SixtyNineDegrees.MiteDesk.Core.Model; + +namespace SixtyNineDegrees.MiteDesk.Core.Data +{ + public interface IActivityRepository + { + IList GetAllActiveActivities(); + IList GetAllArchivedActivities(); + Activity GetActivityByID(int activityID); + void DeleteActivity(int activityID); + void CreateActivity(Activity activity); + void UpdateActivity(Activity activity); + } +} \ No newline at end of file diff --git a/Core/MiteDesk.Core.Data/Contracts/ICustomerRepository.cs b/Core/MiteDesk.Core.Data/Contracts/ICustomerRepository.cs new file mode 100644 index 0000000..6664676 --- /dev/null +++ b/Core/MiteDesk.Core.Data/Contracts/ICustomerRepository.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using SixtyNineDegrees.MiteDesk.Core.Model; + +namespace SixtyNineDegrees.MiteDesk.Core.Data +{ + public interface ICustomerRepository + { + IList GetAllActiveCustomers(); + IList GetAllArchivedCustomers(); + Customer GetCustomerByID(int customerID); + void DeleteCustomer(int customerID); + void CreateCustomer(Customer customer); + void UpdateCustomer(Customer customer); + } +} \ No newline at end of file diff --git a/Core/MiteDesk.Core.Data/Contracts/IProjectRepository.cs b/Core/MiteDesk.Core.Data/Contracts/IProjectRepository.cs new file mode 100644 index 0000000..cae3f61 --- /dev/null +++ b/Core/MiteDesk.Core.Data/Contracts/IProjectRepository.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using SixtyNineDegrees.MiteDesk.Core.Model; + +namespace SixtyNineDegrees.MiteDesk.Core.Data +{ + public interface IProjectRepository + { + IList GetAllActiveProjects(); + IList GetAllArchivedProjects(); + IList GetProjectsByCustomer(int customerID); + Project GetProjectByID(int projectID); + void CreateProject(Project project); + void UpdateProject(Project project); + void DeleteProject(int projectID); + } +} \ No newline at end of file diff --git a/Core/MiteDesk.Core.Data/Contracts/ITimeEntryRepository.cs b/Core/MiteDesk.Core.Data/Contracts/ITimeEntryRepository.cs new file mode 100644 index 0000000..4562e1d --- /dev/null +++ b/Core/MiteDesk.Core.Data/Contracts/ITimeEntryRepository.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using SixtyNineDegrees.MiteDesk.Core.Model; + +namespace SixtyNineDegrees.MiteDesk.Core.Data +{ + public interface ITimeEntryRepository + { + void CreateTimeEntry(ref TimeEntry entry); + void DeleteTimeEntry(int timeEntryID); + void UpdateTimeEntry(TimeEntry entry); + void StartStopwatch(int timeEntryID); + void StopStopwatch(int timeEntryID); + IList GetTimeEntriesByDate(DateTime date); + IList GetTimeEntriesByActivityID(int activityID); + IList GetTimeEntriesByProjectID(int projectID); + IList GetTimeEntryDatesByRange(DateTime start, DateTime end, int userID); + TimeEntry GetTimeEntryByID(int timeEntryID); + TimeEntry GetTimeEntryCurrentlyTrackedByStopwatch(); + } +} \ No newline at end of file diff --git a/Core/MiteDesk.Core.Data/Contracts/IUserRepository.cs b/Core/MiteDesk.Core.Data/Contracts/IUserRepository.cs new file mode 100644 index 0000000..a77de4e --- /dev/null +++ b/Core/MiteDesk.Core.Data/Contracts/IUserRepository.cs @@ -0,0 +1,9 @@ +using SixtyNineDegrees.MiteDesk.Core.Model; + +namespace SixtyNineDegrees.MiteDesk.Core.Data +{ + public interface IUserRepository + { + User GetAuthenticatedUser(); + } +} \ No newline at end of file diff --git a/Core/MiteDesk.Core.Data/Mite/MiteCustomers.cs b/Core/MiteDesk.Core.Data/Mite/MiteCustomers.cs new file mode 100644 index 0000000..214b280 --- /dev/null +++ b/Core/MiteDesk.Core.Data/Mite/MiteCustomers.cs @@ -0,0 +1,97 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; +using SixtyNineDegrees.MiteDesk.Core.Infrastructure; +using SixtyNineDegrees.MiteDesk.Core.Model; +using SixtyNineDegrees.MiteDesk.Tools.Connector; + +namespace SixtyNineDegrees.MiteDesk.Core.Data +{ + public class MiteCustomers : ICustomerRepository + { + + public MiteCustomers(IConfigurationService configurationService) + { + ConfigurationService = configurationService; + } + + private readonly IConfigurationService ConfigurationService; + + private Connector Connector + { + get { return new Connector(ConfigurationService.GetAppSettings());} + } + + public IList GetAllActiveCustomers() + { + var customersSource = Connector.HttpGet("customers.xml"); + IEnumerable customers = from c in customersSource.Elements("customer") + select c; + + var result = new List(); + foreach (XElement c in customers) + { + result.Add(CreateCustomer(c)); + } + return result; + } + + public IList GetAllArchivedCustomers() + { + var customersSource = Connector.HttpGet("customers/archived.xml"); + IEnumerable customers = from c in customersSource.Elements("customer") + select c; + + var result = new List(); + foreach (XElement c in customers) + { + result.Add(CreateCustomer(c)); + } + return result; + } + + public Customer GetCustomerByID(int customerID) + { + return CreateCustomer(Connector.HttpGet(string.Format("customers/{0}.xml", customerID))); + } + + public void DeleteCustomer(int customerID) + { + Connector.HttpDelete("customers/" + customerID + ".xml"); + } + + public void CreateCustomer(Customer customer) + { + var xml = new StringBuilder(); + xml.Append(""); + xml.AppendFormat("{0}", System.Web.HttpUtility.HtmlEncode(customer.Name)); + xml.AppendFormat("{0}", System.Web.HttpUtility.HtmlEncode(customer.Note)); + xml.AppendFormat("{0}", customer.Archived ? "true" : "false"); + xml.Append(""); + Connector.HttpPost("customers.xml", xml.ToString()); + } + + public void UpdateCustomer(Customer customer) + { + var xml = new StringBuilder(); + xml.Append(""); + xml.AppendFormat("{0}", System.Web.HttpUtility.HtmlEncode(customer.Name)); + xml.AppendFormat("{0}", System.Web.HttpUtility.HtmlEncode(customer.Note)); + xml.AppendFormat("{0}", customer.Archived ? "true" : "false"); + xml.Append(""); + Connector.HttpPut("customers/" + customer.ID + ".xml", xml.ToString()); + } + + private Customer CreateCustomer(XElement source) + { + return new Customer + { + ID = source.Element(XName.Get("id")) != null ? int.Parse(source.Element(XName.Get("id")).Value) : 0, + Name = source.Element(XName.Get("name")) != null ? source.Element(XName.Get("name")).Value : string.Empty, + Note = source.Element(XName.Get("note")) != null ? source.Element(XName.Get("note")).Value : string.Empty, + Archived = source.Element(XName.Get("archived")) != null ? bool.Parse(source.Element(XName.Get("archived")).Value) : false + }; + } + } +} \ No newline at end of file diff --git a/Core/MiteDesk.Core.Data/Mite/MiteProjects.cs b/Core/MiteDesk.Core.Data/Mite/MiteProjects.cs new file mode 100644 index 0000000..abf14f6 --- /dev/null +++ b/Core/MiteDesk.Core.Data/Mite/MiteProjects.cs @@ -0,0 +1,121 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; +using SixtyNineDegrees.MiteDesk.Core.Infrastructure; +using SixtyNineDegrees.MiteDesk.Core.Model; +using SixtyNineDegrees.MiteDesk.Tools.Connector; + +namespace SixtyNineDegrees.MiteDesk.Core.Data +{ + public class MiteProjects : IProjectRepository + { + + public MiteProjects(IConfigurationService configurationService) + { + ConfigurationService = configurationService; + } + + private readonly IConfigurationService ConfigurationService; + + private Connector Connector + { + get { return new Connector(ConfigurationService.GetAppSettings());} + } + + public IList GetAllActiveProjects() + { + var projectsSource = Connector.HttpGet("projects.xml"); + IEnumerable projects = from p in projectsSource.Elements("project") + select p; + + var result = new List(); + foreach (XElement p in projects) + { + result.Add(CreateProject(p)); + } + return result; + } + + public IList GetAllArchivedProjects() + { + var projectsSource = Connector.HttpGet("projects/archived.xml"); + IEnumerable projects = from p in projectsSource.Elements("project") + select p; + + var result = new List(); + foreach (XElement p in projects) + { + result.Add(CreateProject(p)); + } + return result; + } + + public IList GetProjectsByCustomer(int customerID) + { + var projectsSource = Connector.HttpGet("projects.xml?customer-id=" + customerID); + IEnumerable projects = from p in projectsSource.Elements("project") + select p; + + var result = new List(); + foreach (XElement p in projects) + { + result.Add(CreateProject(p)); + } + return result; + } + + public Project GetProjectByID(int projectID) + { + return CreateProject(Connector.HttpGet(string.Format("projects/{0}.xml", projectID))); + } + + public void CreateProject(Project project) + { + var xml = new StringBuilder(); + xml.Append(""); + xml.AppendFormat("{0}", System.Web.HttpUtility.HtmlEncode(project.Name)); + xml.AppendFormat("{0}", System.Web.HttpUtility.HtmlEncode(project.Note)); + xml.AppendFormat("{0}", project.Budget); + xml.AppendFormat("{0}", project.BudgetType); + xml.AppendFormat("{0}", project.Archived ? "true" : "false"); + xml.AppendFormat("{0}", project.CustomerID); + xml.Append(""); + Connector.HttpPost("projects.xml", xml.ToString()); + } + + public void UpdateProject(Project project) + { + var xml = new StringBuilder(); + xml.Append(""); + xml.AppendFormat("{0}", System.Web.HttpUtility.HtmlEncode(project.Name)); + xml.AppendFormat("{0}", System.Web.HttpUtility.HtmlEncode(project.Note)); + xml.AppendFormat("{0}", project.Budget); + xml.AppendFormat("{0}", project.BudgetType); + xml.AppendFormat("{0}", project.Archived ? "true" : "false"); + xml.AppendFormat("{0}", project.CustomerID); + xml.Append(""); + Connector.HttpPut("projects/" + project.ID + ".xml", xml.ToString()); + } + + public void DeleteProject(int projectID) + { + Connector.HttpDelete("projects/" + projectID + ".xml"); + } + + private Project CreateProject(XElement source) + { + return new Project + { + ID = source.Element(XName.Get("id")) != null ? int.Parse(source.Element(XName.Get("id")).Value) : 0, + Name = source.Element(XName.Get("name")) != null ? source.Element(XName.Get("name")).Value : string.Empty, + CustomerID = source.Element(XName.Get("customer-id")) != null && !string.IsNullOrEmpty(source.Element(XName.Get("customer-id")).Value) ? int.Parse(source.Element(XName.Get("customer-id")).Value) : 0, + CustomerName = source.Element(XName.Get("customer-name")) != null ? source.Element(XName.Get("customer-name")).Value : string.Empty, + Note = source.Element(XName.Get("note")) != null ? source.Element(XName.Get("note")).Value : string.Empty, + BudgetType = source.Element(XName.Get("budget-type")) != null ? source.Element(XName.Get("budget-type")).Value : string.Empty, + Budget = source.Element(XName.Get("budget")) != null ? int.Parse(source.Element(XName.Get("budget")).Value) : 0, + Archived = source.Element(XName.Get("archived")) != null ? bool.Parse(source.Element(XName.Get("archived")).Value) : false + }; + } + } +} \ No newline at end of file diff --git a/Core/MiteDesk.Core.Data/Mite/MiteServices.cs b/Core/MiteDesk.Core.Data/Mite/MiteServices.cs new file mode 100644 index 0000000..bd0ee19 --- /dev/null +++ b/Core/MiteDesk.Core.Data/Mite/MiteServices.cs @@ -0,0 +1,103 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; +using SixtyNineDegrees.MiteDesk.Core.Infrastructure; +using SixtyNineDegrees.MiteDesk.Core.Model; +using SixtyNineDegrees.MiteDesk.Tools.Connector; + +namespace SixtyNineDegrees.MiteDesk.Core.Data +{ + public class MiteServices : IActivityRepository + { + + public MiteServices(IConfigurationService configurationService) + { + ConfigurationService = configurationService; + } + + private readonly IConfigurationService ConfigurationService; + + private Connector Connector + { + get { return new Connector(ConfigurationService.GetAppSettings());} + } + + public IList GetAllActiveActivities() + { + var activitiesSource = Connector.HttpGet("services.xml"); + IEnumerable activities = from c in activitiesSource.Elements("service") + select c; + + var result = new List(); + foreach (XElement a in activities) + { + result.Add(CreateActivity(a)); + } + return result; + } + + public IList GetAllArchivedActivities() + { + var activitiesSource = Connector.HttpGet("services/archived.xml"); + IEnumerable activities = from c in activitiesSource.Elements("service") + select c; + + var result = new List(); + foreach (XElement a in activities) + { + result.Add(CreateActivity(a)); + } + return result; + } + + public Activity GetActivityByID(int activityID) + { + return CreateActivity(Connector.HttpGet(string.Format("services/{0}.xml", activityID))); + } + + public void DeleteActivity(int activityID) + { + Connector.HttpDelete("services/" + activityID + ".xml"); + } + + public void CreateActivity(Activity activity) + { + var xml = new StringBuilder(); + xml.Append(""); + xml.AppendFormat("{0}", System.Web.HttpUtility.HtmlEncode(activity.Name)); + xml.AppendFormat("{0}", System.Web.HttpUtility.HtmlEncode(activity.Note)); + xml.AppendFormat("{0}", activity.HourlyRate > 0 ? activity.HourlyRate.ToString() : ""); + xml.AppendFormat("{0}", activity.Archived ? "true" : "false"); + xml.AppendFormat("{0}", activity.Billable ? "true" : "false"); + xml.Append(""); + Connector.HttpPost("services.xml", xml.ToString()); + } + + public void UpdateActivity(Activity activity) + { + var xml = new StringBuilder(); + xml.Append(""); + xml.AppendFormat("{0}", System.Web.HttpUtility.HtmlEncode(activity.Name)); + xml.AppendFormat("{0}", System.Web.HttpUtility.HtmlEncode(activity.Note)); + xml.AppendFormat("{0}", activity.HourlyRate > 0 ? activity.HourlyRate.ToString() : ""); + xml.AppendFormat("{0}", activity.Archived ? "true" : "false"); + xml.AppendFormat("{0}", activity.Billable ? "true" : "false"); + xml.Append(""); + Connector.HttpPut("services/" + activity.ID + ".xml", xml.ToString()); + } + + private Activity CreateActivity(XElement source) + { + return new Activity + { + ID = source.Element(XName.Get("id")) != null ? int.Parse(source.Element(XName.Get("id")).Value) : 0, + Name = source.Element(XName.Get("name")) != null ? source.Element(XName.Get("name")).Value : string.Empty, + Note = source.Element(XName.Get("note")) != null ? source.Element(XName.Get("note")).Value : string.Empty, + Archived = source.Element(XName.Get("archived")) != null ? bool.Parse(source.Element(XName.Get("archived")).Value) : false, + Billable = source.Element(XName.Get("billable")) != null ? bool.Parse(source.Element(XName.Get("billable")).Value) : false, + HourlyRate = source.Element(XName.Get("hourly-rate")) != null && !string.IsNullOrEmpty(source.Element(XName.Get("hourly-rate")).Value) ? int.Parse(source.Element(XName.Get("hourly-rate")).Value) : 0 + }; + } + } +} \ No newline at end of file diff --git a/Core/MiteDesk.Core.Data/Mite/MiteTimeEntries.cs b/Core/MiteDesk.Core.Data/Mite/MiteTimeEntries.cs new file mode 100644 index 0000000..20e7e81 --- /dev/null +++ b/Core/MiteDesk.Core.Data/Mite/MiteTimeEntries.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; +using SixtyNineDegrees.MiteDesk.Core.Infrastructure; +using SixtyNineDegrees.MiteDesk.Core.Model; +using SixtyNineDegrees.MiteDesk.Tools.Connector; + +namespace SixtyNineDegrees.MiteDesk.Core.Data +{ + public class MiteTimeEntries : ITimeEntryRepository + { + + public MiteTimeEntries(IConfigurationService configurationService) + { + ConfigurationService = configurationService; + } + + private readonly IConfigurationService ConfigurationService; + + private Connector Connector + { + get { return new Connector(ConfigurationService.GetAppSettings());} + } + + public void CreateTimeEntry(ref TimeEntry entry) + { + var xml = new StringBuilder(); + xml.Append(""); + xml.AppendFormat("{0}-{1}-{2}", entry.Date.Year, entry.Date.Month, entry.Date.Day); + xml.AppendFormat("{0}", entry.Minutes); + xml.AppendFormat("{0}", System.Web.HttpUtility.HtmlEncode(entry.Note)); + xml.AppendFormat("{0}", entry.ActivityID); + xml.AppendFormat("{0}", entry.ProjectID); + xml.AppendFormat("{0}", entry.Locked ? "1" : "0"); + xml.Append(""); + entry.ID = CreateEntry(Connector.HttpPost("time_entries.xml", xml.ToString())).ID; + } + + public void DeleteTimeEntry(int timeEntryId) + { + Connector.HttpDelete("time_entries/" + timeEntryId + ".xml"); + } + + public void UpdateTimeEntry(TimeEntry entry) + { + var xml = new StringBuilder(); + xml.Append(""); + xml.AppendFormat("{0}-{1}-{2}", entry.Date.Year, entry.Date.Month, entry.Date.Day); + xml.AppendFormat("{0}", entry.Minutes); + xml.AppendFormat("{0}", System.Web.HttpUtility.HtmlEncode(entry.Note)); + xml.AppendFormat("{0}", entry.ActivityID); + xml.AppendFormat("{0}", entry.ProjectID); + xml.AppendFormat("{0}", entry.Locked ? "1" : "0"); + xml.Append("1"); + xml.Append(""); + Connector.HttpPut("time_entries/" + entry.ID + ".xml", xml.ToString()); + } + + public void StartStopwatch(int timeEntryID) + { + Connector.HttpPut("tracker/" + timeEntryID + ".xml", string.Empty); + } + + public void StopStopwatch(int timeEntryID) + { + Connector.HttpDelete("tracker/" + timeEntryID + ".xml"); + } + + public IList GetTimeEntriesByDate(DateTime date) + { + var timeEntriesSource = Connector.HttpGet(string.Format("daily/{0}/{1}/{2}.xml", date.Year, date.Month, date.Day)); + IEnumerable timeEntries = from c in timeEntriesSource.Elements("time-entry") + select c; + + var result = new List(); + foreach (XElement a in timeEntries) + { + result.Add(CreateEntry(a)); + } + return result; + } + + public IList GetTimeEntriesByActivityID(int activityID) + { + var timeEntriesSource = Connector.HttpGet("time_entries.xml?service-id=" + activityID); + IEnumerable timeEntries = from c in timeEntriesSource.Elements("time-entry") + select c; + + var result = new List(); + foreach (XElement a in timeEntries) + { + result.Add(CreateEntry(a)); + } + return result; + } + + public IList GetTimeEntriesByProjectID(int projectID) + { + var timeEntriesSource = Connector.HttpGet("time_entries.xml?project-id=" + projectID); + IEnumerable timeEntries = from c in timeEntriesSource.Elements("time-entry") + select c; + + var result = new List(); + foreach (XElement a in timeEntries) + { + result.Add(CreateEntry(a)); + } + return result; + } + + public IList GetTimeEntryDatesByRange(DateTime start, DateTime end, int userID) + { + var timeEntryGroupSource = Connector.HttpGet(string.Format("time_entries.xml?from={0:yyyy}-{0:MM}-{0:dd}&to={1:yyyy}-{1:MM}-{1:dd}&user-id={2}&group_by=day", start, end, userID)); + IEnumerable timeEntryGroups = from c in timeEntryGroupSource.Elements("time-entry-group") + select c; + var result = new List(); + foreach (XElement g in timeEntryGroups) + { + result.Add(DateTime.Parse(g.Element(XName.Get("day")).Value)); + } + return result; + } + + public TimeEntry GetTimeEntryByID(int timeEntryId) + { + return CreateEntry(Connector.HttpGet(string.Format("time_entries/{0}.xml", timeEntryId))); + } + + public TimeEntry GetTimeEntryCurrentlyTrackedByStopwatch() + { + return CreateEntry(Connector.HttpGet("tracker.xml").Element(XName.Get("tracking-time-entry"))); + } + + private TimeEntry CreateEntry(XElement source) + { + if (source == null) + return null; + + return new TimeEntry + { + ID = source.Element(XName.Get("id")) != null ? int.Parse(source.Element(XName.Get("id")).Value) : 0, + ActivityID = source.Element(XName.Get("service-id")) != null && !string.IsNullOrEmpty(source.Element(XName.Get("service-id")).Value) ? int.Parse(source.Element(XName.Get("service-id")).Value) : 0, + ProjectID = source.Element(XName.Get("project-id")) != null && !string.IsNullOrEmpty(source.Element(XName.Get("project-id")).Value) ? int.Parse(source.Element(XName.Get("project-id")).Value) : 0, + Date = source.Element(XName.Get("date-at")) != null ? DateTime.Parse(source.Element(XName.Get("date-at")).Value) : DateTime.MinValue, + Minutes = source.Element(XName.Get("minutes")) != null ? int.Parse(source.Element(XName.Get("minutes")).Value) : 0, + Note = source.Element(XName.Get("note")) != null ? source.Element(XName.Get("note")).Value : string.Empty, + CustomerName = source.Element(XName.Get("customer-name")) != null ? source.Element(XName.Get("customer-name")).Value : "-", + ProjectName = source.Element(XName.Get("project-name")) != null ? source.Element(XName.Get("project-name")).Value : "-", + ActivityName = source.Element(XName.Get("service-name")) != null ? source.Element(XName.Get("service-name")).Value : "-", + Locked = source.Element(XName.Get("locked")) != null ? bool.Parse(source.Element(XName.Get("locked")).Value) : false + }; + } + } +} \ No newline at end of file diff --git a/Core/MiteDesk.Core.Data/Mite/MiteUsers.cs b/Core/MiteDesk.Core.Data/Mite/MiteUsers.cs new file mode 100644 index 0000000..f578506 --- /dev/null +++ b/Core/MiteDesk.Core.Data/Mite/MiteUsers.cs @@ -0,0 +1,57 @@ +using System.Xml.Linq; +using SixtyNineDegrees.MiteDesk.Core.Infrastructure; +using SixtyNineDegrees.MiteDesk.Core.Model; +using SixtyNineDegrees.MiteDesk.Tools.Connector; + +namespace SixtyNineDegrees.MiteDesk.Core.Data +{ + public class MiteUsers : IUserRepository + { + + public MiteUsers(IConfigurationService configurationService) + { + ConfigurationService = configurationService; + } + + private readonly IConfigurationService ConfigurationService; + + private Connector Connector + { + get { return new Connector(ConfigurationService.GetAppSettings());} + } + + public User GetAuthenticatedUser() + { + return CreateUser(Connector.HttpGet("myself.xml")); + } + + private User CreateUser(XElement source) + { + return new User + { + ID = int.Parse(source.Element(XName.Get("id")).Value), + Name = source.Element(XName.Get("name")).Value, + Email = source.Element(XName.Get("email")).Value, + Role = GetRole(source.Element(XName.Get("role")).Value) + }; + } + + private UserRole GetRole(string text) + { + switch (text) + { + case "coworker": + return UserRole.CoWorker; + case "time_tracker": + return UserRole.TimeTracker; + case "admin": + return UserRole.Admin; + case "owner": + return UserRole.Owner; + default: + throw new UserRoleInvalidException(); + } + } + + } +} \ No newline at end of file diff --git a/Core/MiteDesk.Core.Data/MiteDesk.Core.Data.csproj b/Core/MiteDesk.Core.Data/MiteDesk.Core.Data.csproj new file mode 100644 index 0000000..32743ce --- /dev/null +++ b/Core/MiteDesk.Core.Data/MiteDesk.Core.Data.csproj @@ -0,0 +1,131 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {AD8740D8-14A7-4A31-8F96-7DF850D3D957} + Library + Properties + SixtyNineDegrees.MiteDesk.Core.Data + mite.desk.core.data + v3.5 + 512 + true + + + + + 3.5 + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AllRules.ruleset + + + + + + 3.5 + + + 3.0 + + + 3.0 + + + + 3.5 + + + 3.5 + + + + + + + + + + + + + + + + + + + + {1B74B3CE-8969-4157-8B86-AEDA27494109} + MiteDesk.Tools.Connector + + + {733C8DE9-36B5-4C9F-9701-FDD19B64B966} + MiteDesk.Core.Infrastructure + + + {AE69F9FC-F9D4-41D8-9B00-1261F492FB89} + MiteDesk.Core.Model + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + \ No newline at end of file diff --git a/Core/MiteDesk.Core.Data/Properties/AssemblyInfo.cs b/Core/MiteDesk.Core.Data/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..f5e2dfc --- /dev/null +++ b/Core/MiteDesk.Core.Data/Properties/AssemblyInfo.cs @@ -0,0 +1,14 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("mite.desk.data")] +[assembly: AssemblyDescription("mite.desk.data")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("mite.desk")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] +[assembly: Guid("fe66bff2-02ed-4173-a671-919cf2e54b1b")] + +[assembly: AssemblyVersion("1.2.17")] +[assembly: AssemblyFileVersion("1.2.17")] diff --git a/Core/MiteDesk.Core.Infrastructure/Configuration/ConfigurationService.cs b/Core/MiteDesk.Core.Infrastructure/Configuration/ConfigurationService.cs new file mode 100644 index 0000000..cd185fb --- /dev/null +++ b/Core/MiteDesk.Core.Infrastructure/Configuration/ConfigurationService.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using SixtyNineDegrees.MiteDesk.Core.Model; + +namespace SixtyNineDegrees.MiteDesk.Core.Infrastructure +{ + public class ConfigurationService : IConfigurationService + { + + public ConfigurationService(IRegistryService registryService, IEncryptionService encryptionService, string applicationID, string executablePath) + { + RegistryService = registryService; + EncryptionService = encryptionService; + ApplicationID = applicationID; + ExecutablePath = executablePath; + } + + private readonly IRegistryService RegistryService; + private readonly IEncryptionService EncryptionService; + private readonly string ApplicationID; + private readonly string ExecutablePath; + + public AppSettings GetAppSettings() + { + + var settings = new AppSettings(); + + if (!RegistryService.ApplicationRegistryKeyExists()) + RegistryService.CreateApplicationRegistryKey(); + + settings.AccountName = EncryptionService.DecryptString(RegistryService.GetApplicationRegistryKeyValue("C"), ApplicationID); + settings.Email = EncryptionService.DecryptString(RegistryService.GetApplicationRegistryKeyValue("D"), ApplicationID); + settings.APIKey = EncryptionService.DecryptString(RegistryService.GetApplicationRegistryKeyValue("E"), ApplicationID); + settings.Password = EncryptionService.DecryptString(RegistryService.GetApplicationRegistryKeyValue("F"), ApplicationID); + + settings.ProxyUser = EncryptionService.DecryptString(RegistryService.GetApplicationRegistryKeyValue("R"), ApplicationID); + settings.ProxyPassword = EncryptionService.DecryptString(RegistryService.GetApplicationRegistryKeyValue("S"), ApplicationID); + + settings.Culture = RegistryService.GetApplicationRegistryKeyValue("N"); + if (string.IsNullOrEmpty(settings.Culture)) settings.Culture = "de-DE"; + + string autostartRegistryValue = RegistryService.GetApplicationRegistryKeyValue(@"Software\Microsoft\Windows\CurrentVersion\Run", "mite.desk"); + settings.Autostart = autostartRegistryValue != null && autostartRegistryValue == ExecutablePath; + + bool minimizeByClosing; + bool.TryParse(RegistryService.GetApplicationRegistryKeyValue("H"), out minimizeByClosing); + settings.MinimizeByClosing = minimizeByClosing; + + bool startMinimized; + bool.TryParse(RegistryService.GetApplicationRegistryKeyValue("I"), out startMinimized); + settings.StartMinimized = startMinimized; + + DateTime lastUpdateCheck; + DateTime.TryParse(RegistryService.GetApplicationRegistryKeyValue("K"), out lastUpdateCheck); + settings.LastUpdateCheck = lastUpdateCheck; + + int selectedProjectID; + int.TryParse(RegistryService.GetApplicationRegistryKeyValue("L"), out selectedProjectID); + settings.SelectedProjectID = selectedProjectID; + + int activityID; + int.TryParse(RegistryService.GetApplicationRegistryKeyValue("M"), out activityID); + settings.SelectedActivityID = activityID; + + bool useProxy; + bool.TryParse(RegistryService.GetApplicationRegistryKeyValue("O"), out useProxy); + settings.UseProxy = useProxy; + + bool stopStopwatchByClosing; + bool.TryParse(RegistryService.GetApplicationRegistryKeyValue("T"), out stopStopwatchByClosing); + settings.StopStopwatchByClosing = stopStopwatchByClosing; + + bool askForStoppingStopwatchByClosing; + bool.TryParse(RegistryService.GetApplicationRegistryKeyValue("U"), out askForStoppingStopwatchByClosing); + settings.AskForStoppingStopwatchByClosing = askForStoppingStopwatchByClosing; + + bool sortTimeEntriesDescending; + bool.TryParse(RegistryService.GetApplicationRegistryKeyValue("V"), out sortTimeEntriesDescending); + settings.SortTimeEntriesDescending = sortTimeEntriesDescending; + + settings.ProxyServer = RegistryService.GetApplicationRegistryKeyValue("P"); + + int proxyPort; + int.TryParse(RegistryService.GetApplicationRegistryKeyValue("Q"), out proxyPort); + settings.ProxyPort = proxyPort; + + return settings; + + } + + public Dictionary UpdateAppSettings(AppSettings settings) + { + + Dictionary errors = new Dictionary(); + + if(string.IsNullOrEmpty(settings.AccountName) || settings.AccountName.Trim().Length == 0) + errors.Add("AccountName", Localization.Configuration.AccountNameEmpty); + + if(settings.AuthenticationType == AuthenticationType.EmailAndPassword) + { + if (string.IsNullOrEmpty(settings.Email) || settings.Email.Trim().Length == 0) + errors.Add("Email", Localization.Configuration.EmailEmpty); + else if (!new Regex(@"^[\w\.-]+@[\w\.-]+\.[a-zA-Z]{2,6}$").Match(settings.Email).Success) + errors.Add("Email", Localization.Configuration.EmailFormat); + + if (string.IsNullOrEmpty(settings.Password) || settings.Password.Trim().Length == 0) + errors.Add("Password", Localization.Configuration.PasswordEmpty); + } + else if(settings.AuthenticationType == AuthenticationType.APIKey) + { + if (string.IsNullOrEmpty(settings.APIKey) || settings.APIKey.Trim().Length == 0) + errors.Add("APIKey", Localization.Configuration.APIKeyEmpty); + } + + if(errors.Count == 0) + { + + RegistryService.SetApplicationRegistryKeyValue("C", EncryptionService.EncryptString(settings.AccountName, ApplicationID)); + RegistryService.SetApplicationRegistryKeyValue("H", settings.MinimizeByClosing.ToString()); + RegistryService.SetApplicationRegistryKeyValue("I", settings.StartMinimized.ToString()); + RegistryService.SetApplicationRegistryKeyValue("L", settings.SelectedProjectID.ToString()); + RegistryService.SetApplicationRegistryKeyValue("M", settings.SelectedActivityID.ToString()); + RegistryService.SetApplicationRegistryKeyValue("N", settings.Culture); + RegistryService.SetApplicationRegistryKeyValue("O", settings.UseProxy.ToString()); + RegistryService.SetApplicationRegistryKeyValue("P", settings.ProxyServer); + RegistryService.SetApplicationRegistryKeyValue("Q", settings.ProxyPort.ToString()); + RegistryService.SetApplicationRegistryKeyValue("R", EncryptionService.EncryptString(settings.ProxyUser, ApplicationID)); + RegistryService.SetApplicationRegistryKeyValue("S", EncryptionService.EncryptString(settings.ProxyPassword, ApplicationID)); + RegistryService.SetApplicationRegistryKeyValue("T", settings.StopStopwatchByClosing.ToString()); + RegistryService.SetApplicationRegistryKeyValue("U", settings.AskForStoppingStopwatchByClosing.ToString()); + RegistryService.SetApplicationRegistryKeyValue("V", settings.SortTimeEntriesDescending.ToString()); + + if (settings.LastUpdateCheck > DateTime.MinValue) + RegistryService.SetApplicationRegistryKeyValue("K", settings.LastUpdateCheck.ToShortDateString()); + + if(settings.AuthenticationType == AuthenticationType.EmailAndPassword) + { + RegistryService.SetApplicationRegistryKeyValue("D", EncryptionService.EncryptString(settings.Email, ApplicationID)); + RegistryService.SetApplicationRegistryKeyValue("F", EncryptionService.EncryptString(settings.Password, ApplicationID)); + RegistryService.SetApplicationRegistryKeyValue("E", string.Empty); + } + else if (settings.AuthenticationType == AuthenticationType.APIKey) + { + RegistryService.SetApplicationRegistryKeyValue("D", string.Empty); + RegistryService.SetApplicationRegistryKeyValue("F", string.Empty); + RegistryService.SetApplicationRegistryKeyValue("E", EncryptionService.EncryptString(settings.APIKey, ApplicationID)); + } + + if (settings.Autostart) + RegistryService.SetApplicationRegistryKeyValue(@"Software\Microsoft\Windows\CurrentVersion\Run", "mite.desk", ExecutablePath); + else + RegistryService.DeleteApplicationRegistryKeyValue(@"Software\Microsoft\Windows\CurrentVersion\Run", "mite.desk"); + + } + + return errors; + + } + + } +} diff --git a/Core/MiteDesk.Core.Infrastructure/Configuration/IConfigurationService.cs b/Core/MiteDesk.Core.Infrastructure/Configuration/IConfigurationService.cs new file mode 100644 index 0000000..3d69362 --- /dev/null +++ b/Core/MiteDesk.Core.Infrastructure/Configuration/IConfigurationService.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using SixtyNineDegrees.MiteDesk.Core.Model; + +namespace SixtyNineDegrees.MiteDesk.Core.Infrastructure +{ + public interface IConfigurationService + { + AppSettings GetAppSettings(); + Dictionary UpdateAppSettings(AppSettings settings); + } +} diff --git a/Core/MiteDesk.Core.Infrastructure/Encryption/EncryptionService.cs b/Core/MiteDesk.Core.Infrastructure/Encryption/EncryptionService.cs new file mode 100644 index 0000000..f31646d --- /dev/null +++ b/Core/MiteDesk.Core.Infrastructure/Encryption/EncryptionService.cs @@ -0,0 +1,62 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace SixtyNineDegrees.MiteDesk.Core.Infrastructure +{ + + public class EncryptionService : IEncryptionService + { + + public string EncryptString(string clearText, string password) + { + if (string.IsNullOrEmpty(clearText)) + return clearText; + byte[] clearBytes = Encoding.Unicode.GetBytes(clearText); + Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 }); + byte[] encryptedData = EncryptString(clearBytes, pdb.GetBytes(32), pdb.GetBytes(16)); + return Convert.ToBase64String(encryptedData); + } + + private static byte[] EncryptString(byte[] clearText, byte[] key, byte[] iv) + { + MemoryStream ms = new MemoryStream(); + Rijndael alg = Rijndael.Create(); + alg.Key = key; + alg.IV = iv; + using (CryptoStream cs = new CryptoStream(ms, alg.CreateEncryptor(), CryptoStreamMode.Write)) + { + cs.Write(clearText, 0, clearText.Length); + } + byte[] encryptedData = ms.ToArray(); + return encryptedData; + } + + public string DecryptString(string cipherText, string password) + { + if (string.IsNullOrEmpty(cipherText)) + return null; + byte[] cipherBytes = Convert.FromBase64String(cipherText); + Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 }); + byte[] decryptedData = DecryptString(cipherBytes, pdb.GetBytes(32), pdb.GetBytes(16)); + return Encoding.Unicode.GetString(decryptedData); + } + + private static byte[] DecryptString(byte[] cipherData, byte[] key, byte[] iv) + { + MemoryStream ms = new MemoryStream(); + Rijndael alg = Rijndael.Create(); + alg.Key = key; + alg.IV = iv; + using (CryptoStream cs = new CryptoStream(ms, alg.CreateDecryptor(), CryptoStreamMode.Write)) + { + cs.Write(cipherData, 0, cipherData.Length); + } + byte[] decryptedData = ms.ToArray(); + return decryptedData; + } + + } + +} diff --git a/Core/MiteDesk.Core.Infrastructure/Encryption/IEncryptionService.cs b/Core/MiteDesk.Core.Infrastructure/Encryption/IEncryptionService.cs new file mode 100644 index 0000000..24fd599 --- /dev/null +++ b/Core/MiteDesk.Core.Infrastructure/Encryption/IEncryptionService.cs @@ -0,0 +1,8 @@ +namespace SixtyNineDegrees.MiteDesk.Core.Infrastructure +{ + public interface IEncryptionService + { + string EncryptString(string clearText, string password); + string DecryptString(string cipherText, string password); + } +} diff --git a/Core/MiteDesk.Core.Infrastructure/Localization/Configuration.Designer.cs b/Core/MiteDesk.Core.Infrastructure/Localization/Configuration.Designer.cs new file mode 100644 index 0000000..2dd4d08 --- /dev/null +++ b/Core/MiteDesk.Core.Infrastructure/Localization/Configuration.Designer.cs @@ -0,0 +1,108 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30128.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace SixtyNineDegrees.MiteDesk.Core.Infrastructure.Localization { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Configuration { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Configuration() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SixtyNineDegrees.MiteDesk.Core.Infrastructure.Localization.Configuration", typeof(Configuration).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Gib bitte den Account-Namen an.. + /// + internal static string AccountNameEmpty { + get { + return ResourceManager.GetString("AccountNameEmpty", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Bitte gib den API-Schlüssel an.. + /// + internal static string APIKeyEmpty { + get { + return ResourceManager.GetString("APIKeyEmpty", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Bitte gib E-Mail-Adresse an.. + /// + internal static string EmailEmpty { + get { + return ResourceManager.GetString("EmailEmpty", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Bitte gib eine korrekte E-Mail-Adresse an.. + /// + internal static string EmailFormat { + get { + return ResourceManager.GetString("EmailFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Bitte gib dein Passwort an.. + /// + internal static string PasswordEmpty { + get { + return ResourceManager.GetString("PasswordEmpty", resourceCulture); + } + } + } +} diff --git a/Core/MiteDesk.Core.Infrastructure/Localization/Configuration.en.resx b/Core/MiteDesk.Core.Infrastructure/Localization/Configuration.en.resx new file mode 100644 index 0000000..ff21dc7 --- /dev/null +++ b/Core/MiteDesk.Core.Infrastructure/Localization/Configuration.en.resx @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Please enter your account name + + + Please enter your API key + + + Please enter your email address + + + Please enter a correcty formatted email address + + + Please enter your password + + \ No newline at end of file diff --git a/Core/MiteDesk.Core.Infrastructure/Localization/Configuration.resx b/Core/MiteDesk.Core.Infrastructure/Localization/Configuration.resx new file mode 100644 index 0000000..4273def --- /dev/null +++ b/Core/MiteDesk.Core.Infrastructure/Localization/Configuration.resx @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Gib bitte den Account-Namen an. + + + Bitte gib den API-Schlüssel an. + + + Bitte gib E-Mail-Adresse an. + + + Bitte gib eine korrekte E-Mail-Adresse an. + + + Bitte gib dein Passwort an. + + \ No newline at end of file diff --git a/Core/MiteDesk.Core.Infrastructure/MiteDesk.Core.Infrastructure.csproj b/Core/MiteDesk.Core.Infrastructure/MiteDesk.Core.Infrastructure.csproj new file mode 100644 index 0000000..c6c74e4 --- /dev/null +++ b/Core/MiteDesk.Core.Infrastructure/MiteDesk.Core.Infrastructure.csproj @@ -0,0 +1,123 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {733C8DE9-36B5-4C9F-9701-FDD19B64B966} + Library + Properties + SixtyNineDegrees.MiteDesk.Core.Infrastructure + mite.desk.core.infrastructure + v3.5 + 512 + true + + + + + 3.5 + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AllRules.ruleset + + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + True + True + Configuration.resx + + + + + + + + {AE69F9FC-F9D4-41D8-9B00-1261F492FB89} + MiteDesk.Core.Model + + + + + + ResXFileCodeGenerator + Configuration.Designer.cs + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + \ No newline at end of file diff --git a/Core/MiteDesk.Core.Infrastructure/Properties/AssemblyInfo.cs b/Core/MiteDesk.Core.Infrastructure/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..adb4428 --- /dev/null +++ b/Core/MiteDesk.Core.Infrastructure/Properties/AssemblyInfo.cs @@ -0,0 +1,14 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("mite.desk.infrastructure")] +[assembly: AssemblyDescription("mite.desk.infrastructure")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("mite.desk")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] +[assembly: Guid("cd8b1326-809b-4366-b893-47d5fb176b70")] + +[assembly: AssemblyVersion("1.2.17")] +[assembly: AssemblyFileVersion("1.2.17")] diff --git a/Core/MiteDesk.Core.Infrastructure/Registry/IRegistryService.cs b/Core/MiteDesk.Core.Infrastructure/Registry/IRegistryService.cs new file mode 100644 index 0000000..ffd3325 --- /dev/null +++ b/Core/MiteDesk.Core.Infrastructure/Registry/IRegistryService.cs @@ -0,0 +1,15 @@ +using System; + +namespace SixtyNineDegrees.MiteDesk.Core.Infrastructure +{ + public interface IRegistryService + { + bool ApplicationRegistryKeyExists(); + void CreateApplicationRegistryKey(); + void SetApplicationRegistryKeyValue(string name, string value); + void SetApplicationRegistryKeyValue(string key, string name, string value); + void DeleteApplicationRegistryKeyValue(string key, string name); + string GetApplicationRegistryKeyValue(string key, string name); + string GetApplicationRegistryKeyValue(string name); + } +} diff --git a/Core/MiteDesk.Core.Infrastructure/Registry/RegistryService.cs b/Core/MiteDesk.Core.Infrastructure/Registry/RegistryService.cs new file mode 100644 index 0000000..6982c36 --- /dev/null +++ b/Core/MiteDesk.Core.Infrastructure/Registry/RegistryService.cs @@ -0,0 +1,53 @@ +using System; +using System.Globalization; +using System.Runtime.InteropServices; +using Microsoft.Win32; +using System.Text; + +namespace SixtyNineDegrees.MiteDesk.Core.Infrastructure +{ + public class RegistryService : IRegistryService + { + private const string RegistryKey = "Software\\69 Grad\\mite.desk"; + + public bool ApplicationRegistryKeyExists() + { + return Registry.CurrentUser.OpenSubKey(RegistryKey) != null; + } + + public void CreateApplicationRegistryKey() + { + Registry.CurrentUser.CreateSubKey(RegistryKey); + } + + public void SetApplicationRegistryKeyValue(string key, string name, string value) + { + var subKey = Registry.CurrentUser.OpenSubKey(key, true); + if (subKey == null) + Registry.CurrentUser.CreateSubKey(key); + subKey.SetValue(name, value ?? string.Empty); + } + + public void DeleteApplicationRegistryKeyValue(string key, string name) + { + var subKey = Registry.CurrentUser.OpenSubKey(key, true); + if (subKey != null && subKey.GetValue(name) != null) + subKey.DeleteValue(name); + } + + public string GetApplicationRegistryKeyValue(string key, string name) + { + return (string)Registry.CurrentUser.OpenSubKey(key).GetValue(name); + } + + public string GetApplicationRegistryKeyValue(string name) + { + return GetApplicationRegistryKeyValue(RegistryKey, name); + } + + public void SetApplicationRegistryKeyValue(string name, string value) + { + SetApplicationRegistryKeyValue(RegistryKey, name, value); + } + } +} diff --git a/Core/MiteDesk.Core.Model/Activity.cs b/Core/MiteDesk.Core.Model/Activity.cs new file mode 100644 index 0000000..7491114 --- /dev/null +++ b/Core/MiteDesk.Core.Model/Activity.cs @@ -0,0 +1,25 @@ +namespace SixtyNineDegrees.MiteDesk.Core.Model +{ + public class Activity + { + + public int ID { get; set; } + public string Name { get; set; } + public string Note { get; set; } + public int HourlyRate { get; set; } + public bool Billable { get; set; } + public bool Archived { get; set; } + + public override bool Equals(object obj) + { + var a = (Activity) obj; + return ID == a.ID && + Name == a.Name && + Note == a.Note && + HourlyRate == a.HourlyRate && + Billable == a.Billable && + Archived == a.Archived; + } + + } +} diff --git a/Core/MiteDesk.Core.Model/AppSettings.cs b/Core/MiteDesk.Core.Model/AppSettings.cs new file mode 100644 index 0000000..be9d72b --- /dev/null +++ b/Core/MiteDesk.Core.Model/AppSettings.cs @@ -0,0 +1,36 @@ +using System; + +namespace SixtyNineDegrees.MiteDesk.Core.Model +{ + + public class AppSettings + { + public string Culture { get; set; } + public int SelectedProjectID { get; set; } + public int SelectedActivityID { get; set; } + public DateTime LastUpdateCheck { get; set; } + public bool StartMinimized { get; set; } + public AuthenticationType AuthenticationType { get; set; } + public string AccountName { get; set; } + public string Email { get; set; } + public string Password { get; set; } + public string APIKey { get; set; } + public bool MinimizeByClosing { get; set; } + public bool Autostart { get; set; } + public bool UseProxy { get; set; } + public int ProxyPort { get; set; } + public string ProxyServer { get; set; } + public string ProxyUser { get; set; } + public string ProxyPassword { get; set; } + public bool StopStopwatchByClosing { get; set; } + public bool AskForStoppingStopwatchByClosing { get; set; } + public bool SortTimeEntriesDescending { get; set; } + } + + public enum AuthenticationType + { + EmailAndPassword = 0, + APIKey = 1 + } + +} diff --git a/Core/MiteDesk.Core.Model/Customer.cs b/Core/MiteDesk.Core.Model/Customer.cs new file mode 100644 index 0000000..467d1dc --- /dev/null +++ b/Core/MiteDesk.Core.Model/Customer.cs @@ -0,0 +1,10 @@ +namespace SixtyNineDegrees.MiteDesk.Core.Model +{ + public class Customer + { + public int ID { get; set; } + public string Name { get; set; } + public string Note { get; set; } + public bool Archived { get; set; } + } +} diff --git a/Core/MiteDesk.Core.Model/MiteDesk.Core.Model.csproj b/Core/MiteDesk.Core.Model/MiteDesk.Core.Model.csproj new file mode 100644 index 0000000..017607e --- /dev/null +++ b/Core/MiteDesk.Core.Model/MiteDesk.Core.Model.csproj @@ -0,0 +1,107 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {AE69F9FC-F9D4-41D8-9B00-1261F492FB89} + Library + Properties + SixtyNineDegrees.MiteDesk.Core.Model + mite.desk.core.model + v3.5 + 512 + true + + + + + 3.5 + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AllRules.ruleset + + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + \ No newline at end of file diff --git a/Core/MiteDesk.Core.Model/Project.cs b/Core/MiteDesk.Core.Model/Project.cs new file mode 100644 index 0000000..a39730c --- /dev/null +++ b/Core/MiteDesk.Core.Model/Project.cs @@ -0,0 +1,26 @@ +namespace SixtyNineDegrees.MiteDesk.Core.Model +{ + public class Project + { + public int ID { get; set; } + public string Name { get; set; } + public string Note { get; set; } + public string BudgetType { get; set; } + public int Budget { get; set; } + public bool Archived { get; set; } + public int CustomerID { get; set; } + public string CustomerName { get; set; } + + public override bool Equals(object obj) + { + var p = (Project)obj; + return ID == p.ID && + Name == p.Name && + Note == p.Note && + Budget == p.Budget && + BudgetType == p.BudgetType && + Archived == p.Archived; + } + + } +} diff --git a/Core/MiteDesk.Core.Model/Properties/AssemblyInfo.cs b/Core/MiteDesk.Core.Model/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..d0787bc --- /dev/null +++ b/Core/MiteDesk.Core.Model/Properties/AssemblyInfo.cs @@ -0,0 +1,14 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("mite.desk.model")] +[assembly: AssemblyDescription("mite.desk.model")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("mite.desk")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] +[assembly: Guid("a0d857f1-8fc9-4ffd-9f9d-89f099f5e212")] + +[assembly: AssemblyVersion("1.2.17")] +[assembly: AssemblyFileVersion("1.2.17")] diff --git a/Core/MiteDesk.Core.Model/TimeEntry.cs b/Core/MiteDesk.Core.Model/TimeEntry.cs new file mode 100644 index 0000000..cf89d8c --- /dev/null +++ b/Core/MiteDesk.Core.Model/TimeEntry.cs @@ -0,0 +1,18 @@ +using System; + +namespace SixtyNineDegrees.MiteDesk.Core.Model +{ + public class TimeEntry + { + public int ID { get; set; } + public DateTime Date { get; set; } + public int Minutes { get; set; } + public string Note { get; set; } + public int ProjectID { get; set; } + public int ActivityID { get; set; } + public string CustomerName { get; set; } + public string ProjectName { get; set; } + public string ActivityName { get; set; } + public bool Locked { get; set; } + } +} diff --git a/Core/MiteDesk.Core.Model/User.cs b/Core/MiteDesk.Core.Model/User.cs new file mode 100644 index 0000000..c8009ae --- /dev/null +++ b/Core/MiteDesk.Core.Model/User.cs @@ -0,0 +1,10 @@ +namespace SixtyNineDegrees.MiteDesk.Core.Model +{ + public class User + { + public int ID { get; set; } + public string Name { get; set; } + public string Email { get; set; } + public UserRole Role { get; set; } + } +} diff --git a/Core/MiteDesk.Core.Model/UserRole.cs b/Core/MiteDesk.Core.Model/UserRole.cs new file mode 100644 index 0000000..5162282 --- /dev/null +++ b/Core/MiteDesk.Core.Model/UserRole.cs @@ -0,0 +1,10 @@ +namespace SixtyNineDegrees.MiteDesk.Core.Model +{ + public enum UserRole + { + TimeTracker = 0, + CoWorker = 1, + Admin = 2, + Owner = 3 + } +} diff --git a/Core/MiteDesk.Core.Model/UserRoleInvalidException.cs b/Core/MiteDesk.Core.Model/UserRoleInvalidException.cs new file mode 100644 index 0000000..cbcfb34 --- /dev/null +++ b/Core/MiteDesk.Core.Model/UserRoleInvalidException.cs @@ -0,0 +1,8 @@ +using System; + +namespace SixtyNineDegrees.MiteDesk.Core.Model +{ + public class UserRoleInvalidException : Exception + { + } +} diff --git a/Core/MiteDesk.Core.Services/Activities/ActivityService.cs b/Core/MiteDesk.Core.Services/Activities/ActivityService.cs new file mode 100644 index 0000000..ecb4191 --- /dev/null +++ b/Core/MiteDesk.Core.Services/Activities/ActivityService.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using SixtyNineDegrees.MiteDesk.Core.Data; +using SixtyNineDegrees.MiteDesk.Core.Model; + +namespace SixtyNineDegrees.MiteDesk.Core.Services +{ + public class ActivityService : IActivityService + { + + public ActivityService(IActivityRepository repository) + { + Repository = repository; + } + + private readonly IActivityRepository Repository; + + public IList GetAllActiveActivities() + { + return Repository.GetAllActiveActivities(); + } + + public IList GetAllArchivedActivities() + { + return Repository.GetAllArchivedActivities(); + } + + public Dictionary CreateActivity(Activity activity, string hourlyRate) + { + var errors = ValidateActivity(activity); + if (errors.Count == 0) + { + activity.HourlyRate = ParseHourlyRate(hourlyRate); + Repository.CreateActivity(activity); + } + return errors; + } + + public Dictionary UpdateActivity(Activity activity, string hourlyRate) + { + if (activity.ID == 0) + throw new ArgumentException(Localization.Activities.IDException, "activity"); + + var errors = ValidateActivity(activity); + if (errors.Count == 0) + { + activity.HourlyRate = ParseHourlyRate(hourlyRate); + Repository.UpdateActivity(activity); + } + + return errors; + } + + private static Dictionary ValidateActivity(Activity activity) + { + var errors = new Dictionary(); + if (string.IsNullOrEmpty(activity.Name) || activity.Name.Trim().Length == 0) + { + errors.Add("Name", Localization.Activities.NameEmpty); + } + return errors; + } + + private static int ParseHourlyRate(string value) + { + + int rate = 0; + + if (string.IsNullOrEmpty(value) || value.Trim().Length == 0) + return rate; + + + if(value.IndexOf(',') > -1) + { + + int first, second; + + int.TryParse(value.Split(',')[0], out first); + + if (value.Split(',')[1].Length <= 2) + { + int.TryParse(value.Split(',')[1], out second); + } + else + { + decimal tmp; + decimal.TryParse(value, out tmp); + tmp = Math.Round(tmp, 2); + int.TryParse(tmp.ToString().Split(',')[1], out second); + } + + rate = (first*100) + second; + + } + else + { + int.TryParse(value, out rate); + if(rate > 0) + { + rate = rate * 100; + } + } + + return rate; + + } + + public Activity GetActivityByID(int activityID) + { + return Repository.GetActivityByID(activityID); + } + + public void DeleteActivity(int activityID) + { + Repository.DeleteActivity(activityID); + } + } +} diff --git a/Core/MiteDesk.Core.Services/Activities/IActivityService.cs b/Core/MiteDesk.Core.Services/Activities/IActivityService.cs new file mode 100644 index 0000000..5166c14 --- /dev/null +++ b/Core/MiteDesk.Core.Services/Activities/IActivityService.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using SixtyNineDegrees.MiteDesk.Core.Model; + +namespace SixtyNineDegrees.MiteDesk.Core.Services +{ + public interface IActivityService + { + IList GetAllActiveActivities(); + IList GetAllArchivedActivities(); + Dictionary CreateActivity(Activity activity, string hourlyRate); + Dictionary UpdateActivity(Activity activity, string hourlyRate); + Activity GetActivityByID(int activityID); + void DeleteActivity(int activityID); + } +} \ No newline at end of file diff --git a/Core/MiteDesk.Core.Services/Authentication/AuthenticationService.cs b/Core/MiteDesk.Core.Services/Authentication/AuthenticationService.cs new file mode 100644 index 0000000..58eff72 --- /dev/null +++ b/Core/MiteDesk.Core.Services/Authentication/AuthenticationService.cs @@ -0,0 +1,21 @@ +using SixtyNineDegrees.MiteDesk.Core.Data; +using SixtyNineDegrees.MiteDesk.Core.Model; + +namespace SixtyNineDegrees.MiteDesk.Core.Services +{ + public class AuthenticationService : IAuthenticationService + { + + public AuthenticationService(IUserRepository userRepository) + { + UserRepository = userRepository; + } + + private readonly IUserRepository UserRepository; + + public User GetAuthenticatedUser() + { + return UserRepository.GetAuthenticatedUser(); + } + } +} diff --git a/Core/MiteDesk.Core.Services/Authentication/IAuthenticationService.cs b/Core/MiteDesk.Core.Services/Authentication/IAuthenticationService.cs new file mode 100644 index 0000000..eabf917 --- /dev/null +++ b/Core/MiteDesk.Core.Services/Authentication/IAuthenticationService.cs @@ -0,0 +1,9 @@ +using SixtyNineDegrees.MiteDesk.Core.Model; + +namespace SixtyNineDegrees.MiteDesk.Core.Services +{ + public interface IAuthenticationService + { + User GetAuthenticatedUser(); + } +} diff --git a/Core/MiteDesk.Core.Services/Customers/CustomerService.cs b/Core/MiteDesk.Core.Services/Customers/CustomerService.cs new file mode 100644 index 0000000..0e35d6d --- /dev/null +++ b/Core/MiteDesk.Core.Services/Customers/CustomerService.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using SixtyNineDegrees.MiteDesk.Core.Data; +using SixtyNineDegrees.MiteDesk.Core.Model; + +namespace SixtyNineDegrees.MiteDesk.Core.Services +{ + public class CustomerService : ICustomerService + { + + public CustomerService(ICustomerRepository repository) + { + Repository = repository; + } + + private readonly ICustomerRepository Repository; + + public IList GetAllActiveCustomers() + { + return Repository.GetAllActiveCustomers(); + } + + public IList GetAllArchivedCustomers() + { + return Repository.GetAllArchivedCustomers(); + } + + public Dictionary CreateCustomer(Customer customer) + { + var errors = ValidateCustomer(customer); + if (errors.Count == 0) + Repository.CreateCustomer(customer); + return errors; + } + + public Dictionary UpdateCustomer(Customer customer) + { + if (customer.ID == 0) + throw new ArgumentException(Localization.Customers.IDException, "customer"); + + var errors = ValidateCustomer(customer); + if (errors.Count == 0) + Repository.UpdateCustomer(customer); + + return errors; + } + + private static Dictionary ValidateCustomer(Customer customer) + { + var errors = new Dictionary(); + if (string.IsNullOrEmpty(customer.Name) || customer.Name.Trim().Length == 0) + { + errors.Add("Name", Localization.Customers.NameEmpty); + } + return errors; + } + + public Customer GetCustomerByID(int customerID) + { + return Repository.GetCustomerByID(customerID); + } + + public void DeleteCustomer(int customerID) + { + Repository.DeleteCustomer(customerID); + } + + } +} diff --git a/Core/MiteDesk.Core.Services/Customers/ICustomerService.cs b/Core/MiteDesk.Core.Services/Customers/ICustomerService.cs new file mode 100644 index 0000000..b05b5f9 --- /dev/null +++ b/Core/MiteDesk.Core.Services/Customers/ICustomerService.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using SixtyNineDegrees.MiteDesk.Core.Model; + +namespace SixtyNineDegrees.MiteDesk.Core.Services +{ + public interface ICustomerService + { + IList GetAllActiveCustomers(); + IList GetAllArchivedCustomers(); + Dictionary CreateCustomer(Customer customer); + Dictionary UpdateCustomer(Customer customer); + Customer GetCustomerByID(int customerID); + void DeleteCustomer(int customerID); + } +} \ No newline at end of file diff --git a/Core/MiteDesk.Core.Services/Localization/Activities.Designer.cs b/Core/MiteDesk.Core.Services/Localization/Activities.Designer.cs new file mode 100644 index 0000000..67fbd04 --- /dev/null +++ b/Core/MiteDesk.Core.Services/Localization/Activities.Designer.cs @@ -0,0 +1,81 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30128.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace SixtyNineDegrees.MiteDesk.Core.Services.Localization { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Activities { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Activities() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SixtyNineDegrees.MiteDesk.Core.Services.Localization.Activities", typeof(Activities).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Die ID der Leistung darf nicht 0 sein. + /// + internal static string IDException { + get { + return ResourceManager.GetString("IDException", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Der angegebene Name ist ungültig.. + /// + internal static string NameEmpty { + get { + return ResourceManager.GetString("NameEmpty", resourceCulture); + } + } + } +} diff --git a/Core/MiteDesk.Core.Services/Localization/Activities.en.resx b/Core/MiteDesk.Core.Services/Localization/Activities.en.resx new file mode 100644 index 0000000..5344f20 --- /dev/null +++ b/Core/MiteDesk.Core.Services/Localization/Activities.en.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ID of service may not be 0. + + + Please enter a valid name + + \ No newline at end of file diff --git a/Core/MiteDesk.Core.Services/Localization/Activities.resx b/Core/MiteDesk.Core.Services/Localization/Activities.resx new file mode 100644 index 0000000..db393aa --- /dev/null +++ b/Core/MiteDesk.Core.Services/Localization/Activities.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Die ID der Leistung darf nicht 0 sein + + + Der angegebene Name ist ungültig. + + \ No newline at end of file diff --git a/Core/MiteDesk.Core.Services/Localization/Customers.Designer.cs b/Core/MiteDesk.Core.Services/Localization/Customers.Designer.cs new file mode 100644 index 0000000..040618c --- /dev/null +++ b/Core/MiteDesk.Core.Services/Localization/Customers.Designer.cs @@ -0,0 +1,81 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30128.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace SixtyNineDegrees.MiteDesk.Core.Services.Localization { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Customers { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Customers() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SixtyNineDegrees.MiteDesk.Core.Services.Localization.Customers", typeof(Customers).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Die ID des Kunden darf nicht 0 sein. + /// + internal static string IDException { + get { + return ResourceManager.GetString("IDException", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Der angegebene Name ist ungültig.. + /// + internal static string NameEmpty { + get { + return ResourceManager.GetString("NameEmpty", resourceCulture); + } + } + } +} diff --git a/Core/MiteDesk.Core.Services/Localization/Customers.en.resx b/Core/MiteDesk.Core.Services/Localization/Customers.en.resx new file mode 100644 index 0000000..06c7dcd --- /dev/null +++ b/Core/MiteDesk.Core.Services/Localization/Customers.en.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ID of customer may not be 0. + + + Please enter a valid name + + \ No newline at end of file diff --git a/Core/MiteDesk.Core.Services/Localization/Customers.resx b/Core/MiteDesk.Core.Services/Localization/Customers.resx new file mode 100644 index 0000000..db6cab0 --- /dev/null +++ b/Core/MiteDesk.Core.Services/Localization/Customers.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Die ID des Kunden darf nicht 0 sein + + + Der angegebene Name ist ungültig. + + \ No newline at end of file diff --git a/Core/MiteDesk.Core.Services/Localization/Projects.Designer.cs b/Core/MiteDesk.Core.Services/Localization/Projects.Designer.cs new file mode 100644 index 0000000..a4c77e6 --- /dev/null +++ b/Core/MiteDesk.Core.Services/Localization/Projects.Designer.cs @@ -0,0 +1,81 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30128.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace SixtyNineDegrees.MiteDesk.Core.Services.Localization { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Projects { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Projects() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SixtyNineDegrees.MiteDesk.Core.Services.Localization.Projects", typeof(Projects).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Die ID des Projektes darf nicht 0 sein. + /// + internal static string IDException { + get { + return ResourceManager.GetString("IDException", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Der angegebene Name ist ungültig.. + /// + internal static string NameEmpty { + get { + return ResourceManager.GetString("NameEmpty", resourceCulture); + } + } + } +} diff --git a/Core/MiteDesk.Core.Services/Localization/Projects.en.resx b/Core/MiteDesk.Core.Services/Localization/Projects.en.resx new file mode 100644 index 0000000..c0a2883 --- /dev/null +++ b/Core/MiteDesk.Core.Services/Localization/Projects.en.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ID of project may not be 0. + + + Please enter a valid name + + \ No newline at end of file diff --git a/Core/MiteDesk.Core.Services/Localization/Projects.resx b/Core/MiteDesk.Core.Services/Localization/Projects.resx new file mode 100644 index 0000000..c18b36d --- /dev/null +++ b/Core/MiteDesk.Core.Services/Localization/Projects.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Die ID des Projektes darf nicht 0 sein + + + Der angegebene Name ist ungültig. + + \ No newline at end of file diff --git a/Core/MiteDesk.Core.Services/Localization/TimeEntries.Designer.cs b/Core/MiteDesk.Core.Services/Localization/TimeEntries.Designer.cs new file mode 100644 index 0000000..0539d82 --- /dev/null +++ b/Core/MiteDesk.Core.Services/Localization/TimeEntries.Designer.cs @@ -0,0 +1,99 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30128.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace SixtyNineDegrees.MiteDesk.Core.Services.Localization { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class TimeEntries { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal TimeEntries() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SixtyNineDegrees.MiteDesk.Core.Services.Localization.TimeEntries", typeof(TimeEntries).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Die ausgewählte Leistung konnte nicht gefunden werden.. + /// + internal static string ActivityID { + get { + return ResourceManager.GetString("ActivityID", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Das angegebene Datum ist ungültig.. + /// + internal static string Date { + get { + return ResourceManager.GetString("Date", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Das ausgewählte Projekt konnte nicht gefunden werden.. + /// + internal static string ProjectID { + get { + return ResourceManager.GetString("ProjectID", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Bitte gib eine korrekt formatierte Zeit an.. + /// + internal static string Time { + get { + return ResourceManager.GetString("Time", resourceCulture); + } + } + } +} diff --git a/Core/MiteDesk.Core.Services/Localization/TimeEntries.en.resx b/Core/MiteDesk.Core.Services/Localization/TimeEntries.en.resx new file mode 100644 index 0000000..660a934 --- /dev/null +++ b/Core/MiteDesk.Core.Services/Localization/TimeEntries.en.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The service you selected could not be found + + + Please enter a valid date + + + The project you selected could not be found + + + Please enter a well formatted time + + \ No newline at end of file diff --git a/Core/MiteDesk.Core.Services/Localization/TimeEntries.resx b/Core/MiteDesk.Core.Services/Localization/TimeEntries.resx new file mode 100644 index 0000000..b9426de --- /dev/null +++ b/Core/MiteDesk.Core.Services/Localization/TimeEntries.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Die ausgewählte Leistung konnte nicht gefunden werden. + + + Das angegebene Datum ist ungültig. + + + Das ausgewählte Projekt konnte nicht gefunden werden. + + + Bitte gib eine korrekt formatierte Zeit an. + + \ No newline at end of file diff --git a/Core/MiteDesk.Core.Services/MiteDesk.Core.Services.csproj b/Core/MiteDesk.Core.Services/MiteDesk.Core.Services.csproj new file mode 100644 index 0000000..b9b9c4e --- /dev/null +++ b/Core/MiteDesk.Core.Services/MiteDesk.Core.Services.csproj @@ -0,0 +1,166 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {B7B67B5C-47F1-4E3D-8759-EA8E31AC951E} + Library + Properties + SixtyNineDegrees.MiteDesk.Core.Services + mite.desk.core.services + v3.5 + 512 + true + + + + + 3.5 + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AllRules.ruleset + + + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + + + True + True + Activities.resx + + + True + True + Customers.resx + + + True + True + Projects.resx + + + True + True + TimeEntries.resx + + + + + + + + + + {AD8740D8-14A7-4A31-8F96-7DF850D3D957} + MiteDesk.Core.Data + + + {733C8DE9-36B5-4C9F-9701-FDD19B64B966} + MiteDesk.Core.Infrastructure + + + {AE69F9FC-F9D4-41D8-9B00-1261F492FB89} + MiteDesk.Core.Model + + + + + + ResXFileCodeGenerator + Activities.Designer.cs + + + + ResXFileCodeGenerator + Customers.Designer.cs + + + + ResXFileCodeGenerator + Projects.Designer.cs + + + + ResXFileCodeGenerator + TimeEntries.Designer.cs + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + \ No newline at end of file diff --git a/Core/MiteDesk.Core.Services/Projects/IProjectService.cs b/Core/MiteDesk.Core.Services/Projects/IProjectService.cs new file mode 100644 index 0000000..146bdf9 --- /dev/null +++ b/Core/MiteDesk.Core.Services/Projects/IProjectService.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using SixtyNineDegrees.MiteDesk.Core.Model; + +namespace SixtyNineDegrees.MiteDesk.Core.Services +{ + public interface IProjectService + { + IList GetAllActiveProjects(); + IList GetAllArchivedProjects(); + IList GetProjectsByCustomer(int customerID); + Project GetProjectByID(int projectID); + Dictionary CreateProject(Project project, string budget); + Dictionary UpdateProject(Project project, string budget); + void DeleteProject(int projectID); + } +} \ No newline at end of file diff --git a/Core/MiteDesk.Core.Services/Projects/ProjectService.cs b/Core/MiteDesk.Core.Services/Projects/ProjectService.cs new file mode 100644 index 0000000..4686396 --- /dev/null +++ b/Core/MiteDesk.Core.Services/Projects/ProjectService.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections.Generic; +using SixtyNineDegrees.MiteDesk.Core.Data; +using SixtyNineDegrees.MiteDesk.Core.Model; + +namespace SixtyNineDegrees.MiteDesk.Core.Services +{ + public class ProjectService : IProjectService + { + + public ProjectService(IProjectRepository repository) + { + Repository = repository; + } + + private readonly IProjectRepository Repository; + + public IList GetAllActiveProjects() + { + return Repository.GetAllActiveProjects(); + } + + public IList GetProjectsByCustomer(int customerID) + { + return Repository.GetProjectsByCustomer(customerID); + } + + public IList GetAllArchivedProjects() + { + return Repository.GetAllArchivedProjects(); + } + + public Project GetProjectByID(int projectID) + { + return Repository.GetProjectByID(projectID); + } + + public Dictionary CreateProject(Project project, string budget) + { + var errors = ValidateProject(project); + if (errors.Count == 0) + { + project.Budget = ParseBudget(budget, project.BudgetType); + Repository.CreateProject(project); + } + return errors; + } + + public Dictionary UpdateProject(Project project, string budget) + { + if (project.ID == 0) + throw new ArgumentException(Localization.Projects.IDException, "project"); + + var errors = ValidateProject(project); + if (errors.Count == 0) + { + project.Budget = ParseBudget(budget, project.BudgetType); + Repository.UpdateProject(project); + } + + return errors; + } + + private static Dictionary ValidateProject(Project project) + { + var errors = new Dictionary(); + if (string.IsNullOrEmpty(project.Name) || project.Name.Trim().Length == 0) + { + errors.Add("Name", Localization.Projects.NameEmpty); + } + return errors; + } + + public void DeleteProject(int projectID) + { + Repository.DeleteProject(projectID); + } + + private static int ParseBudget(string value, string type) + { + + // mal aufräumen ... + + int budget = 0; + + if(type == "minutes") + { + + if (!string.IsNullOrEmpty(value)) + { + if (value.IndexOf(':') > 0) + { + string[] tmpTime = value.Split(':'); + int hours, minutes; + if (int.TryParse(tmpTime[0], out hours) && int.TryParse(tmpTime[1], out minutes)) + { + budget = 60*hours + minutes; + } + } + else + { + int.TryParse(value, out budget); + budget = budget*60; + } + } + + return budget; + + } + + if (string.IsNullOrEmpty(value) || value.Trim().Length == 0) + return budget; + + + if (value.IndexOf(',') > -1) + { + + int first, second; + + int.TryParse(value.Split(',')[0], out first); + + if (value.Split(',')[1].Length <= 2) + { + int.TryParse(value.Split(',')[1], out second); + } + else + { + decimal tmp; + decimal.TryParse(value, out tmp); + tmp = Math.Round(tmp, 2); + int.TryParse(tmp.ToString().Split(',')[1], out second); + } + + budget = (first * 100) + second; + + } + else + { + int.TryParse(value, out budget); + if (budget > 0) + { + budget = budget * 100; + } + } + + return budget; + + } + + } +} diff --git a/Core/MiteDesk.Core.Services/Properties/AssemblyInfo.cs b/Core/MiteDesk.Core.Services/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..c4c85b0 --- /dev/null +++ b/Core/MiteDesk.Core.Services/Properties/AssemblyInfo.cs @@ -0,0 +1,14 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("mite.desk.services")] +[assembly: AssemblyDescription("mite.desk.services")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("mite.desk")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] +[assembly: Guid("f2048a59-65e3-4b83-ba99-66479ff3336c")] + +[assembly: AssemblyVersion("1.2.17")] +[assembly: AssemblyFileVersion("1.2.17")] diff --git a/Core/MiteDesk.Core.Services/TimeEntries/ITimeEntryService.cs b/Core/MiteDesk.Core.Services/TimeEntries/ITimeEntryService.cs new file mode 100644 index 0000000..f9374fc --- /dev/null +++ b/Core/MiteDesk.Core.Services/TimeEntries/ITimeEntryService.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using SixtyNineDegrees.MiteDesk.Core.Model; + +namespace SixtyNineDegrees.MiteDesk.Core.Services +{ + public interface ITimeEntryService + { + Dictionary CreateTimeEntry(ref TimeEntry entry, string timeText); + Dictionary UpdateTimeEntry(TimeEntry entry, string timeText); + Dictionary ValidateTimeEntry(TimeEntry entry, string timeText); + IList GetTimeEntriesByDate(DateTime date); + IList GetTimeEntriesByActivityID(int activityID); + IList GetTimeEntriesByProjectID(int projectID); + IList GetTimeEntryDatesByRange(DateTime start, DateTime end, int userID); + TimeEntry GetTimeEntryByID(int timeEntryID); + TimeEntry GetTimeEntryCurrentlyTrackedByStopwatch(); + void DeleteTimeEntry(int timeEntryID); + void StartStopwatch(int timeEntryID); + void StopStopwatch(int timeEntryID); + } +} \ No newline at end of file diff --git a/Core/MiteDesk.Core.Services/TimeEntries/TimeEntryService.cs b/Core/MiteDesk.Core.Services/TimeEntries/TimeEntryService.cs new file mode 100644 index 0000000..c5e15db --- /dev/null +++ b/Core/MiteDesk.Core.Services/TimeEntries/TimeEntryService.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using SixtyNineDegrees.MiteDesk.Core.Data; +using SixtyNineDegrees.MiteDesk.Core.Model; + +namespace SixtyNineDegrees.MiteDesk.Core.Services +{ + public class TimeEntryService : ITimeEntryService + { + + public TimeEntryService(ITimeEntryRepository repository, IProjectService projectService, IActivityService activityService) + { + Repository = repository; + ProjectService = projectService; + ActivityService = activityService; + } + + private readonly ITimeEntryRepository Repository; + private readonly IProjectService ProjectService; + private readonly IActivityService ActivityService; + + public Dictionary CreateTimeEntry(ref TimeEntry entry, string timeText) + { + Dictionary errors = ValidateTimeEntry(entry, timeText); + if(errors.Count == 0) + { + Repository.CreateTimeEntry(ref entry); + } + return errors; + } + + public Dictionary UpdateTimeEntry(TimeEntry entry, string timeText) + { + Dictionary errors = ValidateTimeEntry(entry, timeText); + if (errors.Count == 0) + { + Repository.UpdateTimeEntry(entry); + } + return errors; + + } + + public Dictionary ValidateTimeEntry(TimeEntry entry, string timeText) + { + Dictionary errors = new Dictionary(); + + int projectID = entry.ProjectID; + int activityID = entry.ActivityID; + + // Projekt verfügbar? + if (entry.ProjectID != 0 && !ProjectService.GetAllActiveProjects().Any(p => p.ID == projectID)) + { + errors.Add("ProjectID", Localization.TimeEntries.ProjectID); + } + + // Leistung verfügbar? + if (entry.ActivityID != 0 && !ActivityService.GetAllActiveActivities().Any(a => a.ID == activityID)) + { + errors.Add("ActivityID", Localization.TimeEntries.ActivityID); + } + + // Datum angegeben? + if(entry.Date == DateTime.MinValue) + { + errors.Add("Date", Localization.TimeEntries.Date); + } + + // Zeit richtig formatiert? + bool timeWellFormatted = false; + if(!string.IsNullOrEmpty(timeText)) + { + int time; + decimal decTime; + timeText = timeText.Replace(".", ","); + if (timeText.IndexOf(':') > 0) + { + string[] tmpTime = timeText.Split(':'); + int hours, minutes; + if (int.TryParse(tmpTime[0], out hours) && int.TryParse(tmpTime[1], out minutes)) + { + timeWellFormatted = true; + entry.Minutes = 60*hours + minutes; + } + } + else if (decimal.TryParse(timeText, out decTime)) + { + timeWellFormatted = true; + entry.Minutes = (int)(decTime * 60); + } + else if(int.TryParse(timeText, out time)) + { + timeWellFormatted = true; + entry.Minutes = time*60; + } + } + else + { + timeWellFormatted = true; + entry.Minutes = 0; + } + + if(!timeWellFormatted) + { + errors.Add("Time", Localization.TimeEntries.Time); + } + return errors; + + } + + public IList GetTimeEntriesByDate(DateTime date) + { + return Repository.GetTimeEntriesByDate(date); + } + + public IList GetTimeEntriesByActivityID(int activityID) + { + return Repository.GetTimeEntriesByActivityID(activityID); + } + + public IList GetTimeEntriesByProjectID(int projectID) + { + return Repository.GetTimeEntriesByProjectID(projectID); + } + + public IList GetTimeEntryDatesByRange(DateTime start, DateTime end, int userID) + { + return Repository.GetTimeEntryDatesByRange(start, end, userID); + } + + public TimeEntry GetTimeEntryByID(int id) + { + return Repository.GetTimeEntryByID(id); + } + + public TimeEntry GetTimeEntryCurrentlyTrackedByStopwatch() + { + return Repository.GetTimeEntryCurrentlyTrackedByStopwatch(); + } + + public void DeleteTimeEntry(int id) + { + Repository.DeleteTimeEntry(id); + } + + public void StartStopwatch(int timeEntryID) + { + Repository.StartStopwatch(timeEntryID); + } + + public void StopStopwatch(int timeEntryID) + { + Repository.StopStopwatch(timeEntryID); + } + + } +} diff --git a/Core/MiteDesk.Core.Services/Users/IUserService.cs b/Core/MiteDesk.Core.Services/Users/IUserService.cs new file mode 100644 index 0000000..47e4a39 --- /dev/null +++ b/Core/MiteDesk.Core.Services/Users/IUserService.cs @@ -0,0 +1,16 @@ +/**************************************************************** + * Copyright (c) 2009 69° media solutions - All rights reserved * + * 69 Grad OHG - Forsterstraße 100 - 90441 Nuernberg - Germany * + ***************************************************************/ + +using System.Collections.Generic; +using WinMite.Core.Model; + +namespace WinMite.Core.Services +{ + public interface IUserService + { + User GetUser(); + Dictionary UpdateUser(User user); + } +} diff --git a/Core/MiteDesk.Core.Services/Users/UserService.cs b/Core/MiteDesk.Core.Services/Users/UserService.cs new file mode 100644 index 0000000..106906a --- /dev/null +++ b/Core/MiteDesk.Core.Services/Users/UserService.cs @@ -0,0 +1,58 @@ +/**************************************************************** + * Copyright (c) 2009 69° media solutions - All rights reserved * + * 69 Grad OHG - Forsterstraße 100 - 90441 Nuernberg - Germany * + ***************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using WinMite.Core.Data; +using WinMite.Core.Model; + +namespace WinMite.Core.Services +{ + public class UserService : IUserService + { + + public UserService(IUserRepository repository) + { + Repository = repository; + } + + private readonly IUserRepository Repository; + + public User GetUser() + { + return Repository.GetUser(); + } + + public Dictionary UpdateUser(User user) + { + + Dictionary errors = new Dictionary(); + + // Accountname angegeben? + if(string.IsNullOrEmpty(user.AccountName) || user.AccountName.Trim().Length == 0) + errors.Add("AccountName", "Bitte geben Sie den Account-Namen an."); + + // E-Mail-Adresse okay? + if (string.IsNullOrEmpty(user.Email) || user.Email.Trim().Length == 0) + errors.Add("Email", "Bitte geben Sie Ihre E-Mail-Adresse an."); + else if (!new Regex(@"^[\w\.-]+@[\w\.-]+\.[a-zA-Z]{2,6}$").Match(user.Email).Success) + errors.Add("Email", "Bitte geben Sie eine korrekte E-Mail-Adresse an."); + + // Passwort angegeben? + if (string.IsNullOrEmpty(user.Password) || user.Password.Trim().Length == 0) + errors.Add("Password", "Bitte geben Sie Ihr Passwort an."); + + if(errors.Count == 0) + { + Repository.UpdateUser(user); + } + + return errors; + + } + + } +} diff --git a/Deployment/MiteDesk.WinForms.Setup/MiteDesk.WinForms.Setup.vdproj b/Deployment/MiteDesk.WinForms.Setup/MiteDesk.WinForms.Setup.vdproj new file mode 100644 index 0000000..67f0e8c --- /dev/null +++ b/Deployment/MiteDesk.WinForms.Setup/MiteDesk.WinForms.Setup.vdproj @@ -0,0 +1,1235 @@ +"DeployProject" +{ +"VSVersion" = "3:800" +"ProjectType" = "8:{978C614F-708E-4E1A-B201-565925725DBA}" +"IsWebType" = "8:FALSE" +"ProjectName" = "8:MiteDesk.WinForms.Setup" +"LanguageId" = "3:1031" +"CodePage" = "3:1252" +"UILanguageId" = "3:1031" +"SccProjectName" = "8:" +"SccLocalPath" = "8:" +"SccAuxPath" = "8:" +"SccProvider" = "8:" + "Hierarchy" + { + "Entry" + { + "MsmKey" = "8:_04A8B693F44A4C2AA60FD814913E3D08" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_2D7003A1F920EF9EC3E2849B8E78110A" + "OwnerKey" = "8:_A1D793DFCEAC4C2632C82E16A2294CBE" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_2D7003A1F920EF9EC3E2849B8E78110A" + "OwnerKey" = "8:_CE68243731A7F382932200AC685A832B" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_2D7003A1F920EF9EC3E2849B8E78110A" + "OwnerKey" = "8:_04A8B693F44A4C2AA60FD814913E3D08" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_2D7003A1F920EF9EC3E2849B8E78110A" + "OwnerKey" = "8:_390D2894D6B8216C73514AD70EE9C970" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_2D7003A1F920EF9EC3E2849B8E78110A" + "OwnerKey" = "8:_FCBAAC4F50103C640B2F5DF5201DEC97" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_390D2894D6B8216C73514AD70EE9C970" + "OwnerKey" = "8:_FCBAAC4F50103C640B2F5DF5201DEC97" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_390D2894D6B8216C73514AD70EE9C970" + "OwnerKey" = "8:_04A8B693F44A4C2AA60FD814913E3D08" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_4BA3E6D6E6F0507A83D364988B9EC467" + "OwnerKey" = "8:_04A8B693F44A4C2AA60FD814913E3D08" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_665DDB4A7B834028B01E82C06ADCCC2A" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_7684FBF17F1A472588EF44035BEB78DE" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_A1D793DFCEAC4C2632C82E16A2294CBE" + "OwnerKey" = "8:_FCBAAC4F50103C640B2F5DF5201DEC97" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_A1D793DFCEAC4C2632C82E16A2294CBE" + "OwnerKey" = "8:_04A8B693F44A4C2AA60FD814913E3D08" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_B2002D4BCE4648F5A834FA62822D3BF3" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_CE68243731A7F382932200AC685A832B" + "OwnerKey" = "8:_04A8B693F44A4C2AA60FD814913E3D08" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_FCBAAC4F50103C640B2F5DF5201DEC97" + "OwnerKey" = "8:_04A8B693F44A4C2AA60FD814913E3D08" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_FCBAAC4F50103C640B2F5DF5201DEC97" + "OwnerKey" = "8:_CE68243731A7F382932200AC685A832B" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_UNDEFINED" + "OwnerKey" = "8:_04A8B693F44A4C2AA60FD814913E3D08" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_UNDEFINED" + "OwnerKey" = "8:_4BA3E6D6E6F0507A83D364988B9EC467" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_UNDEFINED" + "OwnerKey" = "8:_CE68243731A7F382932200AC685A832B" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_UNDEFINED" + "OwnerKey" = "8:_FCBAAC4F50103C640B2F5DF5201DEC97" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_UNDEFINED" + "OwnerKey" = "8:_390D2894D6B8216C73514AD70EE9C970" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_UNDEFINED" + "OwnerKey" = "8:_A1D793DFCEAC4C2632C82E16A2294CBE" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_UNDEFINED" + "OwnerKey" = "8:_2D7003A1F920EF9EC3E2849B8E78110A" + "MsmSig" = "8:_UNDEFINED" + } + } + "Configurations" + { + "Debug" + { + "DisplayName" = "8:Debug" + "IsDebugOnly" = "11:TRUE" + "IsReleaseOnly" = "11:FALSE" + "OutputFilename" = "8:Debug\\mite.desk.msi" + "PackageFilesAs" = "3:2" + "PackageFileSize" = "3:-2147483648" + "CabType" = "3:1" + "Compression" = "3:3" + "SignOutput" = "11:FALSE" + "CertificateFile" = "8:" + "PrivateKeyFile" = "8:" + "TimeStampServer" = "8:" + "InstallerBootstrapper" = "3:2" + "BootstrapperCfg:{63ACBE69-63AA-4F98-B2B6-99F9E24495F2}" + { + "Enabled" = "11:TRUE" + "PromptEnabled" = "11:TRUE" + "PrerequisitesLocation" = "2:1" + "Url" = "8:" + "ComponentsUrl" = "8:" + "Items" + { + "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:Microsoft.Net.Framework.3.5" + { + "Name" = "8:.NET Framework 3.5" + "ProductCode" = "8:Microsoft.Net.Framework.3.5" + } + "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:Microsoft.Windows.Installer.3.1" + { + "Name" = "8:Windows Installer 3.1" + "ProductCode" = "8:Microsoft.Windows.Installer.3.1" + } + } + } + } + "Release" + { + "DisplayName" = "8:Release" + "IsDebugOnly" = "11:FALSE" + "IsReleaseOnly" = "11:TRUE" + "OutputFilename" = "8:Release\\mite.desk.msi" + "PackageFilesAs" = "3:2" + "PackageFileSize" = "3:-2147483648" + "CabType" = "3:1" + "Compression" = "3:3" + "SignOutput" = "11:FALSE" + "CertificateFile" = "8:" + "PrivateKeyFile" = "8:" + "TimeStampServer" = "8:" + "InstallerBootstrapper" = "3:2" + "BootstrapperCfg:{63ACBE69-63AA-4F98-B2B6-99F9E24495F2}" + { + "Enabled" = "11:TRUE" + "PromptEnabled" = "11:TRUE" + "PrerequisitesLocation" = "2:1" + "Url" = "8:" + "ComponentsUrl" = "8:" + "Items" + { + "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:Microsoft.Net.Framework.3.5" + { + "Name" = "8:.NET Framework 3.5" + "ProductCode" = "8:Microsoft.Net.Framework.3.5" + } + "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:Microsoft.Windows.Installer.3.1" + { + "Name" = "8:Windows Installer 3.1" + "ProductCode" = "8:Microsoft.Windows.Installer.3.1" + } + } + } + } + } + "Deployable" + { + "CustomAction" + { + } + "DefaultFeature" + { + "Name" = "8:DefaultFeature" + "Title" = "8:" + "Description" = "8:" + } + "ExternalPersistence" + { + "LaunchCondition" + { + "{A06ECF26-33A3-4562-8140-9B0E340D4F24}:_C366A84C997D46A1B3E07AA85ACA4C8A" + { + "Name" = "8:.NET Framework" + "Message" = "8:[VSDNETMSG]" + "FrameworkVersion" = "8:3.5.30729 " + "AllowLaterVersions" = "11:FALSE" + "InstallUrl" = "8:http://www.microsoft.com/downloads/details.aspx?FamilyId=AB99342F-5D1A-413D-8319-81DA479AB0D7&displaylang=de" + } + } + } + "File" + { + "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_2D7003A1F920EF9EC3E2849B8E78110A" + { + "AssemblyRegister" = "3:1" + "AssemblyIsInGAC" = "11:FALSE" + "AssemblyAsmDisplayName" = "8:mite.desk.core.model, Version=1.2.17.0, Culture=neutral, processorArchitecture=MSIL" + "ScatterAssemblies" + { + "_2D7003A1F920EF9EC3E2849B8E78110A" + { + "Name" = "8:mite.desk.core.model.dll" + "Attributes" = "3:512" + } + } + "SourcePath" = "8:mite.desk.core.model.dll" + "TargetName" = "8:" + "Tag" = "8:" + "Folder" = "8:_474D496400934C669E8E0D12487A84B1" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:TRUE" + "IsolateTo" = "8:" + } + "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_390D2894D6B8216C73514AD70EE9C970" + { + "AssemblyRegister" = "3:1" + "AssemblyIsInGAC" = "11:FALSE" + "AssemblyAsmDisplayName" = "8:mite.desk.tools.connector, Version=1.2.17.0, Culture=neutral, processorArchitecture=MSIL" + "ScatterAssemblies" + { + "_390D2894D6B8216C73514AD70EE9C970" + { + "Name" = "8:mite.desk.tools.connector.dll" + "Attributes" = "3:512" + } + } + "SourcePath" = "8:mite.desk.tools.connector.dll" + "TargetName" = "8:" + "Tag" = "8:" + "Folder" = "8:_474D496400934C669E8E0D12487A84B1" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:TRUE" + "IsolateTo" = "8:" + } + "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_4BA3E6D6E6F0507A83D364988B9EC467" + { + "AssemblyRegister" = "3:1" + "AssemblyIsInGAC" = "11:FALSE" + "AssemblyAsmDisplayName" = "8:StructureMap, Version=2.5.3.0, Culture=neutral, PublicKeyToken=e60ad81abae3c223, processorArchitecture=MSIL" + "ScatterAssemblies" + { + "_4BA3E6D6E6F0507A83D364988B9EC467" + { + "Name" = "8:StructureMap.dll" + "Attributes" = "3:512" + } + } + "SourcePath" = "8:StructureMap.dll" + "TargetName" = "8:" + "Tag" = "8:" + "Folder" = "8:_474D496400934C669E8E0D12487A84B1" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:TRUE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_665DDB4A7B834028B01E82C06ADCCC2A" + { + "SourcePath" = "8:mite.desk.ico" + "TargetName" = "8:mite.desk.ico" + "Tag" = "8:" + "Folder" = "8:_474D496400934C669E8E0D12487A84B1" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7684FBF17F1A472588EF44035BEB78DE" + { + "SourcePath" = "8:Setup_Splash.jpg" + "TargetName" = "8:Setup_Splash.jpg" + "Tag" = "8:" + "Folder" = "8:_474D496400934C669E8E0D12487A84B1" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:TRUE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_A1D793DFCEAC4C2632C82E16A2294CBE" + { + "AssemblyRegister" = "3:1" + "AssemblyIsInGAC" = "11:FALSE" + "AssemblyAsmDisplayName" = "8:mite.desk.core.infrastructure, Version=1.2.17.0, Culture=neutral, processorArchitecture=MSIL" + "ScatterAssemblies" + { + "_A1D793DFCEAC4C2632C82E16A2294CBE" + { + "Name" = "8:mite.desk.core.infrastructure.dll" + "Attributes" = "3:512" + } + } + "SourcePath" = "8:mite.desk.core.infrastructure.dll" + "TargetName" = "8:" + "Tag" = "8:" + "Folder" = "8:_474D496400934C669E8E0D12487A84B1" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:TRUE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B2002D4BCE4648F5A834FA62822D3BF3" + { + "SourcePath" = "8:setup_titlebar.jpg" + "TargetName" = "8:setup_titlebar.jpg" + "Tag" = "8:" + "Folder" = "8:_474D496400934C669E8E0D12487A84B1" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:TRUE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_CE68243731A7F382932200AC685A832B" + { + "AssemblyRegister" = "3:1" + "AssemblyIsInGAC" = "11:FALSE" + "AssemblyAsmDisplayName" = "8:mite.desk.core.services, Version=1.2.17.0, Culture=neutral, processorArchitecture=MSIL" + "ScatterAssemblies" + { + "_CE68243731A7F382932200AC685A832B" + { + "Name" = "8:mite.desk.core.services.dll" + "Attributes" = "3:512" + } + } + "SourcePath" = "8:mite.desk.core.services.dll" + "TargetName" = "8:" + "Tag" = "8:" + "Folder" = "8:_474D496400934C669E8E0D12487A84B1" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:TRUE" + "IsolateTo" = "8:" + } + "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_FCBAAC4F50103C640B2F5DF5201DEC97" + { + "AssemblyRegister" = "3:1" + "AssemblyIsInGAC" = "11:FALSE" + "AssemblyAsmDisplayName" = "8:mite.desk.core.data, Version=1.2.17.0, Culture=neutral, processorArchitecture=MSIL" + "ScatterAssemblies" + { + "_FCBAAC4F50103C640B2F5DF5201DEC97" + { + "Name" = "8:mite.desk.core.data.dll" + "Attributes" = "3:512" + } + } + "SourcePath" = "8:mite.desk.core.data.dll" + "TargetName" = "8:" + "Tag" = "8:" + "Folder" = "8:_474D496400934C669E8E0D12487A84B1" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:TRUE" + "IsolateTo" = "8:" + } + } + "FileType" + { + } + "Folder" + { + "{1525181F-901A-416C-8A58-119130FE478E}:_29C9412AA9BD49A6A833A48F59B6BD0D" + { + "Name" = "8:#1916" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:DesktopFolder" + "Folders" + { + } + } + "{1525181F-901A-416C-8A58-119130FE478E}:_3483506330BF40DA9E48247C5C7DA1ED" + { + "Name" = "8:#1928" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:StartupFolder" + "Folders" + { + } + } + "{3C67513D-01DD-4637-8A68-80971EB9504F}:_474D496400934C669E8E0D12487A84B1" + { + "DefaultLocation" = "8:[ProgramFilesFolder][Manufacturer]\\[ProductName]" + "Name" = "8:#1925" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:TARGETDIR" + "Folders" + { + } + } + "{1525181F-901A-416C-8A58-119130FE478E}:_4757F6931EB44E119EFEE4ABF59151FE" + { + "Name" = "8:#1919" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:ProgramMenuFolder" + "Folders" + { + } + } + "{1525181F-901A-416C-8A58-119130FE478E}:_C8CAF572CADD4604A23AABF8060CA298" + { + "Name" = "8:#1915" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:AppDataFolder" + "Folders" + { + } + } + } + "LaunchCondition" + { + } + "Locator" + { + } + "MsiBootstrapper" + { + "LangId" = "3:1031" + "RequiresElevation" = "11:FALSE" + } + "Product" + { + "Name" = "8:Microsoft Visual Studio" + "ProductName" = "8:mite.desk" + "ProductCode" = "8:{64190F74-3531-4813-B09F-11685336F3B4}" + "PackageCode" = "8:{0EE72A52-F9B5-4919-AC4B-7D01F28AFF29}" + "UpgradeCode" = "8:{57BAD98D-AB91-48CD-8DBC-9FC1C8029282}" + "AspNetVersion" = "8:4.0.30319.0" + "RestartWWWService" = "11:FALSE" + "RemovePreviousVersions" = "11:TRUE" + "DetectNewerInstalledVersion" = "11:FALSE" + "InstallAllUsers" = "11:TRUE" + "ProductVersion" = "8:1.3.0" + "Manufacturer" = "8:69 Grad" + "ARPHELPTELEPHONE" = "8:" + "ARPHELPLINK" = "8:http://www.69grad.de" + "Title" = "8:mite.desk Setup" + "Subject" = "8:" + "ARPCONTACT" = "8:69 Grad" + "Keywords" = "8:" + "ARPCOMMENTS" = "8:" + "ARPURLINFOABOUT" = "8:http://www.69grad.de/" + "ARPPRODUCTICON" = "8:_665DDB4A7B834028B01E82C06ADCCC2A" + "ARPIconIndex" = "3:0" + "SearchPath" = "8:" + "UseSystemSearchPath" = "11:TRUE" + "TargetPlatform" = "3:0" + "PreBuildEvent" = "8:" + "PostBuildEvent" = "8:" + "RunPostBuildEvent" = "3:0" + } + "Registry" + { + "HKLM" + { + "Keys" + { + "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_8E067F3AD9E64DE39DF5CBCC53370867" + { + "Name" = "8:Software" + "Condition" = "8:" + "AlwaysCreate" = "11:FALSE" + "DeleteAtUninstall" = "11:FALSE" + "Transitive" = "11:FALSE" + "Keys" + { + "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_28D63300C54D4E25B8F2D212C798732B" + { + "Name" = "8:[Manufacturer]" + "Condition" = "8:" + "AlwaysCreate" = "11:FALSE" + "DeleteAtUninstall" = "11:FALSE" + "Transitive" = "11:FALSE" + "Keys" + { + } + "Values" + { + } + } + } + "Values" + { + } + } + } + } + "HKCU" + { + "Keys" + { + "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_3D87F767BA6B44AD9872E8B2969A320F" + { + "Name" = "8:Software" + "Condition" = "8:" + "AlwaysCreate" = "11:FALSE" + "DeleteAtUninstall" = "11:FALSE" + "Transitive" = "11:FALSE" + "Keys" + { + "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_28C07956704B4E3EB9D446DED2664AE2" + { + "Name" = "8:[Manufacturer]" + "Condition" = "8:" + "AlwaysCreate" = "11:FALSE" + "DeleteAtUninstall" = "11:FALSE" + "Transitive" = "11:FALSE" + "Keys" + { + } + "Values" + { + } + } + } + "Values" + { + } + } + } + } + "HKCR" + { + "Keys" + { + } + } + "HKU" + { + "Keys" + { + } + } + "HKPU" + { + "Keys" + { + } + } + } + "Sequences" + { + } + "Shortcut" + { + "{970C0BB2-C7D0-45D7-ABFA-7EC378858BC0}:_8460030AFF0842F49D59444AAEC77343" + { + "Name" = "8:mite.desk" + "Arguments" = "8:" + "Description" = "8:" + "ShowCmd" = "3:1" + "IconIndex" = "3:0" + "Transitive" = "11:FALSE" + "Target" = "8:_04A8B693F44A4C2AA60FD814913E3D08" + "Folder" = "8:_29C9412AA9BD49A6A833A48F59B6BD0D" + "WorkingFolder" = "8:_474D496400934C669E8E0D12487A84B1" + "Icon" = "8:_665DDB4A7B834028B01E82C06ADCCC2A" + "Feature" = "8:" + } + "{970C0BB2-C7D0-45D7-ABFA-7EC378858BC0}:_FD70069B358B4EA69E94BB0394385AF0" + { + "Name" = "8:mite.desk" + "Arguments" = "8:" + "Description" = "8:" + "ShowCmd" = "3:1" + "IconIndex" = "3:0" + "Transitive" = "11:FALSE" + "Target" = "8:_04A8B693F44A4C2AA60FD814913E3D08" + "Folder" = "8:_4757F6931EB44E119EFEE4ABF59151FE" + "WorkingFolder" = "8:_474D496400934C669E8E0D12487A84B1" + "Icon" = "8:_665DDB4A7B834028B01E82C06ADCCC2A" + "Feature" = "8:" + } + } + "UserInterface" + { + "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_0F21D88DE6754393B61444016DF4F692" + { + "Name" = "8:#1900" + "Sequence" = "3:1" + "Attributes" = "3:1" + "Dialogs" + { + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_2F7CA84E87314D798E1B7157A142804A" + { + "Sequence" = "3:300" + "DisplayName" = "8:Confirm Installation" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdConfirmDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:2" + "Value" = "8:_B2002D4BCE4648F5A834FA62822D3BF3" + "UsePlugInResources" = "11:TRUE" + } + } + } + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_7C2FFB9880C948F9A5485A0B9805B1EC" + { + "Sequence" = "3:110" + "DisplayName" = "8:Splash" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdSplashDlg.wid" + "Properties" + { + "SplashBitmap" + { + "Name" = "8:SplashBitmap" + "DisplayName" = "8:#1013" + "Description" = "8:#1113" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:0" + "Setting" = "3:2" + "Value" = "8:_7684FBF17F1A472588EF44035BEB78DE" + "UsePlugInResources" = "11:TRUE" + } + "Sunken" + { + "Name" = "8:Sunken" + "DisplayName" = "8:#1007" + "Description" = "8:#1107" + "Type" = "3:5" + "ContextData" = "8:4;True=4;False=0" + "Attributes" = "3:0" + "Setting" = "3:0" + "Value" = "3:0" + "DefaultValue" = "3:4" + "UsePlugInResources" = "11:TRUE" + } + } + } + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_9F3099BC506C4419925266281880EE0E" + { + "Sequence" = "3:200" + "DisplayName" = "8:Installation Folder" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdFolderDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:2" + "Value" = "8:_B2002D4BCE4648F5A834FA62822D3BF3" + "UsePlugInResources" = "11:TRUE" + } + "InstallAllUsersVisible" + { + "Name" = "8:InstallAllUsersVisible" + "DisplayName" = "8:#1059" + "Description" = "8:#1159" + "Type" = "3:5" + "ContextData" = "8:1;True=1;False=0" + "Attributes" = "3:0" + "Setting" = "3:0" + "Value" = "3:1" + "DefaultValue" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + } + } + } + } + "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_188D95436D754802B81FDFCE432C1002" + { + "Name" = "8:#1901" + "Sequence" = "3:2" + "Attributes" = "3:2" + "Dialogs" + { + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_8709E07B55EB4200B16F774B0915A908" + { + "Sequence" = "3:100" + "DisplayName" = "8:Progress" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdAdminProgressDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:2" + "Value" = "8:_B2002D4BCE4648F5A834FA62822D3BF3" + "UsePlugInResources" = "11:TRUE" + } + "ShowProgress" + { + "Name" = "8:ShowProgress" + "DisplayName" = "8:#1009" + "Description" = "8:#1109" + "Type" = "3:5" + "ContextData" = "8:1;True=1;False=0" + "Attributes" = "3:0" + "Setting" = "3:0" + "Value" = "3:1" + "DefaultValue" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + } + } + } + } + "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_2C14386792EA4490873704E74E862AAC" + { + "Name" = "8:#1902" + "Sequence" = "3:1" + "Attributes" = "3:3" + "Dialogs" + { + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_8984BDC4525A4276A3A7BCADB58B642D" + { + "Sequence" = "3:100" + "DisplayName" = "8:Finished" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdFinishedDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:2" + "Value" = "8:_B2002D4BCE4648F5A834FA62822D3BF3" + "UsePlugInResources" = "11:TRUE" + } + "UpdateText" + { + "Name" = "8:UpdateText" + "DisplayName" = "8:#1058" + "Description" = "8:#1158" + "Type" = "3:15" + "ContextData" = "8:" + "Attributes" = "3:0" + "Setting" = "3:2" + "Value" = "8:" + "DefaultValue" = "8:#1258" + "UsePlugInResources" = "11:TRUE" + } + } + } + } + } + "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_368EFD2AEFFF4776B1EE63522E39E231" + { + "Name" = "8:#1901" + "Sequence" = "3:1" + "Attributes" = "3:2" + "Dialogs" + { + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_F3B220FD1A504DE397B94DF9F8B45DB1" + { + "Sequence" = "3:100" + "DisplayName" = "8:Progress" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdProgressDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:2" + "Value" = "8:_B2002D4BCE4648F5A834FA62822D3BF3" + "UsePlugInResources" = "11:TRUE" + } + "ShowProgress" + { + "Name" = "8:ShowProgress" + "DisplayName" = "8:#1009" + "Description" = "8:#1109" + "Type" = "3:5" + "ContextData" = "8:1;True=1;False=0" + "Attributes" = "3:0" + "Setting" = "3:0" + "Value" = "3:1" + "DefaultValue" = "3:1" + "UsePlugInResources" = "11:TRUE" + } + } + } + } + } + "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_5CBE240E454949EDBE92A181CB2FD5AF" + { + "Name" = "8:#1902" + "Sequence" = "3:2" + "Attributes" = "3:3" + "Dialogs" + { + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_B5ACFF92442949F2A982D5A02C6AD40B" + { + "Sequence" = "3:100" + "DisplayName" = "8:Finished" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdAdminFinishedDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:2" + "Value" = "8:_B2002D4BCE4648F5A834FA62822D3BF3" + "UsePlugInResources" = "11:TRUE" + } + } + } + } + } + "{2479F3F5-0309-486D-8047-8187E2CE5BA0}:_A78015CF23EA4386A15C22A109F048E4" + { + "UseDynamicProperties" = "11:FALSE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdUserInterface.wim" + } + "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_DFA92D47F66F47B2B52DF87DC0CF022C" + { + "Name" = "8:#1900" + "Sequence" = "3:2" + "Attributes" = "3:1" + "Dialogs" + { + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_09D1D3227DA04BF08022F58F6453BC53" + { + "Sequence" = "3:120" + "DisplayName" = "8:Installation Folder" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdAdminFolderDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:2" + "Value" = "8:_B2002D4BCE4648F5A834FA62822D3BF3" + "UsePlugInResources" = "11:TRUE" + } + } + } + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_8F17CD27BB744D12AD09D893EC86E114" + { + "Sequence" = "3:100" + "DisplayName" = "8:Splash" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdAdminSplashDlg.wid" + "Properties" + { + "SplashBitmap" + { + "Name" = "8:SplashBitmap" + "DisplayName" = "8:#1013" + "Description" = "8:#1113" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:0" + "Setting" = "3:2" + "Value" = "8:_7684FBF17F1A472588EF44035BEB78DE" + "UsePlugInResources" = "11:TRUE" + } + "Sunken" + { + "Name" = "8:Sunken" + "DisplayName" = "8:#1007" + "Description" = "8:#1107" + "Type" = "3:5" + "ContextData" = "8:4;True=4;False=0" + "Attributes" = "3:0" + "Setting" = "3:0" + "Value" = "3:0" + "DefaultValue" = "3:4" + "UsePlugInResources" = "11:TRUE" + } + } + } + "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_FC03B617E08C4BDC8901447FFC480ED4" + { + "Sequence" = "3:200" + "DisplayName" = "8:Confirm Installation" + "UseDynamicProperties" = "11:TRUE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdAdminConfirmDlg.wid" + "Properties" + { + "BannerBitmap" + { + "Name" = "8:BannerBitmap" + "DisplayName" = "8:#1001" + "Description" = "8:#1101" + "Type" = "3:8" + "ContextData" = "8:Bitmap" + "Attributes" = "3:4" + "Setting" = "3:2" + "Value" = "8:_B2002D4BCE4648F5A834FA62822D3BF3" + "UsePlugInResources" = "11:TRUE" + } + } + } + } + } + "{2479F3F5-0309-486D-8047-8187E2CE5BA0}:_E9276C061A344212A4E18C0C3457474F" + { + "UseDynamicProperties" = "11:FALSE" + "IsDependency" = "11:FALSE" + "SourcePath" = "8:\\VsdBasicDialogs.wim" + } + } + "MergeModule" + { + } + "ProjectOutput" + { + "{5259A561-127C-4D43-A0A1-72F10C7B3BF8}:_04A8B693F44A4C2AA60FD814913E3D08" + { + "SourcePath" = "8:..\\..\\App\\MiteDesk.WinForms\\obj\\Release\\mite.desk.exe" + "TargetName" = "8:" + "Tag" = "8:" + "Folder" = "8:_474D496400934C669E8E0D12487A84B1" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + "ProjectOutputGroupRegister" = "3:1" + "OutputConfiguration" = "8:" + "OutputGroupCanonicalName" = "8:Built" + "OutputProjectGuid" = "8:{352CDDB3-9A72-43FE-84B4-7021C6CBCAD4}" + "ShowKeyOutput" = "11:TRUE" + "ExcludeFilters" + { + } + } + "{5259A561-127C-4D43-A0A1-72F10C7B3BF8}:_BB09C5F62C6D4D7C9D3154FE02976466" + { + "SourcePath" = "8:" + "TargetName" = "8:" + "Tag" = "8:" + "Folder" = "8:_474D496400934C669E8E0D12487A84B1" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + "ProjectOutputGroupRegister" = "3:1" + "OutputConfiguration" = "8:" + "OutputGroupCanonicalName" = "8:LocalizedResourceDlls" + "OutputProjectGuid" = "8:{352CDDB3-9A72-43FE-84B4-7021C6CBCAD4}" + "ShowKeyOutput" = "11:TRUE" + "ExcludeFilters" + { + } + } + "{5259A561-127C-4D43-A0A1-72F10C7B3BF8}:_C06EDB9458EB43E89A25EF1A758B6655" + { + "SourcePath" = "8:" + "TargetName" = "8:" + "Tag" = "8:" + "Folder" = "8:_474D496400934C669E8E0D12487A84B1" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + "ProjectOutputGroupRegister" = "3:1" + "OutputConfiguration" = "8:" + "OutputGroupCanonicalName" = "8:LocalizedResourceDlls" + "OutputProjectGuid" = "8:{733C8DE9-36B5-4C9F-9701-FDD19B64B966}" + "ShowKeyOutput" = "11:TRUE" + "ExcludeFilters" + { + } + } + "{5259A561-127C-4D43-A0A1-72F10C7B3BF8}:_C7ECF21159E04AABB6405AC9B0D57722" + { + "SourcePath" = "8:" + "TargetName" = "8:" + "Tag" = "8:" + "Folder" = "8:_474D496400934C669E8E0D12487A84B1" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + "ProjectOutputGroupRegister" = "3:1" + "OutputConfiguration" = "8:" + "OutputGroupCanonicalName" = "8:LocalizedResourceDlls" + "OutputProjectGuid" = "8:{B7B67B5C-47F1-4E3D-8759-EA8E31AC951E}" + "ShowKeyOutput" = "11:TRUE" + "ExcludeFilters" + { + } + } + } + } +} diff --git a/Deployment/MiteDesk.WinForms.Setup/Setup_Splash.jpg b/Deployment/MiteDesk.WinForms.Setup/Setup_Splash.jpg new file mode 100644 index 0000000..159707d Binary files /dev/null and b/Deployment/MiteDesk.WinForms.Setup/Setup_Splash.jpg differ diff --git a/Deployment/MiteDesk.WinForms.Setup/mite.desk.ico b/Deployment/MiteDesk.WinForms.Setup/mite.desk.ico new file mode 100644 index 0000000..ff25fca Binary files /dev/null and b/Deployment/MiteDesk.WinForms.Setup/mite.desk.ico differ diff --git a/Deployment/MiteDesk.WinForms.Setup/setup_titlebar.jpg b/Deployment/MiteDesk.WinForms.Setup/setup_titlebar.jpg new file mode 100644 index 0000000..e76e04b Binary files /dev/null and b/Deployment/MiteDesk.WinForms.Setup/setup_titlebar.jpg differ diff --git a/Library/Shared/RhinoMocks/Rhino.Mocks.dll b/Library/Shared/RhinoMocks/Rhino.Mocks.dll new file mode 100644 index 0000000..4b6904b Binary files /dev/null and b/Library/Shared/RhinoMocks/Rhino.Mocks.dll differ diff --git a/Library/Shared/RhinoMocks/Rhino.Mocks.xml b/Library/Shared/RhinoMocks/Rhino.Mocks.xml new file mode 100644 index 0000000..ebb20c5 --- /dev/null +++ b/Library/Shared/RhinoMocks/Rhino.Mocks.xml @@ -0,0 +1,5226 @@ + + + + Rhino.Mocks.Partial + + + + + Interface for constraints + + + + + determains if the object pass the constraints + + + + + And operator for constraints + + + + + Not operator for constraints + + + + + Or operator for constraints + + + + + Allow overriding of || or && + + + + + + + Allow overriding of || or && + + + + + + + Gets the message for this constraint + + + + + + Initializes a new constraint object. + + The expected object, The actual object is passed in as a parameter to the method + + + + Evaluate this constraint. + + The actual object that was passed in the method call to the mock. + True when the constraint is met, else false. + + + + Checks if the properties of the object + are the same as the properies of the object. + + The expected object + The actual object + True when both objects have the same values, else False. + + + + + + + + + This is the real heart of the beast. + + + + Used by CheckReferenceType to check all properties of the reference type. + + The expected object + The actual object + True when both objects have the same values, else False. + + + + Used by CheckReferenceType to check all fields of the reference type. + + The expected object + The actual object + True when both objects have the same values, else False. + + + + Checks the items of both collections + + The expected collection + + True if both collections contain the same items in the same order. + + + + Builds a propertyname from the Stack _properties like 'Order.Product.Price' + to be used in the error message. + + A nested property name. + + + + Rhino.Mocks uses this property to generate an error message. + + + A message telling the tester why the constraint failed. + + + + + Constrain that the public field has a specified value + + + + + Constrain that the public field matches another constraint. + + + + + Creates a new instance. + + Name of the public field. + Constraint to place on the public field value. + + + + Creates a new instance, specifying a disambiguating + for the public field. + + The type that declares the public field, used to disambiguate between public fields. + Name of the public field. + Constraint to place on the public field value. + + + + Determines if the object passes the constraint. + + + + + Gets the message for this constraint + + + + + + Creates a new instance. + + Name of the public field. + Expected value. + + + + Creates a new instance, specifying a disambiguating + for the public field. + + The type that declares the public field, used to disambiguate between public fields. + Name of the public field. + Expected value. + + + + Constrain that the property has a specified value + + + + + Constrain that the property matches another constraint. + + + + + Creates a new instance. + + Name of the property. + Constraint to place on the property value. + + + + Creates a new instance, specifying a disambiguating + for the property. + + The type that declares the property, used to disambiguate between properties. + Name of the property. + Constraint to place on the property value. + + + + Determines if the object passes the constraint. + + + + + Gets the message for this constraint + + + + + + Creates a new instance. + + Name of the property. + Expected value. + + + + Creates a new instance, specifying a disambiguating + for the property. + + The type that declares the property, used to disambiguate between properties. + Name of the property. + Expected value. + + + + Constrain that the parameter must be of the specified type + + + + + Creates a new instance. + + Type. + + + + determains if the object pass the constraints + + + + + Gets the message for this constraint + + + + + + Constraint that determines whether an object is the same object as another. + + + + + Creates a new instance. + + Obj. + + + + Determines if the object passes the constraints. + + + + + Gets the message for this constraint. + + + + + Evaluate a parameter using constraints + + + + + Create new instance + + + + + + determains if the object pass the constraints + + + + + Gets the message for this constraint + + + + + + A constraint based on lambda expression, we are using Expression{T} + because we want to be able to get good error reporting on that. + + + + + Initializes a new instance of the class. + + The expr. + + + + determains if the object pass the constraints + + + + + + + Gets the message for this constraint + + + + + + Constrain that the list contains the same items as the parameter list + + + + + Creates a new instance. + + In list. + + + + determains if the object pass the constraints + + + + + Gets the message for this constraint + + + + + + Constrain that the parameter is one of the items in the list + + + + + Creates a new instance. + + In list. + + + + determains if the object pass the constraints + + + + + Gets the message for this constraint + + + + + + Constrain that the object is inside the parameter list + + + + + Creates a new instance. + + In list. + + + + determains if the object pass the constraints + + + + + Gets the message for this constraint + + + + + + Applies another AbstractConstraint to the collection count. + + + + + Creates a new instance. + + The constraint that should be applied to the collection count. + + + + Determines if the parameter conforms to this constraint. + + + + + Gets the message for this constraint. + + + + + Applies another AbstractConstraint to a specific list element. + + + + + Creates a new instance. + + The zero-based index of the list element. + The constraint that should be applied to the list element. + + + + Determines if the parameter conforms to this constraint. + + + + + Gets the message for this constraint + + + + + + Applies another AbstractConstraint to a specific generic keyed list element. + + + + + Creates a new instance. + + The key of the list element. + The constraint that should be applied to the list element. + + + + Determines if the parameter conforms to this constraint. + + + + + Gets the message for this constraint + + + + + + Constrains that all elements are in the parameter list + + + + + Initializes a new instance of the class. + + The these. + + + + determains if the object pass the constraints + + + + + + + Gets the message for this constraint + + + + + + Combines two constraints, constraint pass if either is fine. + + + + + Creates a new instance. + + C1. + C2. + + + + determains if the object pass the constraints + + + + + Gets the message for this constraint + + + + + + Negate a constraint + + + + + Creates a new instance. + + C1. + + + + determains if the object pass the constraints + + + + + Gets the message for this constraint + + + + + + Combines two constraints + + + + + + Creates a new instance. + + C1. + C2. + + + + determains if the object pass the constraints + + + + + Gets the message for this constraint + + + + + + Constrain the argument to validate according to regex pattern + + + + + Creates a new instance. + + Pattern. + + + + determains if the object pass the constraints + + + + + Gets the message for this constraint + + + + + + Constraint that evaluate whatever an argument contains the specified string. + + + + + Creates a new instance. + + Inner string. + + + + determains if the object pass the constraints + + + + + Gets the message for this constraint + + + + + + Constraint that evaluate whatever an argument ends with the specified string + + + + + Creates a new instance. + + End. + + + + determains if the object pass the constraints + + + + + Gets the message for this constraint + + + + + + Constraint that evaluate whatever an argument start with the specified string + + + + + Creates a new instance. + + Start. + + + + determains if the object pass the constraints + + + + + Gets the message for this constraint + + + + + + Constraint that evaluate whatever an object equals another + + + + + Creates a new instance. + + Obj. + + + + determains if the object pass the constraints + + + + + Gets the message for this constraint + + + + + + Constraint that always returns true + + + + + determains if the object pass the constraints + + + + + Gets the message for this constraint + + + + + + Constraint that evaluate whatever a comparable is greater than another + + + + + Creates a new instance. + + + + + determains if the object pass the constraints + + + + + Gets the message for this constraint + + + + + + Central location for constraints + + + + + Evaluate a greater than constraint for . + + The object the parameter should be greater than + + + + Evaluate a less than constraint for . + + The object the parameter should be less than + + + + Evaluate a less than or equal constraint for . + + The object the parameter should be less than or equal to + + + + Evaluate a greater than or equal constraint for . + + The object the parameter should be greater than or equal to + + + + Evaluate an equal constraint for . + + The object the parameter should equal to + + + + Evaluate a not equal constraint for . + + The object the parameter should not equal to + + + + Evaluate a same as constraint. + + The object the parameter should the same as. + + + + Evaluate a not same as constraint. + + The object the parameter should not be the same as. + + + + A constraints that accept anything + + + + + + A constraint that accept only nulls + + + + + + A constraint that accept only non null values + + + + + + A constraint that accept only value of the specified type + + + + + A constraint that accept only value of the specified type + + + + + Evaluate a parameter using a predicate + + The predicate to use + + + + Provides access to the constraintes defined in the class to be used in context + with the syntax. + + The type of the argument + + + + Evaluate a greater than constraint for . + + The object the parameter should be greater than + + + + Evaluate a less than constraint for . + + The object the parameter should be less than + + + + Evaluate a less than or equal constraint for . + + The object the parameter should be less than or equal to + + + + Evaluate a greater than or equal constraint for . + + The object the parameter should be greater than or equal to + + + + Evaluate an equal constraint for . + + The object the parameter should equal to + + + + Evaluate a not equal constraint for . + + The object the parameter should not equal to + + + + Evaluate a same as constraint. + + The object the parameter should the same as. + + + + Evaluate a not same as constraint. + + The object the parameter should not be the same as. + + + + Throws NotSupportedException. Don't use Equals to define constraints. Use Equal instead. + + + + + + + Serves as a hash function for a particular type. + + + A hash code for the current . + + + + + A constraints that accept anything + + + + + + A constraint that accept only nulls + + + + + + A constraint that accept only non null values + + + + + + A constraint that accept only value of the specified type. + The check is performed on the type that has been defined + as the argument type. + + + + + Central location for constraints about lists and collections + + + + + Determines whether the specified obj is in the paramter. + The parameter must be IEnumerable. + + Obj. + + + + + Determains whatever the parameter is in the collection. + + + + + Determains that the parameter collection is identical to the specified collection + + + + + Determines that the parameter collection has the specified number of elements. + + The constraint that should be applied to the collection count. + + + + Determines that an element of the parameter collections conforms to another AbstractConstraint. + + The zero-based index of the list element. + The constraint which should be applied to the list element. + + + + Determines that an element of the parameter collections conforms to another AbstractConstraint. + + The key of the element. + The constraint which should be applied to the element. + + + + Determines that all elements of the specified collection are in the the parameter collection + + The collection to compare against + The constraint which should be applied to the list parameter. + + + + Provides access to the constraintes defined in the class to be used in context + with the syntax. + + + + + Determines whether the specified object is in the paramter. + The parameter must be IEnumerable. + + Obj. + + + + + Determains whatever the parameter is in the collection. + + + + + Determains that the parameter collection is identical to the specified collection + + + + + Determines that the parameter collection has the specified number of elements. + + The constraint that should be applied to the collection count. + + + + Determines that an element of the parameter collections conforms to another AbstractConstraint. + + The zero-based index of the list element. + The constraint which should be applied to the list element. + + + + Determines that all elements of the specified collection are in the the parameter collection + + The collection to compare against + The constraint which should be applied to the list parameter. + + + + Throws NotSupportedException. Don't use Equals to define constraints. Use Equal instead. + + + + + + + Serves as a hash function for a particular type. + + + A hash code for the current . + + + + + Provides a dummy field to pass as out or ref argument. + + + + + + Dummy field to satisfy the compiler. Used for out and ref arguments. + + + + + Central location for constraints for object's properties + + + + + Constrains the parameter to have property with the specified value + + Name of the property. + Expected value. + + + + + Constrains the parameter to have property with the specified value. + + The type that declares the property, used to disambiguate between properties. + Name of the property. + Expected value. + + + + + Constrains the parameter to have a property satisfying a specified constraint. + + Name of the property. + Constraint for the property. + + + + Constrains the parameter to have a property satisfying a specified constraint. + + The type that declares the property, used to disambiguate between properties. + Name of the property. + Constraint for the property. + + + + Determines whether the parameter has the specified property and that it is null. + + Name of the property. + + + + + Determines whether the parameter has the specified property and that it is null. + + The type that declares the property, used to disambiguate between properties. + Name of the property. + + + + + Determines whether the parameter has the specified property and that it is not null. + + Name of the property. + + + + + Determines whether the parameter has the specified property and that it is not null. + + The type that declares the property, used to disambiguate between properties. + Name of the property. + + + + + constraints the parameter to have the exact same property values as the expected object. + + An object, of the same type as the parameter, whose properties are set with the expected values. + An instance of the constraint that will do the actual check. + + The parameter's public property values and public field values will be matched against the expected object's + public property values and public field values. The first mismatch will be reported and no further matching is done. + The matching is recursive for any property or field that has properties or fields of it's own. + Collections are supported through IEnumerable, which means the constraint will check if the actual and expected + collection contain the same values in the same order, where the values contained by the collection can have properties + and fields of their own that will be checked as well because of the recursive nature of this constraint. + + + + + Central location for constraints for object's public fields + + + + + Constrains the parameter to have a public field with the specified value + + Name of the public field. + Expected value. + + + + + Constrains the parameter to have a public field with the specified value. + + The type that declares the public field, used to disambiguate between public fields. + Name of the public field. + Expected value. + + + + + Constrains the parameter to have a public field satisfying a specified constraint. + + Name of the public field. + Constraint for the public field. + + + + Constrains the parameter to have a public field satisfying a specified constraint. + + The type that declares the public field, used to disambiguate between public fields. + Name of the public field. + Constraint for the public field. + + + + Determines whether the parameter has the specified public field and that it is null. + + Name of the public field. + + + + + Determines whether the parameter has the specified public field and that it is null. + + The type that declares the public field, used to disambiguate between public fields. + Name of the public field. + + + + + Determines whether the parameter has the specified public field and that it is not null. + + Name of the public field. + + + + + Determines whether the parameter has the specified public field and that it is not null. + + The type that declares the public field, used to disambiguate between public fields. + Name of the public field. + + + + + Central location for all text related constraints + + + + + Constrain the argument to starts with the specified string + + + + + Constrain the argument to end with the specified string + + + + + Constrain the argument to contain the specified string + + + + + Constrain the argument to validate according to regex pattern + + + + + Provides access to the constraintes defined in the class to be used in context + with the syntax. + + + + + Constrain the argument to starts with the specified string + + + + + + Constrain the argument to end with the specified string + + + + + Constrain the argument to contain the specified string + + + + + Constrain the argument to validate according to regex pattern + + + + + Throws NotSupportedException. Don't use Equals to define constraints. Use Equal instead. + + + + + + + Serves as a hash function for a particular type. + + + A hash code for the current . + + + + + An expectaton violation was detected. + + + + + Creates a new instance. + + Message. + + + + Serialization constructor + + + + + Signals that an object was call on a mock repostiroy which doesn't + belong to this mock repository or not a mock + + + + + Creates a new instance. + + Message. + + + + Serialization constructor + + + + + Abstract class that holds common information for + expectations. + + + + + Interface to validate that a method call is correct. + + + + + Validate the arguments for the method. + This method can be called numerous times, so be careful about side effects + + The arguments with which the method was called + + + + Add an actual method call to this expectation + + + + + Returns the return value or throw the exception and setup any output / ref parameters + that has been set. + + + + + Allow to set the return value in the future, if it was already set. + + + + + Builds the verification failure message. + + + + + + Gets the error message. + + + + + + Range of expected calls + + + + + Number of call actually made for this method + + + + + If this expectation is still waiting for calls. + + + + + The return value for a method matching this expectation + + + + + Gets or sets the exception to throw on a method matching this expectation. + + + + + Gets a value indicating whether this instance's action is staisfied. + A staisfied instance means that there are no more requirements from + this method. A method with non void return value must register either + a return value or an exception to throw. + + + + + Gets the method this expectation is for. + + + + + Gets or sets what special condtions there are for this method + repeating. + + + + + Gets a value indicating whether this expectation was satisfied + + + + + Specify whatever this expectation has a return value set + You can't check ReturnValue for this because a valid return value include null. + + + + + An action to execute when the method is matched. + + + + + Set the out / ref parameters for the method call. + The indexing is zero based and ignores any non out/ref parameter. + It is possible not to pass all the parameters. This method can be called only once. + + + + + Documentation Message + + + + + Gets the invocation for this expectation + + The invocation. + + + + Occurs when the exceptation is match on a method call + + + + + Number of actuall calls made that passed this expectation + + + + + Range of expected calls that should pass this expectation. + + + + + The return value for a method matching this expectation + + + + + The exception to throw on a method matching this expectation. + + + + + The method this expectation is for. + + + + + The return value for this method was set + + + + + Whether this method will repeat + unlimited number of times. + + + + + A delegate that will be run when the + expectation is matched. + + + + + The arguments that matched this expectation. + + + + + Documentation message + + + + + The method originalInvocation + + + + + Get the hash code + + + + + Add an actual actualMethodCall call to this expectation + + + + + Builds the verification failure message. + + + + + + Allow to set the return value in the future, if it was already set. + + + + + Returns the return value or throw the exception and setup output / ref parameters + + + + + Validate the arguments for the method on the child methods + + The arguments with which the method was called + + + + Creates a new instance. + + The originalInvocation for this method, required because it contains the generic type infromation + + + + Creates a new instance. + + Expectation. + + + + Validate the arguments for the method on the child methods + + The arguments with which the method was called + + + + Determines if this object equal to obj + + + + + The error message for these arguments + + + + + Asserts that the delegate has the same parameters as the expectation's method call + + + + + Setter for the outpur / ref parameters for this expecataion. + Can only be set once. + + + + + Specify whatever this expectation has a return value set + You can't check ReturnValue for this because a valid return value include null. + + + + + Gets the method this expectation is for. + + + + + Gets the originalInvocation for this expectation + + The originalInvocation. + + + + Gets or sets what special condtions there are for this method + + + + + Range of expected calls + + + + + Number of call actually made for this method + + + + + If this expectation is still waiting for calls. + + + + + Gets a value indicating whether this expectation was satisfied + + + + + The return value for a method matching this expectation + + + + + An action to execute when the method is matched. + + + + + Gets or sets the exception to throw on a method matching this expectation. + + + + + Gets a value indicating whether this instance's action is staisfied. + A staisfied instance means that there are no more requirements from + this method. A method with non void return value must register either + a return value or an exception to throw or an action to execute. + + + + + Documentation message + + + + + Occurs when the exceptation is match on a method call + + + + + Gets the error message. + + + + + + Expectation that matchs any arguments for the method. + + + + + Creates a new instance. + + Invocation for this expectation + + + + Creates a new instance. + + Expectation. + + + + Validate the arguments for the method. + + The arguments with which the method was called + + + + Determines if the object equal to expectation + + + + + Get the hash code + + + + + Gets the error message. + + + + + + Summary description for ArgsEqualExpectation. + + + + + Creates a new instance. + + Expected args. + The invocation for this expectation + + + + Validate the arguments for the method. + + The arguments with which the method was called + + + + Determines if the object equal to expectation + + + + + Get the hash code + + + + + Gets the error message. + + + + + + Get the expected args. + + + + + Call a specified callback to verify the expectation + + + + + Creates a new instance. + + Expectation. + Callback. + + + + Creates a new instance. + + Invocation for this expectation + Callback. + + + + Validate the arguments for the method on the child methods + + The arguments with which the method was called + + + + Determines if the object equal to expectation + + + + + Get the hash code + + + + + Gets the error message. + + + + + + Expect the method's arguments to match the contraints + + + + + Creates a new instance. + + Invocation for this expectation + Constraints. + + + + Creates a new instance. + + Expectation. + Constraints. + + + + Validate the arguments for the method. + + The arguments with which the method was called + + + + Determines if the object equal to expectation + + + + + Get the hash code + + + + + Gets the error message. + + + + + + ExpectationsList + + + + + Dictionary + + + + + Dictionary class + + + + + Create a new instance of ProxyStateDictionary + + + + + Operation on a remoting proxy + + + It is not possible to directly communicate to a real proxy via transparent proxy. + Transparent proxy impersonates a user type and only methods of that user type are callable. + The only methods that are guaranteed to exist on any transparent proxy are methods defined + in Object: namely ToString(), GetHashCode(), and Equals()). + + These three methods are the only way to tell the real proxy to do something. + Equals() is the most suitable of all, since it accepts an arbitrary object parameter. + The RemotingProxy code is built so that if it is compared to an IRemotingProxyOperation, + transparentProxy.Equals(operation) will call operation.Process(realProxy). + This way we can retrieve a real proxy from transparent proxy and perform + arbitrary operation on it. + + + + + Implementation of IInvocation based on remoting proxy + + Some methods are marked NotSupported since they either don't make sense + for remoting proxies, or they are never called by Rhino Mocks + + + + Generates remoting proxies and provides utility functions + + + + + Create the proxy using remoting + + + + + Check whether an object is a transparent proxy with a RemotingProxy behind it + + Object to check + true if the object is a transparent proxy with a RemotingProxy instance behind it, false otherwise + We use Equals() method to communicate with the real proxy behind the object. + See IRemotingProxyOperation for more details + + + + Retrieve a mocked object from a transparent proxy + + Transparent proxy with a RemotingProxy instance behind it + Mocked object associated with the proxy + We use Equals() method to communicate with the real proxy behind the object. + See IRemotingProxyOperation for more details + + + + Allows to call a method and immediatly get it's options. + + + + + Interface to allows to call a method and immediatly get it's options. + + + + + Get the method options for the call + + The method call should go here, the return value is ignored + + + + Creates a new instance. + + + + + Get the method options for the call + + The method call should go here, the return value is ignored + + + + Allows to call a method and immediatly get it's options. + Set the expected number for the call to Any() + + + + + Creates a new instance. + + Proxy. + Mocked instance. + + + + Get the method options for the call + + The method call should go here, the return value is ignored + + + + This class is reponsible for taking a delegate and creating a wrapper + interface around it, so it can be mocked. + + + + + The scope for all the delegate interfaces create by this mock repositroy. + + + + + Gets a type with an "Invoke" method suitable for use as a target of the + specified delegate type. + + + + + + + Raise events for all subscribers for an event + + + + + Raise events for all subscribers for an event + + + + + Raise the event + + + + + The most common form for the event handler signature + + + + + Create an event raise for the specified event on this instance. + + + + + Creates a new instance of EventRaiser + + + + + Raise the event + + + + + The most common signature for events + Here to allow intellisense to make better guesses about how + it should suggest parameters. + + + + + Allows to define what would happen when a method + is called. + + + + + Allows to define what would happen when a method + is called. + + + + + Set the return value for the method. + + The object the method will return + IRepeat that defines how many times the method will return this value + + + + Allow to override this return value in the future + + IRepeat that defines how many times the method will return this value + + + + Throws the specified exception when the method is called. + + Exception to throw + + + + Ignores the arguments for this method. Any argument will be matched + againt this method. + + + + + Add constraints for the method's arguments. + + + + + Set a callback method for the last call + + + + + Set a delegate to be called when the expectation is matched. + The delegate return value will be returned from the expectation. + + + + + Set a delegate to be called when the expectation is matched. + The delegate return value will be returned from the expectation. + + + + + Set a delegate to be called when the expectation is matched. + The delegate return value will be returned from the expectation. + + + + + Set a delegate to be called when the expectation is matched. + The delegate return value will be returned from the expectation. + + + + + Set a delegate to be called when the expectation is matched. + The delegate return value will be returned from the expectation. + + + + + Set a delegate to be called when the expectation is matched. + The delegate return value will be returned from the expectation. + + + + + Set a delegate to be called when the expectation is matched. + The delegate return value will be returned from the expectation. + + + + + Set a delegate to be called when the expectation is matched. + The delegate return value will be returned from the expectation. + + + + + Set a delegate to be called when the expectation is matched. + The delegate return value will be returned from the expectation. + + + + + Set a delegate to be called when the expectation is matched. + The delegate return value will be returned from the expectation. + + + + + Set a delegate to be called when the expectation is matched. + The delegate return value will be returned from the expectation. + + + + + Set a delegate to be called when the expectation is matched. + The delegate return value will be returned from the expectation. + + + + + Set a delegate to be called when the expectation is matched + and allow to optionally modify the invocation as needed + + + + + Call the original method on the class, bypassing the mocking layers. + + + + + + Call the original method on the class, optionally bypassing the mocking layers. + + + + + + Use the property as a simple property, getting/setting the values without + causing mock expectations. + + + + + Expect last (property) call as property setting, ignore the argument given + + + + + + Expect last (property) call as property setting with a given argument. + + + + + + + Get an event raiser for the last subscribed event. + + + + + Set the parameter values for out and ref parameters. + This is done using zero based indexing, and _ignoring_ any non out/ref parameter. + + + + + Documentation message for the expectation + + Message + + + + Better syntax to define repeats. + + + + + Allows to specify the number of time for method calls + + + + + Repeat the method twice. + + + + + Repeat the method once. + + + + + Repeat the method at least once, then repeat as many time as it would like. + + + + + Repeat the method any number of times. + This has special affects in that this method would now ignore orderring. + + + + + Set the range to repeat an action. + + Min. + Max. + + + + Set the amount of times to repeat an action. + + + + + This method must not appear in the replay state. + This has special affects in that this method would now ignore orderring. + + + + + Creates a new instance. + + the repository for this expectation + the recorder for this proxy + the proxy for this expectation + Expectation. + + + + Add constraints for the method's arguments. + + + + + Set a callback method for the last call + + + + + Set a callback method for the last call + + + + + Set a callback method for the last call + + + + + Set a callback method for the last call + + + + + Set a callback method for the last call + + + + + Set a callback method for the last call + + + + + Set a callback method for the last call + + + + + Set a callback method for the last call + + + + + Set a callback method for the last call + + + + + Set a callback method for the last call + + + + + Set a callback method for the last call + + + + + Set a callback method for the last call + + + + + Set a delegate to be called when the expectation is matched. + The delegate return value will be returned from the expectation. + + + + + Set a delegate to be called when the expectation is matched. + The delegate return value will be returned from the expectation. + + + + + Set the return value for the method. + + The object the method will return + IRepeat that defines how many times the method will return this value + + + + Set the return value for the method, but allow to override this return value in the future + + IRepeat that defines how many times the method will return this value + + + + Throws the specified exception when the method is called. + + Exception to throw + + + + Ignores the arguments for this method. Any argument will be matched + againt this method. + + + + + Call the original method on the class, bypassing the mocking layers. + + + + + + Call the original method on the class, optionally bypassing the mocking layers + + + + + + Use the property as a simple property, getting/setting the values without + causing mock expectations. + + + + + Expect last (property) call as property setting, ignore the argument given + + + + + + Expect last (property) call as property setting with a given argument. + + + + + + + Gets the event raiser for the last event + + + + + Set the parameter values for out and ref parameters. + This is done using zero based indexing, and _ignoring_ any non out/ref parameter. + + + + + Repeat the method twice. + + + + + Repeat the method once. + + + + + Repeat the method at least once, then repeat as many time as it would like. + + + + + This method must not appear in the replay state. + + + + + Documentation message for the expectation + + Message + + + + Repeat the method any number of times. + + + + + Set the range to repeat an action. + + Min. + Max. + + + + Set the amount of times to repeat an action. + + + + + Better syntax to define repeats. + + + + + This class will provide hash code for hashtables without needing + to call the GetHashCode() on the object, which may very well be mocked. + This class has no state so it is a singelton to avoid creating a lot of objects + that does the exact same thing. See flyweight patterns. + + + + + Get the hash code for a proxy object without calling GetHashCode() + on the object. + + + + + Compares two instances of mocked objects + + + + + Compare two mocked objects + + + + + The next hash code value for a mock object. + This is safe for multi threading. + + + + + The sole instance of + + + + + Doesn't log anything, just makes happy noises + + + + + Log expectations - allows to see what is going on inside Rhino Mocks + + + + + Logs the expectation as is was recorded + + The invocation. + The expectation. + + + + Logs the expectation as it was recorded + + The invocation. + The expectation. + + + + Logs the unexpected method call. + + The invocation. + The message. + + + + Logs the expectation as is was recorded + + The invocation. + The expectation. + + + + Logs the expectation as it was recorded + + The invocation. + The expectation. + + + + Logs the unexpected method call. + + The invocation. + The message. + + + + This is a dummy type that is used merely to give DynamicProxy the proxy instance that + it needs to create IProxy's types. + + + + + Interface to find the repository of a mocked object + + + + + Return true if it should call the original method on the object + instead of pass it to the message chain. + + The method to call + + + + Register a method to be called on the object directly + + + + + Register a property on the object that will behave as a simple property + + + + + Check if the method was registered as a property method. + + + + + Do get/set on the property, according to need. + + + + + Do add/remove on the event + + + + + Get the subscribers of a spesific event + + + + + Gets the declaring type of the method, taking into acccount the possible generic + parameters that it was created with. + + + + + Clears the state of the object, remove original calls, property behavior, subscribed events, etc. + + + + + Get all the method calls arguments that were made against this object with the specificed + method. + + + Only method calls in replay mode are counted + + + + + Records the method call + + + + + The unique hash code of this mock, which is not related + to the value of the GetHashCode() call on the object. + + + + + Gets the repository. + + + + + Gets the implemented types by this mocked object + + The implemented. + + + + Gets or sets the constructor arguments. + + The constructor arguments. + + + + Create a new instance of + + + + + Return true if it should call the original method on the object + instead of pass it to the message chain. + + The method to call + + + + Register a method to be called on the object directly + + + + + Register a property on the object that will behave as a simple property + Return true if there is already a value for the property + + + + + Check if the method was registered as a property method. + + + + + Do get/set on the property, according to need. + + + + + Do add/remove on the event + + + + + Get the subscribers of a spesific event + + + + + Gets the declaring type of the method, taking into acccount the possible generic + parameters that it was created with. + + + + + Get all the method calls arguments that were made against this object with the specificed + method. + + + + + Only method calls in replay mode are counted + + + + + Records the method call + + + + + + + Clears the state of the object, remove original calls, property behavior, subscribed events, etc. + + + + + The unique hash code of this proxy, which is not related + to the value of the GetHashCode() call on the object. + + + + + Gets the repository. + + + + + Gets or sets the constructor arguments. + + The constructor arguments. + + + + Gets the implemented types by this mocked object + + The implemented. + + + + Range for expected method calls + + + + + Creates a new instance. + + Min. + Max. + + + + Return the string representation of this range. + + + + + Gets or sets the min. + + + + + + Gets or sets the max. + + + + + + Records all the expectations for a mock and + return a ReplayDynamicMockState when Replay() + is called. + + + + + Records all the expectations for a mock + + + + + Different actions on this mock + + + + + Add a method call for this state' mock. + + The invocation for this method + The method that was called + The arguments this method was called with + + + + Verify that this mock expectations have passed. + + + + + Verify that we can move to replay state and move + to the reply state. + + + + + Gets a mock state that match the original mock state of the object. + + + + + Get the options for the last method call + + + + + Set the exception to throw when Verify is called. + This is used to report exception that may have happened but where caught in the code. + This way, they are reported anyway when Verify() is called. + + + + + This method is called to indicate that a property behavior call. + This is done so we generate good error message in the common case of people using + Stubbed properties with Return(). + + + + + Gets the matching verify state for this state + + + + + Get the options for the last method call + + + + + Get the options for the last method call + + + + + Set the exception to throw when Verify is called. + This is used to report exception that may have happened but where caught in the code. + This way, they are reported anyway when Verify() is called. + + + + + This method is called to indicate that a property behavior call. + This is done so we generate good error message in the common case of people using + Stubbed properties with Return(). + + + + + Creates a new instance. + + Repository. + The proxy that generates the method calls + + + + Add a method call for this state' mock. + + The invocation for this method + The method that was called + The arguments this method was called with + + + + Verify that we can move to replay state and move + to the reply state. + + + + + Verify that we can move to replay state and move + to the reply state. + + + + + Verify that this mock expectations have passed. + + + + + Gets a mock state that match the original mock state of the object. + + + + + Asserts the previous method is closed (had an expectation set on it so we can replay it correctly) + + + + + Gets the last expectation. + + + + + Gets the total method calls count. + + + + + Get the options for the last method call + + + + + Gets the matching verify state for this state + + + + + Creates a new instance. + + Repository. + The proxy that generates the method calls + + + + Verify that we can move to replay state and move + to the reply state. + + + + + Gets a mock state that match the original mock state of the object. + + + + + Records all the expectations for a mock and + return a ReplayPartialMockState when Replay() + is called. + + + + + Creates a new instance. + + Repository. + The proxy that generates the method calls + + + + Verify that we can move to replay state and move + to the reply state. + + + + + Gets a mock state that match the original mock state of the object. + + + + + Options for special repeat option + + + + + This method can be called only as many times as the IMethodOptions.Expect allows. + + + + + This method should never be called + + + + + This method can be call any number of times + + + + + This method will call the original method + + + + + This method will call the original method, bypassing the mocking layer + + + + + This method will simulate simple property behavior + + + + + Validate all expectations on a mock and ignores calls to + any method that was not setup properly. + + + + + Validate all expectations on a mock + + + + + The repository for this state + + + + + The proxy object for this state + + + + + Get the options for the last method call + + + + + Creates a new instance. + + The previous state for this method + + + + Add a method call for this state' mock. + + The invocation for this method + The method that was called + The arguments this method was called with + + + + Add a method call for this state' mock. + This allows derived method to cleanly get a the setupresult behavior while adding + their own. + + The invocation for this method + The method that was called + The arguments this method was called with + + + + Set the exception to throw when Verify is called. + This is used to report exception that may have happened but where caught in the code. + This way, they are reported anyway when Verify() is called. + + + + + not relevant + + + + + Verify that this mock expectations have passed. + + + + + Verify that we can move to replay state and move + to the reply state. + + + + + Gets a mock state that match the original mock state of the object. + + + + + Get the options for the last method call + + + + + Gets the matching verify state for this state + + + + + Creates a new instance. + + The previous state for this method + + + + Add a method call for this state' mock. + + The invocation for this method + The method that was called + The arguments this method was called with + + + + Gets a mock state that match the original mock state of the object. + + + + + Validate all expectations on a mock and ignores calls to + any method that was not setup properly. + + + + + Creates a new instance. + + The previous state for this method + + + + Add a method call for this state' mock. + + The invocation for this method + The method that was called + The arguments this method was called with + + + + Gets a mock state that match the original mock state of the object. + + + + + Summary description for RhinoInterceptor. + + + + + Creates a new instance. + + + + + Intercept a method call and direct it to the repository. + + + + + Behave like a stub, all properties and events acts normally, methods calls + return default values by default (but can use expectations to set them up), etc. + + + + + Initializes a new instance of the class. + + The proxy that generates the method calls + Repository. + + + + We don't care much about expectations here, so we will remove the exepctation if + it is not closed. + + + + + Verify that we can move to replay state and move + to the reply state. + + + + + + Validate expectations on recorded methods, but in general completely ignoring them. + Similar to except that it would return a + when BackToRecord is called. + + + + + Initializes a new instance of the class. + + The previous state for this method + + + + Add a method call for this state' mock. + + The invocation for this method + The method that was called + The arguments this method was called with + + + + Gets a mock state that match the original mock state of the object. + + + + + Rudimetry implementation that simply logs methods calls as text. + + + + + Initializes a new instance of the class. + + The writer. + + + + Logs the expectation as is was recorded + + The invocation. + The expectation. + + + + Logs the expectation as it was recorded + + The invocation. + The expectation. + + + + Logs the unexpected method call. + + The invocation. + The message. + + + + Write rhino mocks log info to the trace + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + if set to true [log recorded]. + if set to true [log replayed]. + if set to true [log unexpected]. + + + + Logs the expectation as is was recorded + + The invocation. + The expectation. + + + + Logs the expectation as it was recorded + + The invocation. + The expectation. + + + + Logs the unexpected method call. + + The invocation. + The message. + + + + Writes log information as stack traces about rhino mocks activity + + + + + Allows to redirect output to a different location. + + + + + Logs the expectation as is was recorded + + The invocation. + The expectation. + + + + Logs the expectation as it was recorded + + The invocation. + The expectation. + + + + Logs the unexpected method call. + + The invocation. + The message. + + + + Validate arguments for methods + + + + + Validate that the passed argument is not null. + + The object to validate + The name of the argument + + If the obj is null, an ArgumentNullException with the passed name + is thrown. + + + + + Validate that the arguments are equal. + + Expected args. + Actual Args. + + + + Validate that the two argument are equals, including validation for + when the arguments are collections, in which case it will validate their values. + + + + + This method is safe for use even if any of the objects is a mocked object + that override equals. + + + + + Throw an object already verified when accessed + + + + + Create a new instance of VerifiedMockState + + The previous mock state, used to get the initial record state + + + + Add a method call for this state' mock. + + The invocation for this method + The method that was called + The arguments this method was called with + + + + Verify that this mock expectations have passed. + + + + + Verify that we can move to replay state and move + to the reply state. + + + + + Gets a mock state that match the original mock state of the object. + + + + + Get the options for the last method call + + + + + Set the exception to throw when Verify is called. + This is used to report exception that may have happened but where caught in the code. + This way, they are reported anyway when Verify() is called. + + + + + not relevant + + + + + Gets the matching verify state for this state + + + + + Get the options for the last method call + + + + + Records the actions on all the mocks created by a repository. + + + + + Records the specified call with the specified args on the mocked object. + + + + + Get the expectation for this method on this object with this arguments + + + + + This check the methods that were setup using the SetupResult.For() + or LastCall.Repeat.Any() and that bypass the whole expectation model. + + + + + Gets the all expectations for a mocked object and method combination, + regardless of the expected arguments / callbacks / contraints. + + Mocked object. + Method. + List of all relevant expectation + + + + Gets the all expectations for proxy. + + Mocked object. + List of all relevant expectation + + + + Removes all the repeatable expectations for proxy. + + Mocked object. + + + + Replaces the old expectation with the new expectation for the specified proxy/method pair. + This replace ALL expectations that equal to old expectations. + + Proxy. + Method. + Old expectation. + New expectation. + + + + Adds the recorder and turn it into the active recorder. + + Recorder. + + + + Moves to previous recorder. + + + + + Gets the recorded expectation or null. + + + + + Gets the next expected calls string. + + + + + Moves to parent recorder. + + + + + Set the expectation so it can repeat any number of times. + + + + + Removes the expectation from the recorder + + + + + Clear the replayer to call (and all its chain of replayers) + This also removes it from the list of expectations, so it will never be considered again + + + + + Get the expectation for this method on this object with this arguments + + + + + Gets a value indicating whether this instance has expectations that weren't satisfied yet. + + + true if this instance has expectations; otherwise, false. + + + + + Marker interface used to indicate that this is a partial mock. + + + + + Options for CallOriginalMethod + + + + + No expectation is created, the method will be called directly + + + + + Normal expectation is created, but when the method is later called, it will also call the original method + + + + + Base class for method recorders, handle delegating to inner recorder if needed. + + + + + List of the expected actions on for this recorder + The legal values are: + * Expectations + * Method Recorders + + + + + The current recorder. + + + + + The current replayer; + + + + + The parent recorder of this one, may be null. + + + + + This contains a list of all the replayers that should be ignored + for a spesific method call. A replayer gets into this list by calling + ClearReplayerToCall() on its parent. This list is Clear()ed on each new invocation. + + + + + All the repeatable methods calls. + + + + + Counts the recursion depth of the current expectation search stack + + + + + Creates a new instance. + + + + + Creates a new instance. + + Parent recorder. + Repeatable methods + + + + Records the specified call with the specified args on the mocked object. + + + + + Get the expectation for this method on this object with this arguments + + + + + Gets the all expectations for a mocked object and method combination, + regardless of the expected arguments / callbacks / contraints. + + Mocked object. + Method. + List of all relevant expectation + + + + Gets the all expectations for proxy. + + Mocked object. + List of all relevant expectation + + + + Replaces the old expectation with the new expectation for the specified proxy/method pair. + This replace ALL expectations that equal to old expectations. + + Proxy. + Method. + Old expectation. + New expectation. + + + + Remove the all repeatable expectations for proxy. + + Mocked object. + + + + Set the expectation so it can repeat any number of times. + + + + + Removes the expectation from the recorder + + + + + Adds the recorder and turn it into the active recorder. + + Recorder. + + + + Moves to previous recorder. + + + + + Moves to parent recorder. + + + + + Gets the recorded expectation or null. + + + + + Clear the replayer to call (and all its chain of replayers). + This also removes it from the list of expectations, so it will never be considered again + + + + + Get the expectation for this method on this object with this arguments + + + + + Gets the next expected calls string. + + + + + Handles the real getting of the recorded expectation or null. + + + + + Handle the real execution of this method for the derived class + + + + + Handle the real execution of this method for the derived class + + + + + Handle the real execution of this method for the derived class + + + + + Handle the real execution of this method for the derived class + + + + + Handle the real execution of this method for the derived class + + + + + Handle the real execution of this method for the derived class + + + + + Should this replayer be considered valid for this call? + + + + + This check the methods that were setup using the SetupResult.For() + or LastCall.Repeat.Any() and that bypass the whole expectation model. + + + + + Gets a value indicating whether this instance has expectations that weren't satisfied yet. + + + true if this instance has expectations; otherwise, false. + + + + + Handle the real execution of this method for the derived class + + + + + Ordered collection of methods, methods must arrive in specified order + in order to pass. + + + + + Unordered collection of method records, any expectation that exist + will be matched. + + + + + The parent recorder we have redirected to. + Useful for certain edge cases in orderring. + See: FieldProblem_Entropy for the details. + + + + + Creates a new instance. + + Parent recorder. + Repeatable methods + + + + Creates a new instance. + + + + + Records the specified call with the specified args on the mocked object. + + Mocked object. + Method. + Expectation. + + + + Get the expectation for this method on this object with this arguments + + Invocation for this method + Mocked object. + Method. + Args. + True is the call was recorded, false otherwise + + + + Gets the all expectations for a mocked object and method combination, + regardless of the expected arguments / callbacks / contraints. + + Mocked object. + Method. + List of all relevant expectation + + + + Gets the all expectations for proxy. + + Mocked object. + List of all relevant expectation + + + + Replaces the old expectation with the new expectation for the specified proxy/method pair. + This replace ALL expectations that equal to old expectations. + + Proxy. + Method. + Old expectation. + New expectation. + + + + Handle the real execution of this method for the derived class + + + + + Handles the real getting of the recorded expectation or null. + + + + + Handle the real execution of this method for the derived class + + + + + Gets the next expected calls string. + + + + + Create an exception for an unexpected method call. + + + + + Gets a value indicating whether this instance has expectations that weren't satisfied yet. + + + true if this instance has expectations; otherwise, false. + + + + + Creates a new instance. + + Parent recorder. + Repetable methods + + + + Creates a new instance. + + + + + Handles the real getting of the recorded expectation or null. + + + + + Get the expectation for this method on this object with this arguments + + + + + Gets the next expected calls string. + + + + + Hold an expectation for a method call on an object + + + + + Creates a new instance. + + Proxy. + Method. + Expectation. + + + + Determains if the object equal to this instance + + Obj. + + + + + Gets the hash code. + + + + + + Gets the proxy. + + + + + + Gets the method. + + + + + + Gets the expectation. + + + + + + Holds a pair of mocked object and a method + and allows to compare them against each other. + This allows us to have a distinction between mockOne.MyMethod() and + mockTwo.MyMethod()... + + + + + Creates a new instance. + + Proxy. + Method. + + + + Determains whatever obj equals to this instance. + ProxyMethodPairs are equals when they point to the same /instance/ of + an object, and to the same method. + + Obj. + + + + + Gets the hash code. + + + + + + Gets the proxy. + + + + + + Gets the method. + + + + + + Change the recorder from ordered to unordered and vice versa + + + + + Creates a new instance. + + + + + Disposes this instance. + + + + + Utility class for dealing with messing generics scenarios. + + + + + There are issues with trying to get this to work correctly with open generic types, since this is an edge case, + I am letting the runtime handle it. + + + + + Gets the real type, including de-constructing and constructing the type of generic + methods parameters. + + The type. + The invocation. + + + + + Because we need to support complex types here (simple generics were handled above) we + need to be aware of the following scenarios: + List[T] and List[Foo[T]] + + + + + Utility class for working with method calls. + + + + + Return the string representation of a method call and its arguments. + + The method + The method arguments + Invocation of the method, used to get the generics arguments + Delegate to format the parameter + The string representation of this method call + + + + Return the string representation of a method call and its arguments. + + The invocation of the method, used to get the generic parameters + The method + The method arguments + The string representation of this method call + + + + Delegate to format the argument for the string representation of + the method call. + + + + + Utility to get the default value for a type + + + + + The default value for a type. + Null for reference types and void + 0 for value types. + First element for enums + Note that we need to get the value even for opened generic types, such as those from + generic methods. + + Type. + The invocation. + the default value + + + + Defines constraints and return values for arguments of a mock. + Only use Arg inside a method call on a mock that is recording. + Example: + ExpectCall( + mock.foo( + Arg<int>.Is.GreaterThan(2), + Arg<string>.Is.Anything + )); + Use Arg.Text for string specific constraints + Use Arg<ListClass>.List for list specific constraints + + + + + + Register the predicate as a constraint for the current call. + + The predicate. + default(T) + + Allow you to use code to create constraints + + demo.AssertWasCalled(x => x.Bar(Arg{string}.Matches(a => a.StartsWith("b") && a.Contains("ba")))); + + + + + + Define a complex constraint for this argument by passing several constraints + combined with operators. (Use Is in simple cases.) + Example: Arg<string>.Matches(Is.Equal("Hello") || Text.EndsWith("u")); + + Constraints using Is, Text and List + Dummy to satisfy the compiler + + + + Define a Ref argument. + + Constraints for this argument + value returned by the mock + + + + + Define a out parameter. Use it together with the keyword out and use the + Dummy field available by the return value. + Example: mock.foo( out Arg<string>.Out("hello").Dummy ); + + + + + + + Define a simple constraint for this argument. (Use Matches in simple cases.) + Example: + Arg<int>.Is.Anthing + Arg<string>.Is.Equal("hello") + + + + + Define Constraints on list arguments. + + + + + Use the Arg class (without generic) to define Text constraints + + + + + Evaluate an equal constraint for . + + The object the parameter should equal to + + + + Define constraints on text arguments. + + + + + Used to manage the static state of the Arg<T> class"/> + + + + + Resets the static state + + + + + Returns return values for the out and ref parameters + Note: the array returned has the size of the number of out and ref + argument definitions + + + + + + Returns the constraints for all arguments. + Out arguments have an Is.Anything constraint and are also in the list. + + + + + + What should BackToRecord clear + + + + + Retain all expectations and behaviors and return to mock + + + + + All expectations + + + + + Event subscribers for this instance + + + + + Methods that should be forwarded to the base class implementation + + + + + Properties that should behave like properties + + + + + Remove all the behavior of the object + + + + + This delegate is compatible with the System.Func{T,R} signature + We have to define our own to get compatability with 2.0 + + + + + This class defines a lot of method signatures, which we will use + to allow compatability on net-2.0 + + + + + dummy + + + + + dummy + + + + + dummy + + + + + dummy + + + + + dummy + + + + + dummy + + + + + dummy + + + + + dummy + + + + + dummy + + + + + dummy + + + + + dummy + + + + + dummy + + + dummy + + + + + dummy + + + + + dummy + + + + + dummy + + + + + dummy + + + + + dummy + + + + + dummy + + + + + dummy + + + + + dummy + + + + + dummy + + + + + Allows expectations to be set on methods that should never be called. + For methods with void return value, you need to use LastCall or + DoNotExpect.Call() with a delegate. + + + + + Sets LastCall.Repeat.Never() on /any/ proxy on /any/ repository on the current thread. + This method if not safe for multi threading scenarios. + + + + + Accepts a delegate that will execute inside the method which + LastCall.Repeat.Never() will be applied to. + It is expected to be used with anonymous delegates / lambda expressions and only one + method should be called. + + + IService mockSrv = mocks.CreateMock(typeof(IService)) as IService; + DoNotExpect.Call(delegate{ mockSrv.Stop(); }); + ... + + + + + Allows to set expectation on methods that has return values. + For methods with void return value, you need to use LastCall + + + + + The method options for the last call on /any/ proxy on /any/ repository on the current thread. + This method if not safe for multi threading scenarios, use . + + + + + Accepts a delegate that will execute inside the method, and then return the resulting + instance. + It is expected to be used with anonymous delegates / lambda expressions and only one + method should be called. + + + IService mockSrv = mocks.CreateMock(typeof(IService)) as IService; + Expect.Call(delegate{ mockSrv.Start(); }).Throw(new NetworkException()); + ... + + + + + Get the method options for the last method call on the mockInstance. + + + + + A delegate that can be used to get better syntax on Expect.Call(delegate { foo.DoSomething(); }); + + + + + Allows to set various options for the last method call on + a specified object. + If the method has a return value, it's recommended to use Expect + + + + + Allows to get an interface to work on the last call. + + The mocked object + Interface that allows to set options for the last method call on this object + + + + Set the return value for the method. + + The object the method will return + IRepeat that defines how many times the method will return this value + + + + Set the return value for the method. This overload is needed for LastCall.Return(null) + + The object the method will return + IRepeat that defines how many times the method will return this value + + + + Throws the specified exception when the method is called. + + Exception to throw + + + + Ignores the arguments for this method. Any argument will be matched + againt this method. + + + + + Add constraints for the method's arguments. + + + + + Set a callback method for the last call + + + + + Set a callback method for the last call + + + + + Set a callback method for the last call + + + + + Set a callback method for the last call + + + + + Set a callback method for the last call + + + + + Set a callback method for the last call + + + + + Set a callback method for the last call + + + + + Set a callback method for the last call + + + + + Set a callback method for the last call + + + + + Set a callback method for the last call + + + + + Set a callback method for the last call + + + + + Set a callback method for the last call + + + + + Call the original method on the class, bypassing the mocking layers, for the last call. + + + + + Call the original method on the class, optionally bypassing the mocking layers, for the last call. + + + + + Set a delegate to be called when the expectation is matched. + The delegate return value will be returned from the expectation. + + + + + Gets an interface that will raise the last event when called. + + + + + Set the parameter values for out and ref parameters. + This is done using zero based indexing, and _ignoring_ any non out/ref parameter. + + + + + Documentation message for the expectation + + Message + + + + Use the property as a simple property, getting/setting the values without + causing mock expectations. + + + + + Better syntax to define repeats. + + + + + This is a data structure that is used by + to pass + the current method to the relevant delegate + + + + + Initializes a new instance of the class. + + The invocation. + + + + Gets the args for this method invocation + + + + + Gets or sets the return value for this method invocation + + The return value. + + + + Accessor for the current mocker + + + + + The current mocker + + + + + Creates proxied instances of types. + + + Adds optional new usage: + using(mockRepository.Record()) { + Expect.Call(mock.Method()).Return(retVal); + } + using(mockRepository.Playback()) { + // Execute code + } + N.B. mockRepository.ReplayAll() and mockRepository.VerifyAll() + calls are taken care of by Record/Playback + + + + + This is a map of types to ProxyGenerators. + + + + + This is used to record the last repository that has a method called on it. + + + + + this is used to get to the last proxy on this repository. + + + + + For mock delegates, maps the proxy instance from intercepted invocations + back to the delegate that was originally returned to client code, if any. + + + + + All the proxies in the mock repositories + + + + + This is here because we can't put it in any of the recorders, since repeatable methods + have no orderring, and if we try to handle them using the usual manner, we would get into + wierd situations where repeatable method that was defined in an orderring block doesn't + exists until we enter this block. + + + + + Creates a new instance. + + + + + Move the repository to ordered mode + + + + + Move the repository to un-ordered mode + + + + + Creates a mock for the specified type. + + Type. + Arguments for the class' constructor, if mocking a concrete class + + + + Creates a strict mock for the specified type. + + Type. + Arguments for the class' constructor, if mocking a concrete class + + + + Creates a remoting mock for the specified type. + + Type. + Arguments for the class' constructor, if mocking a concrete class + + + + Creates a strict remoting mock for the specified type. + + Type. + Arguments for the class' constructor, if mocking a concrete class + + + + Creates a remoting mock for the specified type. + + + Arguments for the class' constructor, if mocking a concrete class + + + + + Creates a strict remoting mock for the specified type. + + + Arguments for the class' constructor, if mocking a concrete class + + + + + Creates a mock from several types, with strict semantics. + Only may be a class. + + + + + Creates a strict mock from several types, with strict semantics. + Only may be a class. + + + + + Creates a mock from several types, with strict semantics. + Only may be a class. + + The main type to mock. + Extra interface types to mock. + Arguments for the class' constructor, if mocking a concrete class. + + + + Creates a strict mock from several types, with strict semantics. + Only may be a class. + + The main type to mock. + Extra interface types to mock. + Arguments for the class' constructor, if mocking a concrete class. + + + + Creates a mock from several types, with dynamic semantics. + Only may be a class. + + The main type to mock. + Extra interface types to mock. + + + + Creates a mock from several types, with dynamic semantics. + Only may be a class. + + The main type to mock. + Extra interface types to mock. + Arguments for the class' constructor, if mocking a concrete class. + + + + Creates a dynamic mock for the specified type. + + Type. + Arguments for the class' constructor, if mocking a concrete class + + + + Creates a dynamic mock for the specified type. + + Type. + Arguments for the class' constructor, if mocking a concrete class + + + + Creates a dynamic mock for the specified type. + + + Arguments for the class' constructor, if mocking a concrete class + + + + + Creates a mock object that defaults to calling the class methods. + + Type. + Arguments for the class' constructor. + + + + Creates a mock object that defaults to calling the class methods. + + Type. + Extra interface types to mock. + + + + Creates a mock object that defaults to calling the class methods. + + Type. + Extra interface types to mock. + Arguments for the class' constructor. + + + + Creates a mock object using remoting proxies + + Type to mock - must be MarshalByRefObject + Mock object + Proxy mock can mock non-virtual methods, but not static methods + Creates the mock state for this proxy + + + + Cause the mock state to change to replay, any further call is compared to the + ones that were called in the record state. + + the object to move to replay state + + + + Cause the mock state to change to replay, any further call is compared to the + ones that were called in the record state. + + the object to move to replay state + + + + + Move the mocked object back to record state. + Will delete all current expectations! + + + + + Move the mocked object back to record state. + Optionally, can delete all current expectations, but allows more granularity about how + it would behave with regard to the object state. + + + + + Verify that all the expectations for this object were fulfilled. + + the object to verify the expectations for + + + + Get the method options for the last call on + mockedInstance. + + The mock object + Method options for the last call + + + + Maps an invocation proxy back to the mock object instance that was originally + returned to client code which might have been a delegate to this proxy. + + The mock object proxy from the intercepted invocation + The mock object + + + + This is provided to allow advance extention functionality, where Rhino Mocks standard + functionality is not enough. + + The type to mock + Delegate that create the first state of the mocked object (usualy the record state). + Additional types to be implemented, this can be only interfaces + optional arguments for the constructor + + + + + Method: GetMockedObject + Get an IProxy from a mocked object instance, or throws if the + object is not a mock object. + + + + + Method: GetMockedObjectOrNull + Get an IProxy from a mocked object instance, or null if the + object is not a mock object. + + + + + Pops the recorder. + + + + + Pushes the recorder. + + New recorder. + + + + All the mock objects in this repository will be moved + to record state. + + + + + All the mock objects in this repository will be moved + to record state. + + + + + Replay all the mocks from this repository + + + + + Verify all the mocks from this repository + + + + + Gets the proxy generator for a specific type. Having a single ProxyGenerator + with multiple types linearly degrades the performance so this implementation + keeps one ProxyGenerator per type. + + + + + Set the exception to be thrown when verified is called. + + + + + Creates a mock for the spesified type. + + Arguments for the class' constructor, if mocking a concrete class + + + + Creates a strict mock for the spesified type. + + Arguments for the class' constructor, if mocking a concrete class + + + + Creates a dynamic mock for the specified type. + + Arguments for the class' constructor, if mocking a concrete class + + + + Creates a mock object from several types. + + + + + Creates a strict mock object from several types. + + + + + Create a mock object from several types with dynamic semantics. + + + + + Create a mock object from several types with partial semantics. + + + + + Create a mock object from several types with strict semantics. + + Extra interface types to mock. + Arguments for the class' constructor, if mocking a concrete class + + + + Create a strict mock object from several types with strict semantics. + + Extra interface types to mock. + Arguments for the class' constructor, if mocking a concrete class + + + + Create a mock object from several types with dynamic semantics. + + Extra interface types to mock. + Arguments for the class' constructor, if mocking a concrete class + + + + Create a mock object from several types with partial semantics. + + Extra interface types to mock. + Arguments for the class' constructor, if mocking a concrete class + + + + Create a mock object with from a class that defaults to calling the class methods + + Arguments for the class' constructor, if mocking a concrete class + + + + Create a stub object, one that has properties and events ready for use, and + can have methods called on it. It requires an explicit step in order to create + an expectation for a stub. + + The arguments for constructor. + + + + Create a stub object, one that has properties and events ready for use, and + can have methods called on it. It requires an explicit step in order to create + an expectation for a stub. + + The type. + The arguments for constructor. + + + + + Generates a stub without mock repository + + The arguments for constructor. + + + + + Generates the stub without mock repository + + The type. + The arguments for constructor. + + + + Returns true if the passed mock is currently in replay mode. + + The mock to test. + True if the mock is in replay mode, false otherwise. + + + + Generate a mock object without needing the mock repository + + + + + Determines whether the specified proxy is a stub. + + The proxy. + + + + Register a call on a prperty behavior + + + + + + + + + + + + + + + + Gets the recorder. + + + + + + Gets the replayer for this repository. + + + + + + Gets the last proxy which had a method call. + + + + + Delegate: CreateMockState + This is used internally to cleanly handle the creation of different + RecordMockStates. + + + + + Used for [assembly: InternalsVisibleTo(RhinoMocks.StrongName)] + Used for [assembly: InternalsVisibleTo(RhinoMocks.NormalName)] + + + + + Strong name for the Dynamic Proxy assemblies. Used for InternalsVisibleTo specification. + + + + + Normal name for dynamic proxy assemblies. Used for InternalsVisibleTo specification. + + + + + Logs all method calls for methods + + + + + A set of extension methods that adds Arrange Act Assert mode to Rhino Mocks + + + + + Create an expectation on this mock for this action to occur + + + The mock. + The action. + + + + + Reset all expectations on this mock object + + + The mock. + + + + Reset the selected expectation on this mock object + + + The mock. + The options to reset the expectations on this mock. + + + + Cause the mock state to change to replay, any further call is compared to the + ones that were called in the record state. + + the mocked object to move to replay state + + + + Gets the mock repository for this specificied mock object + + + The mock. + + + + + Create an expectation on this mock for this action to occur + + + + The mock. + The action. + + + + + Tell the mock object to perform a certain action when a matching + method is called. + Does not create an expectation for this method. + + + The mock. + The action. + + + + + Tell the mock object to perform a certain action when a matching + method is called. + Does not create an expectation for this method. + + + + The mock. + The action. + + + + + Gets the arguments for calls made on this mock object and the method that was called + in the action. + + + The mock. + The action. + + + Here we will get all the arguments for all the calls made to DoSomething(int) + + var argsForCalls = foo54.GetArgumentsForCallsMadeOn(x => x.DoSomething(0)) + + + + + + Gets the arguments for calls made on this mock object and the method that was called + in the action and matches the given constraints + + + The mock. + The action. + The setup constraints. + + + Here we will get all the arguments for all the calls made to DoSomething(int) + + var argsForCalls = foo54.GetArgumentsForCallsMadeOn(x => x.DoSomething(0)) + + + + + + Asserts that a particular method was called on this mock object + + + The mock. + The action. + + + + Asserts that a particular method was called on this mock object that match + a particular constraint set. + + + The mock. + The action. + The setup constraints. + + + + Asserts that a particular method was NOT called on this mock object + + + The mock. + The action. + + + + Asserts that a particular method was NOT called on this mock object that match + a particular constraint set. + + + The mock. + The action. + The setup constraints. + + + + Finds the approprite implementation type of this item. + This is the class or an interface outside of the rhino mocks. + + The mocked obj. + + + + + Verifies all expectations on this mock object + + The mock object. + + + + Gets the event raiser for the event that was called in the action passed + + The type of the event source. + The mock object. + The event subscription. + + + + + Raise the specified event using the passed arguments. + The even is extracted from the passed labmda + + The type of the event source. + The mock object. + The event subscription. + The sender. + The instance containing the event data. + + + + Raise the specified event using the passed arguments. + The even is extracted from the passed labmda + + The type of the event source. + The mock object. + The event subscription. + The args. + + + + Fake type that disallow creating it. + Should have been System.Type, but we can't use it. + + + + + Setup method calls to repeat any number of times. + + + + + Get the method options and set the last method call to repeat + any number of times. + This also means that the method would transcend ordering + + + + + Get the method options for the last method call on the mockInstance and set it + to repeat any number of times. + This also means that the method would transcend ordering + + + + + Allows easier access to MockRepository, works closely with Mocker.Current to + allow access to a context where the mock repository is automatially verified at + the end of the code block. + + + + + Initialize a code block where Mocker.Current is initialized. + At the end of the code block, all the expectation will be verified. + This overload will create a new MockRepository. + + The code that will be executed under the mock context + + + + Initialize a code block where Mocker.Current is initialized. + At the end of the code block, all the expectation will be verified. + This overload will create a new MockRepository. + + The mock repository to use, at the end of the code block, VerifyAll() will be called on the repository. + The code that will be executed under the mock context + + + + Create a FluentMocker + + The mock repository to use. + + + + A method with no arguments and no return value that will be called under the mock context. + + + + + FluentMocker implements some kind of fluent interface attempt + for saying "With the Mocks [mocks], Expecting (in same order) [things] verify [that]." + + + + + Interface to verify previously defined expectations + + + + + Verifies if a piece of code + + + + + Defines unordered expectations + + A delegate describing the expectations + an IMockVerifier + + + + Defines ordered expectations + + A delegate describing the expectations + an IMockVerifier + + + + Verifies previously defined expectations + + + + diff --git a/Library/Shared/RhinoMocks/acknowledgements.txt b/Library/Shared/RhinoMocks/acknowledgements.txt new file mode 100644 index 0000000..01af4c1 --- /dev/null +++ b/Library/Shared/RhinoMocks/acknowledgements.txt @@ -0,0 +1,2 @@ +Rhino Mocks is using Castle Dynamic Proxy (http://www.castleproject.org/dynamicproxy/index.html) to handle proxying the types it needs to mock. +The Dynamic Proxy project has been invaluable resource and made creating Rhino Mocks possible. \ No newline at end of file diff --git a/Library/Shared/RhinoMocks/license.txt b/Library/Shared/RhinoMocks/license.txt new file mode 100644 index 0000000..1c8a46e --- /dev/null +++ b/Library/Shared/RhinoMocks/license.txt @@ -0,0 +1,25 @@ +Copyright (c) 2005 - 2008 Ayende Rahien (ayende@ayende.com) +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Ayende Rahien nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/Library/Shared/StructureMap/LICENSE.TXT b/Library/Shared/StructureMap/LICENSE.TXT new file mode 100644 index 0000000..ef08e66 --- /dev/null +++ b/Library/Shared/StructureMap/LICENSE.TXT @@ -0,0 +1,194 @@ +StructureMap + +Copyright 2004-2009 Jeremy D. Miller + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/Library/Shared/StructureMap/Rhino.Mocks.dll b/Library/Shared/StructureMap/Rhino.Mocks.dll new file mode 100644 index 0000000..4b6904b Binary files /dev/null and b/Library/Shared/StructureMap/Rhino.Mocks.dll differ diff --git a/Library/Shared/StructureMap/StructureMap.AutoMocking.dll b/Library/Shared/StructureMap/StructureMap.AutoMocking.dll new file mode 100644 index 0000000..40dc423 Binary files /dev/null and b/Library/Shared/StructureMap/StructureMap.AutoMocking.dll differ diff --git a/Library/Shared/StructureMap/StructureMap.chm b/Library/Shared/StructureMap/StructureMap.chm new file mode 100644 index 0000000..6c91685 Binary files /dev/null and b/Library/Shared/StructureMap/StructureMap.chm differ diff --git a/Library/Shared/StructureMap/StructureMap.dll b/Library/Shared/StructureMap/StructureMap.dll new file mode 100644 index 0000000..a8e84e5 Binary files /dev/null and b/Library/Shared/StructureMap/StructureMap.dll differ diff --git a/Library/Shared/StructureMap/StructureMapDoctor.exe b/Library/Shared/StructureMap/StructureMapDoctor.exe new file mode 100644 index 0000000..0d8a1df Binary files /dev/null and b/Library/Shared/StructureMap/StructureMapDoctor.exe differ diff --git a/LocalTestRun.testrunconfig b/LocalTestRun.testrunconfig new file mode 100644 index 0000000..107aee7 --- /dev/null +++ b/LocalTestRun.testrunconfig @@ -0,0 +1,5 @@ + + + Dies ist eine standardmäßige Testlaufkonfiguration für einen lokalen Testlauf. + + \ No newline at end of file diff --git a/Tests/MiteDesk.Core.Data.Tests/App.config b/Tests/MiteDesk.Core.Data.Tests/App.config new file mode 100644 index 0000000..90b8f64 --- /dev/null +++ b/Tests/MiteDesk.Core.Data.Tests/App.config @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Tests/MiteDesk.Core.Data.Tests/MiteCustomersTests.cs b/Tests/MiteDesk.Core.Data.Tests/MiteCustomersTests.cs new file mode 100644 index 0000000..611297b --- /dev/null +++ b/Tests/MiteDesk.Core.Data.Tests/MiteCustomersTests.cs @@ -0,0 +1,124 @@ +using System; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Rhino.Mocks; +using SixtyNineDegrees.MiteDesk.Core.Infrastructure; +using SixtyNineDegrees.MiteDesk.Core.Model; +using SixtyNineDegrees.MiteDesk.Tools.Connector; + +namespace SixtyNineDegrees.MiteDesk.Core.Data.Tests +{ + [TestClass] + public class MiteCustomersTests + { + + private MiteCustomers Repository; + + [TestInitialize] + public void Setup() + { + var mockRepository = new MockRepository(); + var configurationService = mockRepository.DynamicMock(); + using (mockRepository.Record()) + { + Expect.Call(configurationService.GetAppSettings()).Return(new AppSettings { AccountName = "win", Email = "bandt@69grad.de", Password = "winmite" }).Repeat.Any(); + } + Repository = new MiteCustomers(configurationService); + } + + [TestMethod] + public void GetAllActiveCustomers_liefert_eine_Liste_aller_aktiven_Kunden_zurück() + { + var result = Repository.GetAllActiveCustomers(); + Assert.AreNotEqual(0, result.Count); + } + + [TestMethod] + public void GetAllArchivedCustomers_liefert_eine_Liste_aller_archivierten_Kunden_zurück() + { + // 1 Kunde, 17.08.2009 + var result = Repository.GetAllArchivedCustomers(); + Assert.AreEqual(1, result.Count); + } + + [TestMethod] + public void GetCustomerByID_liefert_einen_bestimmten_Kunden_anhand_seiner_ID_zurück() + { + // 54054, Microsoft, 17.08.2009 + var result = Repository.GetCustomerByID(54054); + Assert.IsNotNull(result); + Assert.AreEqual("Microsoft", result.Name); + } + + [TestMethod] + public void UpdateCustomer_aktualisiert_einen_Kunden_in_der_Mite_Datenbank() + { + + // 54054, Microsoft, 17.08.2009 + var id = 54054; + var note = Guid.NewGuid().ToString(); + + var customer = Repository.GetCustomerByID(id); + + Assert.AreNotEqual(note, customer.Note); + + customer.Note = note; + Repository.UpdateCustomer(customer); + + customer = Repository.GetCustomerByID(id); + Assert.AreEqual(note, customer.Note); + + } + + [TestMethod] + public void DeleteCustomer_löscht_einen_Benutzer_anhand_seiner_ID_aus_der_mite_Datenbank() + { + + var customer = new Customer(); + customer.Note = Guid.NewGuid().ToString(); + customer.Name = Guid.NewGuid().ToString(); + + Repository.CreateCustomer(customer); + + var result = Repository.GetAllActiveCustomers().SingleOrDefault(c => c.Name == customer.Name); + + Assert.IsNotNull(result); + Repository.DeleteCustomer(result.ID); + + Exception exception = null; + try + { + Repository.GetCustomerByID(result.ID); + } + catch (Exception e) + { + exception = e; + } + + Assert.IsNotNull(exception); + Assert.IsInstanceOfType(exception, typeof(MiteConnectorException)); + Assert.IsTrue(exception.Message.IndexOf("404") > -1); + + } + + [TestMethod] + public void CreateCustomer_legt_einen_neuen_Kunden_in_der_Mite_Datenbank_an() + { + + var customer = new Customer(); + customer.Note = Guid.NewGuid().ToString(); + customer.Name = Guid.NewGuid().ToString(); + + Repository.CreateCustomer(customer); + + var result = Repository.GetAllActiveCustomers().SingleOrDefault(c => c.Name == customer.Name); + + Assert.IsNotNull(result); + Assert.AreEqual(customer.Note, result.Note); + + Repository.DeleteCustomer(result.ID); + + } + + } +} diff --git a/Tests/MiteDesk.Core.Data.Tests/MiteDesk.Core.Data.Tests.csproj b/Tests/MiteDesk.Core.Data.Tests/MiteDesk.Core.Data.Tests.csproj new file mode 100644 index 0000000..63ba364 --- /dev/null +++ b/Tests/MiteDesk.Core.Data.Tests/MiteDesk.Core.Data.Tests.csproj @@ -0,0 +1,130 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {DFD37C08-9F36-4534-96B2-4A67AEE85F00} + Library + Properties + SixtyNineDegrees.MiteDesk.Core.Data.Tests + MiteDesk.Core.Data.Tests + v4.0 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + 3.5 + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AllRules.ruleset + + + + + False + ..\..\Library\Shared\RhinoMocks\Rhino.Mocks.dll + + + + 3.5 + + + + + + + + + + + + + + + + + + {AD8740D8-14A7-4A31-8F96-7DF850D3D957} + MiteDesk.Core.Data + + + {733C8DE9-36B5-4C9F-9701-FDD19B64B966} + MiteDesk.Core.Infrastructure + + + {AE69F9FC-F9D4-41D8-9B00-1261F492FB89} + MiteDesk.Core.Model + + + {1B74B3CE-8969-4157-8B86-AEDA27494109} + MiteDesk.Tools.Connector + + + + + + + + False + Microsoft .NET Framework 4 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + false + + + False + Windows Installer 3.1 + true + + + + + \ No newline at end of file diff --git a/Tests/MiteDesk.Core.Data.Tests/MiteProjectsTests.cs b/Tests/MiteDesk.Core.Data.Tests/MiteProjectsTests.cs new file mode 100644 index 0000000..73a885c --- /dev/null +++ b/Tests/MiteDesk.Core.Data.Tests/MiteProjectsTests.cs @@ -0,0 +1,136 @@ +using System; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Rhino.Mocks; +using SixtyNineDegrees.MiteDesk.Core.Infrastructure; +using SixtyNineDegrees.MiteDesk.Core.Model; +using SixtyNineDegrees.MiteDesk.Tools.Connector; + +namespace SixtyNineDegrees.MiteDesk.Core.Data.Tests +{ + [TestClass] + public class MiteProjectsTests + { + + private MiteProjects Repository; + + [TestInitialize] + public void Setup() + { + var mockRepository = new MockRepository(); + var configurationService = mockRepository.DynamicMock(); + using (mockRepository.Record()) + { + Expect.Call(configurationService.GetAppSettings()).Return(new AppSettings { AccountName = "win", Email = "bandt@69grad.de", Password = "winmite" }).Repeat.Any(); + } + Repository = new MiteProjects(configurationService); + } + + [TestMethod] + public void GetAllActiveProjects_liefert_eine_Liste_aller_aktiven_Projekte() + { + // 4 aktive Projekte, 12.09.2009 + var result = Repository.GetAllActiveProjects(); + Assert.AreEqual(5, result.Count); + } + + [TestMethod] + public void GetProjectsByProject_liefert_eine_Liste_aller_Projekte_eines_Kunden() + { + // 54054, Microsoft, 2 Projekte 12.09.2009 + var result = Repository.GetProjectsByCustomer(54054); + Assert.AreEqual(2, result.Count); + } + + + [TestMethod] + public void GetAllArchivedProjects_liefert_eine_Liste_aller_archivierten_Projekte_zurück() + { + // 1 archiviertes Projekt, 18.08.2009 + var result = Repository.GetAllArchivedProjects(); + Assert.AreEqual(1, result.Count); + } + + [TestMethod] + public void GetProjectByID_liefert_eine_bestimmtes_Projekt_anhand_seiner_ID_zurück() + { + // 152453, Snow Leopard, 18.08.2009 + var result = Repository.GetProjectByID(152453); + Assert.IsNotNull(result); + Assert.AreEqual("Snow Leopard", result.Name); + } + + [TestMethod] + public void UpdateProject_aktualisiert_ein_Projekt_in_der_Mite_Datenbank() + { + + // 152453, Snow Leopard, 18.08.2009 + var id = 152453; + var note = Guid.NewGuid().ToString(); + + var project = Repository.GetProjectByID(id); + + Assert.AreNotEqual(note, project.Note); + + project.Note = note; + Repository.UpdateProject(project); + + project = Repository.GetProjectByID(id); + Assert.AreEqual(note, project.Note); + + } + + [TestMethod] + public void DeleteProject_löscht_ein_Projekt_anhand_seiner_ID_aus_der_mite_Datenbank() + { + + var project = new Project(); + project.Note = Guid.NewGuid().ToString(); + project.Name = Guid.NewGuid().ToString(); + project.BudgetType = "minutes"; + + Repository.CreateProject(project); + + var result = Repository.GetAllActiveProjects().SingleOrDefault(c => c.Name == project.Name); + + Assert.IsNotNull(result); + Repository.DeleteProject(result.ID); + + Exception exception = null; + try + { + Repository.GetProjectByID(result.ID); + } + catch (Exception e) + { + exception = e; + } + + Assert.IsNotNull(exception); + Assert.IsInstanceOfType(exception, typeof(MiteConnectorException)); + Assert.IsTrue(exception.Message.IndexOf("404") > -1); + + } + + [TestMethod] + public void CreateProject_legt_ein_neues_Projekt_in_der_Mite_Datenbank_an() + { + + var project = new Project(); + project.Note = Guid.NewGuid().ToString(); + project.Name = Guid.NewGuid().ToString(); + project.BudgetType = "minutes"; + + Repository.CreateProject(project); + + var result = Repository.GetAllActiveProjects().SingleOrDefault(c => c.Name == project.Name); + + Assert.IsNotNull(result); + Assert.AreEqual(project.Note, result.Note); + + Repository.DeleteProject(result.ID); + + } + + } +} diff --git a/Tests/MiteDesk.Core.Data.Tests/MiteServicesTests.cs b/Tests/MiteDesk.Core.Data.Tests/MiteServicesTests.cs new file mode 100644 index 0000000..15f14c9 --- /dev/null +++ b/Tests/MiteDesk.Core.Data.Tests/MiteServicesTests.cs @@ -0,0 +1,125 @@ +using System; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Rhino.Mocks; +using SixtyNineDegrees.MiteDesk.Core.Infrastructure; +using SixtyNineDegrees.MiteDesk.Core.Model; +using SixtyNineDegrees.MiteDesk.Tools.Connector; + +namespace SixtyNineDegrees.MiteDesk.Core.Data.Tests +{ + [TestClass] + public class MiteServicesTests + { + + private MiteServices Repository; + + [TestInitialize] + public void Setup() + { + var mockRepository = new MockRepository(); + var configurationService = mockRepository.DynamicMock(); + using (mockRepository.Record()) + { + Expect.Call(configurationService.GetAppSettings()).Return(new AppSettings { AccountName = "win", Email = "bandt@69grad.de", Password = "winmite" }).Repeat.Any(); + } + Repository = new MiteServices(configurationService); + } + + [TestMethod] + public void GetAllActiveActivities_liefert_alle_aktiven_Leistungen_zurück() + { + // 3 Leistungen, 13.07.2009 + var result = Repository.GetAllActiveActivities(); + Assert.AreEqual(3, result.Count); + } + + [TestMethod] + public void GetAllArchivedActivities_liefert_eine_Liste_aller_archivierten_Leistungen_zurück() + { + // 1 archivierte Leistung, 17.08.2009 + var result = Repository.GetAllArchivedActivities(); + Assert.AreEqual(1, result.Count); + } + + [TestMethod] + public void GetActivityByID_liefert_eine_bestimmte_Leistung_anhand_ihrer_ID_zurück() + { + // 76072, Entwickeln, 17.08.2009 + var result = Repository.GetActivityByID(76072); + Assert.IsNotNull(result); + Assert.AreEqual("Entwickeln", result.Name); + } + + [TestMethod] + public void UpdateActivity_aktualisiert_eine_Leistung_in_der_Mite_Datenbank() + { + + // 76072, Entwickeln, 17.08.2009 + var id = 76072; + var note = Guid.NewGuid().ToString(); + + var activity = Repository.GetActivityByID(id); + + Assert.AreNotEqual(note, activity.Note); + + activity.Note = note; + Repository.UpdateActivity(activity); + + activity = Repository.GetActivityByID(id); + Assert.AreEqual(note, activity.Note); + + } + + [TestMethod] + public void DeleteActivity_löscht_eine_Leistung_anhand_ihrer_ID_aus_der_mite_Datenbank() + { + + var activity = new Activity(); + activity.Note = Guid.NewGuid().ToString(); + activity.Name = Guid.NewGuid().ToString(); + + Repository.CreateActivity(activity); + + var result = Repository.GetAllActiveActivities().SingleOrDefault(c => c.Name == activity.Name); + + Assert.IsNotNull(result); + Repository.DeleteActivity(result.ID); + + Exception exception = null; + try + { + Repository.GetActivityByID(result.ID); + } + catch (Exception e) + { + exception = e; + } + + Assert.IsNotNull(exception); + Assert.IsInstanceOfType(exception, typeof(MiteConnectorException)); + Assert.IsTrue(exception.Message.IndexOf("404") > -1); + + } + + [TestMethod] + public void CreateActivity_legt_eine_neue_Leistung_in_der_Mite_Datenbank_an() + { + + var activity = new Activity(); + activity.Note = Guid.NewGuid().ToString(); + activity.Name = Guid.NewGuid().ToString(); + + Repository.CreateActivity(activity); + + var result = Repository.GetAllActiveActivities().SingleOrDefault(c => c.Name == activity.Name); + + Assert.IsNotNull(result); + Assert.AreEqual(activity.Note, result.Note); + + Repository.DeleteActivity(result.ID); + + } + + } +} diff --git a/Tests/MiteDesk.Core.Data.Tests/MiteTimeEntriesTests.cs b/Tests/MiteDesk.Core.Data.Tests/MiteTimeEntriesTests.cs new file mode 100644 index 0000000..ae5af67 --- /dev/null +++ b/Tests/MiteDesk.Core.Data.Tests/MiteTimeEntriesTests.cs @@ -0,0 +1,222 @@ +using System; +using System.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Rhino.Mocks; +using SixtyNineDegrees.MiteDesk.Core.Infrastructure; +using SixtyNineDegrees.MiteDesk.Core.Model; +using SixtyNineDegrees.MiteDesk.Tools.Connector; + +namespace SixtyNineDegrees.MiteDesk.Core.Data.Tests +{ + [TestClass] + public class MiteTimeEntriesTests + { + + private MiteTimeEntries Repository; + private List TempEntries; + + [TestInitialize] + public void Setup() + { + var mockRepository = new MockRepository(); + var configurationService = mockRepository.DynamicMock(); + using (mockRepository.Record()) + { + Expect.Call(configurationService.GetAppSettings()).Return(new AppSettings { AccountName = "win", Email = "bandt@69grad.de", Password = "winmite" }).Repeat.Any(); + } + Repository = new MiteTimeEntries(configurationService); + TempEntries = new List(); + } + + [TestCleanup] + public void Cleanup() + { + foreach (var entry in TempEntries) + { + Repository.DeleteTimeEntry(entry); + } + } + + [TestMethod] + public void GetTimeEntries_liefert_alle_Einträge_zu_einem_Datum() + { + // 13.07.2009 - 3 Einträge + var result = Repository.GetTimeEntriesByDate(new DateTime(2009, 7, 13)); + Assert.AreEqual(3, result.Count); + } + + [TestMethod] + public void GetTimeEntriesByActivityID_liefert_alle_Einträge_zu_einer_Leistung() + { + // Entwickeln - 76072, 15.12.2009 + var result = Repository.GetTimeEntriesByActivityID(76072); + Assert.AreEqual(15, result.Count); + } + + [TestMethod] + public void GetTimeEntriesByProjectID_liefert_alle_Einträge_zu_einem_Projekt() + { + // Windows 7 - 152451, 5 Einträge, 15.12.2009 + var result = Repository.GetTimeEntriesByProjectID(152451); + Assert.AreEqual(5, result.Count); + } + + [TestMethod] + public void GetTimeEntry_liefert_einen_bestimmten_Eintrag_anhand_seiner_ID() + { + // 1952882 - vom 13.07.2009 + var result = Repository.GetTimeEntryByID(1952882); + Assert.AreEqual("Taskbar", result.Note); + } + + [TestMethod] + public void CreateTimeEntry_erstellt_einen_neuen_Eintrag() + { + + var entry = new TimeEntry + { + ActivityID = 76072, + ProjectID = 152455, + Minutes = 3, + Date = new DateTime(2009, 7, 12), + Note = Guid.NewGuid().ToString(), + ID = 0 + }; + + Repository.CreateTimeEntry(ref entry); + + Assert.AreNotEqual(0, entry.ID); + + TempEntries.Add(entry.ID); + + } + + [TestMethod] + public void DeleteTimeEntry_löscht_einen_vorhandenen_Eintrag() + { + + // Anlegen + var entry = new TimeEntry + { + ActivityID = 76072, + ProjectID = 152455, + Minutes = 3, + Date = new DateTime(2009, 7, 12), + Note = Guid.NewGuid().ToString(), + ID = 0 + }; + + Repository.CreateTimeEntry(ref entry); + Assert.AreNotEqual(0, entry.ID); + + // Löschen + Repository.DeleteTimeEntry(entry.ID); + + // Server muss 404 liefern (Der Remoteserver hat einen Fehler zurückgegeben: (404) Nicht gefunden..) + MiteConnectorException exception = null; + try + { + Repository.GetTimeEntryByID(entry.ID); + } + catch (MiteConnectorException e) + { + exception = e; + } + Assert.IsNotNull(exception); + + } + + [TestMethod] + public void UpdateTimeEntry_Aktualisiert_einen_vorhandenen_Eintrag() + { + + // Eintrag steht am 7.7.09 fest drin + + var entry = new TimeEntry + { + Note = Guid.NewGuid().ToString(), + ID = 1954379, + ActivityID = 76073, + ProjectID = 152451, + Minutes = 0, + Date = new DateTime(2009, 7, 7) + }; + + Repository.UpdateTimeEntry(entry); + + // Prüfen ob der Text aktualisiert wurde + var result = Repository.GetTimeEntryByID(entry.ID); + Assert.AreEqual(entry.Note, result.Note); + + } + + [TestMethod] + public void StartStopwatch_startet_die_Stopuhr_auf_einen_angegebenen_Zeiteintrag() + { + // Eintrag 1954379 steht am 7.7.09 fest drin + + // Check + var result = Repository.GetTimeEntryCurrentlyTrackedByStopwatch(); + Assert.IsNull(result); + + // Start + Repository.StartStopwatch(1954379); + + // Check + result = Repository.GetTimeEntryCurrentlyTrackedByStopwatch(); + Assert.AreEqual(1954379, result.ID); + + // Stop + Repository.StopStopwatch(1954379); + } + + [TestMethod] + public void StopStopwatch_stoppt_die_Stopuhr_auf_einen_angegebenen_Zeiteintrag() + { + // Eintrag 1954379 steht am 7.7.09 fest drin + + // Start + Repository.StartStopwatch(1954379); + + // Check + var result = Repository.GetTimeEntryCurrentlyTrackedByStopwatch(); + Assert.AreEqual(1954379, result.ID); + + // Stop + Repository.StopStopwatch(1954379); + + // Check + result = Repository.GetTimeEntryCurrentlyTrackedByStopwatch(); + Assert.IsNull(result); + } + + [TestMethod] + public void GetTimeEntryCurrentlyTrackedByStopwatch_liefert_den_Zeiteintrag_der_gerade_gestoppt_wird() + { + // Eintrag 1954379 steht am 7.7.09 fest drin + + // Check + var result = Repository.GetTimeEntryCurrentlyTrackedByStopwatch(); + Assert.IsNull(result); + + // Start + Repository.StartStopwatch(1954379); + + // Check + result = Repository.GetTimeEntryCurrentlyTrackedByStopwatch(); + Assert.AreEqual(1954379, result.ID); + + // Stop + Repository.StopStopwatch(1954379); + } + + [TestMethod] + public void GetTimeEntryDatesByRange_liefert_eine_Liste_von_Dates_in_einem_bestimmten_Datumsbereich() + { + var result = Repository.GetTimeEntryDatesByRange(new DateTime(2009, 8, 1), new DateTime(2009, 8, 31), 18352); + Assert.AreEqual(7, result.Count); + Assert.AreEqual(new DateTime(2009, 8, 3), result[6]); + } + + } +} diff --git a/Tests/MiteDesk.Core.Data.Tests/MiteUsersTests.cs b/Tests/MiteDesk.Core.Data.Tests/MiteUsersTests.cs new file mode 100644 index 0000000..a49b83d --- /dev/null +++ b/Tests/MiteDesk.Core.Data.Tests/MiteUsersTests.cs @@ -0,0 +1,35 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Rhino.Mocks; +using SixtyNineDegrees.MiteDesk.Core.Infrastructure; +using SixtyNineDegrees.MiteDesk.Core.Model; + +namespace SixtyNineDegrees.MiteDesk.Core.Data.Tests +{ + [TestClass] + public class MiteUsersTests + { + + private MiteUsers Repository; + + [TestInitialize] + public void Setup() + { + var mockRepository = new MockRepository(); + var configurationService = mockRepository.DynamicMock(); + using (mockRepository.Record()) + { + Expect.Call(configurationService.GetAppSettings()).Return(new AppSettings { AccountName = "win", Email = "bandt@69grad.de", Password = "winmite" }).Repeat.Any(); + } + Repository = new MiteUsers(configurationService); + } + + [TestMethod] + public void GetAuthenticatedUser_liefert_den_aktuell_angemeldeten_Benutzer() + { + // Benutzer 18352, bandt@69grad.de + var result = Repository.GetAuthenticatedUser(); + Assert.AreEqual(18352, result.ID); + } + + } +} diff --git a/Tests/MiteDesk.Core.Data.Tests/Properties/AssemblyInfo.cs b/Tests/MiteDesk.Core.Data.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..83e3ba6 --- /dev/null +++ b/Tests/MiteDesk.Core.Data.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,34 @@ +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 mit einer Assembly verknüpft sind. +[assembly: AssemblyTitle("MiteDesk.Core.Data.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("MiteDesk.Core.Data.Tests")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("64dd6542-07d0-4b83-9ed3-a1e3bf3b944b")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Revisions- und Buildnummern +// übernehmen, indem Sie "*" eingeben: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Tests/MiteDesk.Core.Data.Tests/TestAccounts.txt b/Tests/MiteDesk.Core.Data.Tests/TestAccounts.txt new file mode 100644 index 0000000..6ee6e73 --- /dev/null +++ b/Tests/MiteDesk.Core.Data.Tests/TestAccounts.txt @@ -0,0 +1,4 @@ +• "time_tracker" Zeiterfasser public@bandt.ws aae01461 +• "coworker" Standard-Nutzer mail@thomasbandt.de 259c8768 +• "admin" Administrator info@69grad.de 4cf82f2e +• "owner" Besitzer des Accounts bandt@69grad.de winmite \ No newline at end of file diff --git a/Tests/MiteDesk.Core.Infrastructure.Tests/ConfigurationServiceTests.cs b/Tests/MiteDesk.Core.Infrastructure.Tests/ConfigurationServiceTests.cs new file mode 100644 index 0000000..6bbc3b6 --- /dev/null +++ b/Tests/MiteDesk.Core.Infrastructure.Tests/ConfigurationServiceTests.cs @@ -0,0 +1,431 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Rhino.Mocks; +using SixtyNineDegrees.MiteDesk.Core.Model; + +namespace SixtyNineDegrees.MiteDesk.Core.Infrastructure.Tests +{ + + [TestClass] + public class ConfigurationServiceTests + { + + #region Setup + + private ConfigurationService ConfigurationService; + private MockRepository MockRepository; + private IRegistryService RegistryService; + private string ApplicationID; + private IEncryptionService EncryptionService; + + [TestInitialize] + public void Setup() + { + MockRepository = new MockRepository(); + ApplicationID = new Guid("32141871-78de-40b2-a715-49ced5c0c872").ToString(); + EncryptionService = new EncryptionService(); + RegistryService = MockRepository.DynamicMock(); + ConfigurationService = new ConfigurationService(RegistryService, EncryptionService, ApplicationID, "executablepath"); + using(MockRepository.Record()) + { + Expect.Call(RegistryService.GetApplicationRegistryKeyValue("D")).Return("T/jSvAFKeMsiOAk5IfqEEg==").Repeat.Any(); // Klartext: Email + Expect.Call(RegistryService.GetApplicationRegistryKeyValue("C")).Return("gnrAw6R9MIEu7ODFX5vFP8oG3ViER5xKMPwe74iQBUQ=").Repeat.Any(); // Klartext: AccountName + Expect.Call(RegistryService.GetApplicationRegistryKeyValue("F")).Return("drn0I4314VvDkPx/CLSu1kBHLLW1D6Fy7HlYwAMcMyw=").Repeat.Any(); // Klartext: Password + Expect.Call(RegistryService.GetApplicationRegistryKeyValue("E")).Return("lW0Qqcm99mIdHsRVHRsXdg==").Repeat.Any(); // Klartext: APIKey + Expect.Call(RegistryService.GetApplicationRegistryKeyValue("G")).Return("true").Repeat.Any(); + Expect.Call(RegistryService.GetApplicationRegistryKeyValue("H")).Return("true").Repeat.Any(); + Expect.Call(RegistryService.ApplicationRegistryKeyExists()).Return(true).Repeat.Any(); + Expect.Call(RegistryService.GetApplicationRegistryKeyValue(@"Software\Microsoft\Windows\CurrentVersion\Run", "mite.desk")).Return("executablepath").Repeat.Any(); + Expect.Call(RegistryService.GetApplicationRegistryKeyValue("I")).Return("true").Repeat.Any(); + Expect.Call(RegistryService.GetApplicationRegistryKeyValue("J")).Return("14").Repeat.Any(); + Expect.Call(RegistryService.GetApplicationRegistryKeyValue("K")).Return(new DateTime(2009, 9, 14).ToShortDateString()).Repeat.Any(); + Expect.Call(RegistryService.GetApplicationRegistryKeyValue("L")).Return("12345").Repeat.Any(); + Expect.Call(RegistryService.GetApplicationRegistryKeyValue("M")).Return("54321").Repeat.Any(); + Expect.Call(RegistryService.GetApplicationRegistryKeyValue("N")).Return("en-US").Repeat.Any(); + Expect.Call(RegistryService.GetApplicationRegistryKeyValue("O")).Return("false").Repeat.Any(); + Expect.Call(RegistryService.GetApplicationRegistryKeyValue("P")).Return("proxy").Repeat.Any(); + Expect.Call(RegistryService.GetApplicationRegistryKeyValue("Q")).Return("8080").Repeat.Any(); + Expect.Call(RegistryService.GetApplicationRegistryKeyValue("R")).Return("N6bmZIEuU+0a3j3UB186eQ==").Repeat.Any(); + Expect.Call(RegistryService.GetApplicationRegistryKeyValue("S")).Return("ygNNVm9GOfo5hZPeIrv67RunNJVsLJg+v9JlAReQE7Q=").Repeat.Any(); + Expect.Call(RegistryService.GetApplicationRegistryKeyValue("T")).Return("true").Repeat.Any(); + Expect.Call(RegistryService.GetApplicationRegistryKeyValue("U")).Return("true").Repeat.Any(); + Expect.Call(RegistryService.GetApplicationRegistryKeyValue("V")).Return("true").Repeat.Any(); + } + } + + #endregion + + #region UpdateAppSettings + + [TestMethod] + public void UpdateAppSettings_liefert_einen_Fehler_mit_Key_AccountName_wenn_dieser_leer_ist() + { + var settings = new AppSettings { AccountName = "" }; + var result = ConfigurationService.UpdateAppSettings(settings); + Assert.IsTrue(result.ContainsKey("AccountName")); + } + + [TestMethod] + public void UpdateAppSettings_liefert_einen_Fehler_mit_Key_Email_wenn_diese_leer_ist_und_AuthenticationType_EmailAndPassword() + { + var settings = new AppSettings { Email = "", AuthenticationType = AuthenticationType.EmailAndPassword }; + var result = ConfigurationService.UpdateAppSettings(settings); + Assert.IsTrue(result.ContainsKey("Email")); + } + + [TestMethod] + public void UpdateAppSettings_liefert_einen_Fehler_mit_Key_Email_wenn_diese_falsch_formatiert_istund_AuthenticationType_EmailAndPassword() + { + var settings = new AppSettings { Email = "falsch", AuthenticationType = AuthenticationType.EmailAndPassword }; + var result = ConfigurationService.UpdateAppSettings(settings); + Assert.IsTrue(result.ContainsKey("Email")); + } + + [TestMethod] + public void UpdateAppSettings_liefert_einen_Fehler_mit_Key_Password_wenn_das_Passwort_nicht_angegeben_ist_und_AuthenticationType_EmailAndPassword() + { + var settings = new AppSettings { Password = "", AuthenticationType = AuthenticationType.EmailAndPassword }; + var result = ConfigurationService.UpdateAppSettings(settings); + Assert.IsTrue(result.ContainsKey("Password")); + } + + [TestMethod] + public void UpdateAppSettings_liefert_einen_Fehler_mit_Key_APIKey_wenn_dieser_leer_ist_und_AuthenticationType_APIKey() + { + var settings = new AppSettings { APIKey = "", AuthenticationType = AuthenticationType.APIKey }; + var result = ConfigurationService.UpdateAppSettings(settings); + Assert.IsTrue(result.ContainsKey("APIKey")); + } + + [TestMethod] + public void UpdateAppSettings_liefert_keinen_Fehler_wenn_alle_Angaben_korrekt_ausgefüllt_sind() + { + var settings = new AppSettings { Password = "test", AccountName = "test", Email = "info@test.com" }; + var result = ConfigurationService.UpdateAppSettings(settings); + Assert.AreEqual(0, result.Count); + } + + [TestMethod] + public void UpdateAppSettings_speichert_die_Emailadresse_wenn_angegeben_in_der_Registry() + { + var settings = new AppSettings { Password = "test", AccountName = "test", Email = "info@test.com" }; + ConfigurationService.UpdateAppSettings(settings); + RegistryService.AssertWasCalled(r => r.SetApplicationRegistryKeyValue("D", "H2KJPyLdpQ6aO3A83t3k54y48/oFoCfi49Ac7sISTDc=")); + } + + [TestMethod] + public void UpdateAppSettings_speichert_das_Passwort_wenn_angegeben_verschlüsselt_in_der_Registry() + { + var settings = new AppSettings { Password = "Password", AccountName = "test", Email = "info@test.com" }; + ConfigurationService.UpdateAppSettings(settings); + RegistryService.AssertWasCalled(r => r.SetApplicationRegistryKeyValue("F", "drn0I4314VvDkPx/CLSu1kBHLLW1D6Fy7HlYwAMcMyw=")); + } + + [TestMethod] + public void UpdateAppSettings_speichert_die_Emailadresse_wenn_nicht_angegeben_als_Leerstring_in_der_Registry() + { + var settings = new AppSettings { AccountName = "AccountName", APIKey = "Key", AuthenticationType = AuthenticationType.APIKey}; + ConfigurationService.UpdateAppSettings(settings); + RegistryService.AssertWasCalled(r => r.SetApplicationRegistryKeyValue("D", string.Empty)); + } + + [TestMethod] + public void UpdateAppSettings_speichert_das_Passwort_wennn_nicht_angegeben_als_Leerstring_in_der_Registry() + { + var settings = new AppSettings { AccountName = "AccountName", APIKey = "Key", AuthenticationType = AuthenticationType.APIKey }; + ConfigurationService.UpdateAppSettings(settings); + RegistryService.AssertWasCalled(r => r.SetApplicationRegistryKeyValue("F", string.Empty)); + } + + [TestMethod] + public void UpdateAppSettings_speichert_den_ApiKey_wenn_angegeben_verschlüsselt_in_der_Registry() + { + var settings = new AppSettings { AccountName = "AccountName", APIKey = "APIKey", AuthenticationType = AuthenticationType.APIKey }; + ConfigurationService.UpdateAppSettings(settings); + RegistryService.AssertWasCalled(r => r.SetApplicationRegistryKeyValue("E", "lW0Qqcm99mIdHsRVHRsXdg==")); + } + + [TestMethod] + public void UpdateAppSettings_speichert_den_ApiKey_als_Leerstring_in_der_Registry_wenn_nicht_angegeben() + { + var settings = new AppSettings { Password = "test", AccountName = "AccountName", Email = "info@test.com" }; + ConfigurationService.UpdateAppSettings(settings); + RegistryService.AssertWasCalled(r => r.SetApplicationRegistryKeyValue("E", string.Empty)); + } + + [TestMethod] + public void UpdateAppSettings_speichert_den_Accountnamen_in_der_Registry() + { + var settings = new AppSettings { Password = "test", AccountName = "AccountName", Email = "info@test.com" }; + ConfigurationService.UpdateAppSettings(settings); + RegistryService.AssertWasCalled(r => r.SetApplicationRegistryKeyValue("C", "gnrAw6R9MIEu7ODFX5vFP8oG3ViER5xKMPwe74iQBUQ=")); + } + + [TestMethod] + public void UpdateAppSettings_speichert_die_Info_ob_das_Fenster_beim_Schließen_minimiert_wird_in_der_Registry() + { + var settings = new AppSettings { Password = "test", AccountName = "AccountName", Email = "info@test.com", MinimizeByClosing = false}; + ConfigurationService.UpdateAppSettings(settings); + RegistryService.AssertWasCalled(r => r.SetApplicationRegistryKeyValue("H", Boolean.FalseString)); + } + + [TestMethod] + public void UpdateAppSettings_speichert_den_Pfad_zur_Anwendung_wenn_die_Anwendung_per_Autostart_geladen_werden_soll_in_der_Registry() + { + var settings = new AppSettings { Password = "test", AccountName = "AccountName", Email = "info@test.com", Autostart = true }; + ConfigurationService.UpdateAppSettings(settings); + RegistryService.AssertWasCalled(r => r.SetApplicationRegistryKeyValue(@"Software\Microsoft\Windows\CurrentVersion\Run", "mite.desk", "executablepath")); + } + + [TestMethod] + public void UpdateAppSettings_löscht_den_Pfad_zur_Anwendung_wenn_die_Anwendung_nicht_per_Autostart_geladen_werden_soll_aus_der_Registry() + { + var settings = new AppSettings { Password = "test", AccountName = "AccountName", Email = "info@test.com", Autostart = false }; + ConfigurationService.UpdateAppSettings(settings); + RegistryService.AssertWasCalled(r => r.DeleteApplicationRegistryKeyValue(@"Software\Microsoft\Windows\CurrentVersion\Run", "mite.desk")); + } + + [TestMethod] + public void UpdateAppSettings_speichert_die_Info_ob_die_Anwendung_minimiert_gestartet_werden_soll_in_der_Registry() + { + var settings = new AppSettings { Password = "test", AccountName = "AccountName", Email = "info@test.com", StartMinimized = false }; + ConfigurationService.UpdateAppSettings(settings); + RegistryService.AssertWasCalled(r => r.SetApplicationRegistryKeyValue("I", Boolean.FalseString)); + } + + [TestMethod] + public void UpdateAppSettings_speichert_das_zuletzt_gewählte_Projekt_in_der_Registry() + { + var settings = new AppSettings { Password = "test", AccountName = "AccountName", Email = "info@test.com", SelectedProjectID = 123 }; + ConfigurationService.UpdateAppSettings(settings); + RegistryService.AssertWasCalled(r => r.SetApplicationRegistryKeyValue("L", "123")); + } + + [TestMethod] + public void UpdateAppSettings_speichert_die_zuletzt_gewählte_Leistung_in_der_Registry() + { + var settings = new AppSettings { Password = "test", AccountName = "AccountName", Email = "info@test.com", SelectedActivityID = 456 }; + ConfigurationService.UpdateAppSettings(settings); + RegistryService.AssertWasCalled(r => r.SetApplicationRegistryKeyValue("M", "456")); + } + + [TestMethod] + public void UpdateAppSettings_speichert_die_Culture_in_der_Registry() + { + var settings = new AppSettings { Password = "test", AccountName = "AccountName", Email = "info@test.com", Culture = "de-DE" }; + ConfigurationService.UpdateAppSettings(settings); + RegistryService.AssertWasCalled(r => r.SetApplicationRegistryKeyValue("N", "de-DE")); + } + + [TestMethod] + public void UpdateAppSettings_speichert_die_Info_ob_ein_Proxy_verwendet_wird_in_der_Registry() + { + var settings = new AppSettings { Password = "test", AccountName = "AccountName", Email = "info@test.com", UseProxy = true }; + ConfigurationService.UpdateAppSettings(settings); + RegistryService.AssertWasCalled(r => r.SetApplicationRegistryKeyValue("O", true.ToString())); + } + + [TestMethod] + public void UpdateAppSettings_speichert_den_Proxyserver_in_der_Registry() + { + var settings = new AppSettings { Password = "test", AccountName = "AccountName", Email = "info@test.com", ProxyServer = "proxy" }; + ConfigurationService.UpdateAppSettings(settings); + RegistryService.AssertWasCalled(r => r.SetApplicationRegistryKeyValue("P", "proxy")); + } + + [TestMethod] + public void UpdateAppSettings_speichert_den_Proxyport_in_der_Registry() + { + var settings = new AppSettings { Password = "test", AccountName = "AccountName", Email = "info@test.com", ProxyPort = 8080 }; + ConfigurationService.UpdateAppSettings(settings); + RegistryService.AssertWasCalled(r => r.SetApplicationRegistryKeyValue("Q", "8080")); + } + + [TestMethod] + public void UpdateAppSettings_speichert_den_ProxyUser_in_der_Registry() + { + var settings = new AppSettings { Password = "test", AccountName = "AccountName", Email = "info@test.com", ProxyUser = "user" }; + ConfigurationService.UpdateAppSettings(settings); + RegistryService.AssertWasCalled(r => r.SetApplicationRegistryKeyValue("R", "N6bmZIEuU+0a3j3UB186eQ==")); + } + + [TestMethod] + public void UpdateAppSettings_speichert_das_ProxyPasswort_in_der_Registry() + { + var settings = new AppSettings { Password = "test", AccountName = "AccountName", Email = "info@test.com", ProxyPassword = "password" }; + ConfigurationService.UpdateAppSettings(settings); + RegistryService.AssertWasCalled(r => r.SetApplicationRegistryKeyValue("S", "ygNNVm9GOfo5hZPeIrv67RunNJVsLJg+v9JlAReQE7Q=")); + } + + [TestMethod] + public void UpdateAppSettings_speichert_die_Info_ob_die_Stoppuhr_beim_Schließen_automatisch_gestoppt_wird_in_der_Registry() + { + var settings = new AppSettings { Password = "test", AccountName = "AccountName", Email = "info@test.com", StopStopwatchByClosing = true }; + ConfigurationService.UpdateAppSettings(settings); + RegistryService.AssertWasCalled(r => r.SetApplicationRegistryKeyValue("T", true.ToString())); + } + + [TestMethod] + public void UpdateAppSettings_speichert_die_Info_ob_beim_Schließen_gefragt_wird_ob_die_Stoppuhr_gestoppt_wird_in_der_Registry() + { + var settings = new AppSettings { Password = "test", AccountName = "AccountName", Email = "info@test.com", AskForStoppingStopwatchByClosing = false }; + ConfigurationService.UpdateAppSettings(settings); + RegistryService.AssertWasCalled(r => r.SetApplicationRegistryKeyValue("U", false.ToString())); + } + + [TestMethod] + public void UpdateAppSettings_speichert_die_Info_ob_Zeiteinträge_absteigend_sortiert_werden_sollen() + { + var settings = new AppSettings { Password = "test", AccountName = "AccountName", Email = "info@test.com", SortTimeEntriesDescending = false }; + ConfigurationService.UpdateAppSettings(settings); + RegistryService.AssertWasCalled(r => r.SetApplicationRegistryKeyValue("V", false.ToString())); + } + + #endregion + + #region GetAppSettings + + [TestMethod] + public void GetAppSettings_liefert_die_in_der_Registry_gespeicherte_Email_Adresse() + { + var result = ConfigurationService.GetAppSettings(); + Assert.AreEqual("Email", result.Email); + } + + [TestMethod] + public void GetAppSettings_liefert_den_in_der_Registry_gespeicherten_ApiKey_entschlüsselt() + { + var result = ConfigurationService.GetAppSettings(); + Assert.AreEqual("APIKey", result.APIKey); + } + + [TestMethod] + public void GetAppSettings_liefert_den_in_der_Registry_gespeicherten_Accountnamen() + { + var result = ConfigurationService.GetAppSettings(); + Assert.AreEqual("AccountName", result.AccountName); + } + + [TestMethod] + public void GetAppSettings_liefert_die_in_der_Registry_gespeicherte_Info_ob_das_Fenster_beim_Schließen_minimiert_wird() + { + var result = ConfigurationService.GetAppSettings(); + Assert.IsTrue(result.MinimizeByClosing); + } + + [TestMethod] + public void GetAppSettings_liefert_die_in_der_Registry_gespeicherte_Info_ob_die_Anwendung_per_Autostart_gestartet_wird() + { + var result = ConfigurationService.GetAppSettings(); + Assert.IsTrue(result.Autostart); + } + + [TestMethod] + public void GetAppSettings_liefert_die_in_der_Registry_gespeicherte_Info_ob_die_Anwendung_minimiert_gestartet_wird() + { + var result = ConfigurationService.GetAppSettings(); + Assert.IsTrue(result.StartMinimized); + } + + [TestMethod] + public void GetAppSettings_liefert_den_Tag_an_dem_der_letzte_UpdateCheck_durchgeführt_wurde() + { + var result = ConfigurationService.GetAppSettings(); + Assert.AreEqual(new DateTime(2009, 9, 14), result.LastUpdateCheck); + } + + [TestMethod] + public void GetAppSettings_liefert_die_ID_der_zuletzt_gewählten_Leistung() + { + var result = ConfigurationService.GetAppSettings(); + Assert.AreEqual(54321, result.SelectedActivityID); + } + + [TestMethod] + public void GetAppSettings_liefert_die_ID_des_zuletzt_gewählten_Projektes() + { + var result = ConfigurationService.GetAppSettings(); + Assert.AreEqual(12345, result.SelectedProjectID); + } + + [TestMethod] + public void GetAppSettings_liefert_die_aktuelle_Culture_wenn_in_Registry_vorhanden() + { + var result = ConfigurationService.GetAppSettings(); + Assert.AreEqual("en-US", result.Culture); + } + + [TestMethod] + public void GetAppSettings_liefert_als_aktuelle_Culture_deDE_wenn_nicht_in_Registry_gesetzt() + { + var tmpRegistryService = MockRepository.DynamicMock(); + using (MockRepository.Record()) + { + Expect.Call(tmpRegistryService.ApplicationRegistryKeyExists()).Return(true).Repeat.Any(); + Expect.Call(tmpRegistryService.GetApplicationRegistryKeyValue("N")).Return("").Repeat.Any(); + } + var tmpConfigurationService = new ConfigurationService(tmpRegistryService, EncryptionService, ApplicationID, "executablepath"); + var result = tmpConfigurationService.GetAppSettings(); + Assert.AreEqual("de-DE", result.Culture); + } + + [TestMethod] + public void GetAppSettings_liefert_den_Proxyserver() + { + var result = ConfigurationService.GetAppSettings(); + Assert.IsFalse(result.UseProxy); + } + + [TestMethod] + public void GetAppSettings_liefert_den_Proxyport() + { + var result = ConfigurationService.GetAppSettings(); + Assert.AreEqual("proxy", result.ProxyServer); + } + + [TestMethod] + public void GetAppSettings_liefert_die_Info_ob_ein_Proxy_verwendet_werden_soll() + { + var result = ConfigurationService.GetAppSettings(); + Assert.AreEqual(8080, result.ProxyPort); + } + + [TestMethod] + public void GetAppSettings_liefert_den_Proxyuser() + { + var result = ConfigurationService.GetAppSettings(); + Assert.AreEqual("user", result.ProxyUser); + } + + [TestMethod] + public void GetAppSettings_liefert_das_Proxypasswort() + { + var result = ConfigurationService.GetAppSettings(); + Assert.AreEqual("password", result.ProxyPassword); + } + + [TestMethod] + public void GetAppSettings_liefert_StopStopwatchByClosing() + { + var result = ConfigurationService.GetAppSettings(); + Assert.IsTrue(result.StopStopwatchByClosing); + } + + [TestMethod] + public void GetAppSettings_liefert_AskForStoppingStopwatchByClosing() + { + var result = ConfigurationService.GetAppSettings(); + Assert.IsTrue(result.AskForStoppingStopwatchByClosing); + } + + [TestMethod] + public void GetAppSettings_liefert_SortTimeEntriesDescending() + { + var result = ConfigurationService.GetAppSettings(); + Assert.IsTrue(result.SortTimeEntriesDescending); + } + + #endregion + + } +} diff --git a/Tests/MiteDesk.Core.Infrastructure.Tests/MiteDesk.Core.Infrastructure.Tests.csproj b/Tests/MiteDesk.Core.Infrastructure.Tests/MiteDesk.Core.Infrastructure.Tests.csproj new file mode 100644 index 0000000..ec5cd36 --- /dev/null +++ b/Tests/MiteDesk.Core.Infrastructure.Tests/MiteDesk.Core.Infrastructure.Tests.csproj @@ -0,0 +1,110 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {BEE63E05-FFC7-4F80-A938-2B4272683E0F} + Library + Properties + MiteDesk.Core.Infrastructure.Tests + MiteDesk.Core.Infrastructure.Tests + v4.0 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + 3.5 + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AllRules.ruleset + + + + + False + ..\..\Library\Shared\RhinoMocks\Rhino.Mocks.dll + + + + 3.5 + + + + + + + + + {733C8DE9-36B5-4C9F-9701-FDD19B64B966} + MiteDesk.Core.Infrastructure + + + {AE69F9FC-F9D4-41D8-9B00-1261F492FB89} + MiteDesk.Core.Model + + + + + False + Microsoft .NET Framework 4 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + false + + + False + Windows Installer 3.1 + true + + + + + \ No newline at end of file diff --git a/Tests/MiteDesk.Core.Infrastructure.Tests/Properties/AssemblyInfo.cs b/Tests/MiteDesk.Core.Infrastructure.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..9c61405 --- /dev/null +++ b/Tests/MiteDesk.Core.Infrastructure.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +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 mit einer Assembly verknüpft sind. +[assembly: AssemblyTitle("MiteDesk.Core.Infrastructure.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("MiteDesk.Core.Infrastructure.Tests")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("85c923e4-680f-4423-9f81-558020b9687a")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Revisions- und Buildnummern +// übernehmen, indem Sie "*" eingeben: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Tests/MiteDesk.Core.Services.Tests/ActivityServiceTests.cs b/Tests/MiteDesk.Core.Services.Tests/ActivityServiceTests.cs new file mode 100644 index 0000000..211cf1c --- /dev/null +++ b/Tests/MiteDesk.Core.Services.Tests/ActivityServiceTests.cs @@ -0,0 +1,230 @@ +using System; +using System.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Rhino.Mocks; +using SixtyNineDegrees.MiteDesk.Core.Data; +using SixtyNineDegrees.MiteDesk.Core.Model; + +namespace SixtyNineDegrees.MiteDesk.Core.Services.Tests +{ + [TestClass] + public class ActivityServiceTests + { + + #region Setup + + private ActivityService ActivityService; + private MockRepository MockRepository; + private IActivityRepository ActivityRepository; + + [TestInitialize] + public void Setup() + { + + MockRepository = new MockRepository(); + ActivityRepository = MockRepository.DynamicMock(); + ActivityService = new ActivityService(ActivityRepository); + + using (MockRepository.Record()) + { + Expect.Call(ActivityRepository.GetAllActiveActivities()).Return(new List(new[] { new Activity(), new Activity() })).Repeat.Any(); + Expect.Call(ActivityRepository.GetAllArchivedActivities()).Return(new List(new[] { new Activity() })).Repeat.Any(); + Expect.Call(ActivityRepository.GetActivityByID(1234)).Return(new Activity() { ID = 1234 }).Repeat.Any(); + } + + } + + #endregion + + [TestMethod] + public void GetAllActiveActivities_liefert_eine_Liste_aller_aktiven_Leistungen() + { + var result = ActivityService.GetAllActiveActivities(); + Assert.AreEqual(2, result.Count); + } + + [TestMethod] + public void GetAllArchivedActivities_liefert_alle_archivierten_Leistungen_aus_dem_ActivityRepository() + { + var result = ActivityService.GetAllArchivedActivities(); + Assert.AreEqual(1, result.Count); + } + + [TestMethod] + public void DeleteActivity_ruft_das_Repository_zum_Löschen_der_gewählten_Leistung_auf() + { + ActivityService.DeleteActivity(123456); + ActivityRepository.AssertWasCalled(r => r.DeleteActivity(123456)); + } + + [TestMethod] + public void GetActivityByID_liefert_eine_bestimmte_Leistung_aus_dem_Repository_anhand_ihrer_ID() + { + var result = ActivityService.GetActivityByID(1234); + Assert.IsNotNull(result); + Assert.AreEqual(1234, result.ID); + } + + [TestMethod] + public void CreateActivity_liefert_einen_Fehler_wenn_der_Name_einer_Leistung_nicht_angegeben_wurde() + { + var activity = new Activity { Name = "" }; + var result = ActivityService.CreateActivity(activity, ""); + Assert.IsTrue(result.ContainsKey("Name")); + } + + [TestMethod] + public void CreateActivity_liefert_keinen_Fehler_wenn_alles_okay() + { + var activity = new Activity { Name = "Testleistung" }; + var result = ActivityService.CreateActivity(activity, ""); + Assert.AreEqual(0, result.Count); + } + + [TestMethod] + public void CreateActivity_speichert_die_Leistung_im_Repository_wenn_alles_okay() + { + var activity = new Activity { Name = "Testleistung" }; + ActivityService.CreateActivity(activity, ""); + ActivityRepository.AssertWasCalled(r => r.CreateActivity(activity)); + } + + [TestMethod] + public void CreateActivity_übergibt_als_Stundensatz_bei_Eingabe_von_25_komma_50_2550_ans_Repository() + { + var activity = new Activity { Name = "OK" }; + ActivityService.CreateActivity(activity, "25,50"); + ActivityRepository.AssertWasCalled(r => r.CreateActivity(new Activity { Name = "OK", HourlyRate = 2550 })); + } + + [TestMethod] + public void CreateActivity_übergibt_als_Stundensatz_bei_Eingabe_von_25__komma_r_2500_ans_Repository() + { + var activity = new Activity { Name = "OK" }; + ActivityService.CreateActivity(activity, "25,r"); + ActivityRepository.AssertWasCalled(r => r.CreateActivity(new Activity { Name = "OK", HourlyRate = 2500 })); + } + + [TestMethod] + public void CreateActivity_übergibt_als_Stundensatz_bei_Eingabe_von_25__komma_799_2580_ans_Repository() + { + var activity = new Activity { Name = "OK" }; + ActivityService.CreateActivity(activity, "25,799"); + ActivityRepository.AssertWasCalled(r => r.CreateActivity(new Activity { Name = "OK", HourlyRate = 2580 })); + } + + [TestMethod] + public void CreateActivity_übergibt_als_Stundensatz_bei_Eingabe_von_r_komma_50_50_ans_Repository() + { + var activity = new Activity { Name = "OK" }; + ActivityService.CreateActivity(activity, "r,50"); + ActivityRepository.AssertWasCalled(r => r.CreateActivity(new Activity { Name = "OK", HourlyRate = 50 })); + } + + [TestMethod] + public void CreateActivity_übergibt_als_Stundensatz_bei_Eingabe_von_einem_Leerstring_0_ans_Repository() + { + var activity = new Activity { Name = "OK" }; + ActivityService.CreateActivity(activity, ""); + ActivityRepository.AssertWasCalled(r => r.CreateActivity(new Activity { Name = "OK", HourlyRate = 0 })); + } + + [TestMethod] + public void CreateActivity_übergibt_als_Stundensatz_bei_Eingabe_von_100_10000_ans_Repository() + { + var activity = new Activity { Name = "OK" }; + ActivityService.CreateActivity(activity, "100"); + ActivityRepository.AssertWasCalled(r => r.CreateActivity(new Activity { Name = "OK", HourlyRate = 10000 })); + } + + [TestMethod] + public void UpdateActivity_liefert_einen_Fehler_wenn_der_Name_der_Leistung_nicht_angegeben_wurde() + { + var activity = new Activity { ID = 3, Name = "" }; + var result = ActivityService.UpdateActivity(activity, ""); + Assert.IsTrue(result.ContainsKey("Name")); + } + + [TestMethod] + public void UpdateActivity_wirft_eine_ArgumentException_wenn_die_ID_der_Leistung_nicht_angegeben_wurde() + { + Exception exception = null; + var activity = new Activity { ID = 0 }; + try + { + ActivityService.UpdateActivity(activity, ""); + } + catch (Exception e) + { + exception = e; + } + Assert.IsNotNull(exception); + Assert.IsInstanceOfType(exception, typeof(ArgumentException)); + } + + [TestMethod] + public void UpdateActivity_liefert_keinen_Fehler_wenn_alles_okay() + { + var activity = new Activity { ID = 3, Name = "Testleistung" }; + var result = ActivityService.UpdateActivity(activity, ""); + Assert.AreEqual(0, result.Count); + } + + [TestMethod] + public void UpdateActivity_speichert_die_Leistung_im_Repository_wenn_alles_okay() + { + var activity = new Activity { ID = 3, Name = "Testleistung" }; + ActivityService.UpdateActivity(activity, ""); + ActivityRepository.AssertWasCalled(r => r.UpdateActivity(activity)); + } + + [TestMethod] + public void UpdateActivity_übergibt_als_Stundensatz_bei_Eingabe_von_25_komma_50_2550_ans_Repository() + { + var activity = new Activity { ID = 3, Name = "OK" }; + ActivityService.UpdateActivity(activity, "25,50"); + ActivityRepository.AssertWasCalled(r => r.UpdateActivity(new Activity { ID = 3, Name = "OK", HourlyRate = 2550 })); + } + + [TestMethod] + public void UpdateActivity_übergibt_als_Stundensatz_bei_Eingabe_von_25__komma_r_2500_ans_Repository() + { + var activity = new Activity { ID = 3, Name = "OK" }; + ActivityService.UpdateActivity(activity, "25,r"); + ActivityRepository.AssertWasCalled(r => r.UpdateActivity(new Activity { ID = 3, Name = "OK", HourlyRate = 2500 })); + } + + [TestMethod] + public void UpdateActivity_übergibt_als_Stundensatz_bei_Eingabe_von_r_komma_50_50_ans_Repository() + { + var activity = new Activity { ID = 3, Name = "OK" }; + ActivityService.UpdateActivity(activity, "r,50"); + ActivityRepository.AssertWasCalled(r => r.UpdateActivity(new Activity { ID = 3, Name = "OK", HourlyRate = 50 })); + } + + [TestMethod] + public void UpdateActivity_übergibt_als_Stundensatz_bei_Eingabe_von_einem_Leerstring_0_ans_Repository() + { + var activity = new Activity { ID = 3, Name = "OK" }; + ActivityService.UpdateActivity(activity, ""); + ActivityRepository.AssertWasCalled(r => r.UpdateActivity(new Activity { ID = 3, Name = "OK", HourlyRate = 0 })); + } + + [TestMethod] + public void UpdateActivity_übergibt_als_Stundensatz_bei_Eingabe_von_100_10000_ans_Repository() + { + var activity = new Activity { ID = 3, Name = "OK" }; + ActivityService.UpdateActivity(activity, "100"); + ActivityRepository.AssertWasCalled(r => r.UpdateActivity(new Activity { ID = 3, Name = "OK", HourlyRate = 10000 })); + } + + [TestMethod] + public void UpdateActivity_übergibt_als_Stundensatz_bei_Eingabe_von_25__komma_799_2580_ans_Repository() + { + var activity = new Activity { ID = 3, Name = "OK" }; + ActivityService.UpdateActivity(activity, "25,799"); + ActivityRepository.AssertWasCalled(r => r.UpdateActivity(new Activity { ID = 3, Name = "OK", HourlyRate = 2580 })); + } + + } +} diff --git a/Tests/MiteDesk.Core.Services.Tests/AuthenticationServiceTests.cs b/Tests/MiteDesk.Core.Services.Tests/AuthenticationServiceTests.cs new file mode 100644 index 0000000..b99c169 --- /dev/null +++ b/Tests/MiteDesk.Core.Services.Tests/AuthenticationServiceTests.cs @@ -0,0 +1,38 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Rhino.Mocks; +using SixtyNineDegrees.MiteDesk.Core.Data; + +namespace SixtyNineDegrees.MiteDesk.Core.Services.Tests +{ + [TestClass] + public class AuthenticationServiceTests + { + + #region Setup + + private IAuthenticationService AuthenticationService; + private MockRepository MockRepository; + private IUserRepository UserRepository; + + [TestInitialize] + public void Setup() + { + MockRepository = new MockRepository(); + UserRepository = MockRepository.DynamicMock(); + using (MockRepository.Record()) + { + } + AuthenticationService = new AuthenticationService(UserRepository); + } + + #endregion + + [TestMethod] + public void GetAuthenticatedUser_holt_den_aktuellen_Benutzer_aus_dem_Repository() + { + AuthenticationService.GetAuthenticatedUser(); + UserRepository.AssertWasCalled(r => r.GetAuthenticatedUser()); + } + + } +} diff --git a/Tests/MiteDesk.Core.Services.Tests/CustomerServiceTests.cs b/Tests/MiteDesk.Core.Services.Tests/CustomerServiceTests.cs new file mode 100644 index 0000000..8f35bc3 --- /dev/null +++ b/Tests/MiteDesk.Core.Services.Tests/CustomerServiceTests.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Rhino.Mocks; +using SixtyNineDegrees.MiteDesk.Core.Data; +using SixtyNineDegrees.MiteDesk.Core.Model; + +namespace SixtyNineDegrees.MiteDesk.Core.Services.Tests +{ + [TestClass] + public class CustomerServiceTests + { + + #region Setup + + private CustomerService CustomerService; + private MockRepository MockRepository; + private ICustomerRepository CustomerRepository; + + [TestInitialize] + public void Setup() + { + + MockRepository = new MockRepository(); + CustomerRepository = MockRepository.DynamicMock(); + CustomerService = new CustomerService(CustomerRepository); + + using (MockRepository.Record()) + { + Expect.Call(CustomerRepository.GetAllActiveCustomers()).Return(new List(new[] { new Customer(), new Customer { ID = 2 }, new Customer() })).Repeat.Any(); + Expect.Call(CustomerRepository.GetAllArchivedCustomers()).Return(new List(new[] { new Customer() })).Repeat.Any(); + Expect.Call(CustomerRepository.GetCustomerByID(1234)).Return(new Customer { ID = 1234 }).Repeat.Any(); + } + + } + + #endregion + + [TestMethod] + public void GetAllActiveCustomers_liefert_alle_aktiven_Kunden_aus_dem_CustomerRepository() + { + var result = CustomerService.GetAllActiveCustomers(); + Assert.AreEqual(3, result.Count); + Assert.AreEqual(2, result[1].ID); + } + + [TestMethod] + public void GetAllArchivedCustomers_liefert_alle_archivierten_Kunden_aus_dem_CustomerRepository() + { + var result = CustomerService.GetAllArchivedCustomers(); + Assert.AreEqual(1, result.Count); + } + + [TestMethod] + public void DeleteCustomer_ruft_das_Repository_zum_Löschen_des_gewählten_Kunden_auf() + { + CustomerService.DeleteCustomer(123456); + CustomerRepository.AssertWasCalled(r => r.DeleteCustomer(123456)); + } + + [TestMethod] + public void GetCustomerByID_liefert_einen_bestimmten_Kunden_aus_dem_Repository_anhand_seiner_ID() + { + var result = CustomerService.GetCustomerByID(1234); + Assert.IsNotNull(result); + Assert.AreEqual(1234, result.ID); + } + + [TestMethod] + public void CreateCustomer_liefert_einen_Fehler_wenn_der_name_des_Kunden_nicht_angegeben_wurde() + { + var customer = new Customer { Name = "" }; + var result = CustomerService.CreateCustomer(customer); + Assert.IsTrue(result.ContainsKey("Name")); + } + + [TestMethod] + public void CreateCustomer_liefert_keinen_Fehler_wenn_alles_okay() + { + var customer = new Customer { Name = "Testkunde" }; + var result = CustomerService.CreateCustomer(customer); + Assert.AreEqual(0, result.Count); + } + + [TestMethod] + public void CreateCustomer_speichert_den_Kunden_im_Repository_wenn_alles_okay() + { + var customer = new Customer { Name = "Testkunde" }; + CustomerService.CreateCustomer(customer); + CustomerRepository.AssertWasCalled(r => r.CreateCustomer(customer)); + } + + [TestMethod] + public void UpdateCustomer_liefert_einen_Fehler_wenn_der_Name_des_Kunden_nicht_angegeben_wurde() + { + var customer = new Customer { ID = 3, Name = "" }; + var result = CustomerService.UpdateCustomer(customer); + Assert.IsTrue(result.ContainsKey("Name")); + } + + [TestMethod] + public void UpdateCustomer_wirft_eine_ArgumentException_wenn_die_ID_des_Kunden_nicht_angegeben_wurde() + { + Exception exception = null; + var customer = new Customer { ID = 0 }; + try + { + CustomerService.UpdateCustomer(customer); + } + catch(Exception e) + { + exception = e; + } + Assert.IsNotNull(exception); + Assert.IsInstanceOfType(exception, typeof(ArgumentException)); + } + + [TestMethod] + public void UpdateCustomer_liefert_keinen_Fehler_wenn_alles_okay() + { + var customer = new Customer { ID = 3, Name = "Testkunde" }; + var result = CustomerService.UpdateCustomer(customer); + Assert.AreEqual(0, result.Count); + } + + [TestMethod] + public void UpdateCustomer_speichert_den_Kunden_im_Repository_wenn_alles_okay() + { + var customer = new Customer { ID = 3, Name = "Testkunde" }; + CustomerService.UpdateCustomer(customer); + CustomerRepository.AssertWasCalled(r => r.UpdateCustomer(customer)); + } + + } +} diff --git a/Tests/MiteDesk.Core.Services.Tests/MiteDesk.Core.Services.Tests.csproj b/Tests/MiteDesk.Core.Services.Tests/MiteDesk.Core.Services.Tests.csproj new file mode 100644 index 0000000..bee0982 --- /dev/null +++ b/Tests/MiteDesk.Core.Services.Tests/MiteDesk.Core.Services.Tests.csproj @@ -0,0 +1,131 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {C9BD6574-4771-4068-B321-40943DCD3C88} + Library + Properties + SixtyNineDegrees.MiteDesk.Core.Services.Tests + MiteDesk.Core.Services.Tests + v4.0 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + 3.5 + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AllRules.ruleset + + + + + False + ..\..\Library\Shared\RhinoMocks\Rhino.Mocks.dll + + + + + 3.5 + + + + 3.5 + + + + 3.5 + + + + + + + + + + + + + {AD8740D8-14A7-4A31-8F96-7DF850D3D957} + MiteDesk.Core.Data + + + {733C8DE9-36B5-4C9F-9701-FDD19B64B966} + MiteDesk.Core.Infrastructure + + + {AE69F9FC-F9D4-41D8-9B00-1261F492FB89} + MiteDesk.Core.Model + + + {B7B67B5C-47F1-4E3D-8759-EA8E31AC951E} + MiteDesk.Core.Services + + + + + False + Microsoft .NET Framework 4 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + false + + + False + Windows Installer 3.1 + true + + + + + \ No newline at end of file diff --git a/Tests/MiteDesk.Core.Services.Tests/ProjectServiceTests.cs b/Tests/MiteDesk.Core.Services.Tests/ProjectServiceTests.cs new file mode 100644 index 0000000..edb4039 --- /dev/null +++ b/Tests/MiteDesk.Core.Services.Tests/ProjectServiceTests.cs @@ -0,0 +1,271 @@ +using System; +using System.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Rhino.Mocks; +using SixtyNineDegrees.MiteDesk.Core.Data; +using SixtyNineDegrees.MiteDesk.Core.Model; + +namespace SixtyNineDegrees.MiteDesk.Core.Services.Tests +{ + + [TestClass] + public class ProjectServiceTests + { + + #region Setup + + private ProjectService ProjectService; + private MockRepository MockRepository; + private IProjectRepository ProjectRepository; + + [TestInitialize] + public void Setup() + { + + MockRepository = new MockRepository(); + ProjectRepository = MockRepository.DynamicMock(); + ProjectService = new ProjectService(ProjectRepository); + + using (MockRepository.Record()) + { + Expect.Call(ProjectRepository.GetAllActiveProjects()).Return(new List(new[] { new Project(), new Project(), new Project(), new Project() })).Repeat.Any(); + Expect.Call(ProjectRepository.GetAllArchivedProjects()).Return(new List(new[] { new Project() })).Repeat.Any(); + Expect.Call(ProjectRepository.GetProjectsByCustomer(34)).Return(new List(new[] { new Project(), new Project() })).Repeat.Any(); + Expect.Call(ProjectService.GetProjectByID(1234)).Return(new Project {ID = 1234}).Repeat.Any(); + } + + } + + #endregion + + [TestMethod] + public void GetAllActiveProjects_liefert_eine_Liste_aller_aktiven_Projekte() + { + var result = ProjectService.GetAllActiveProjects(); + Assert.AreEqual(4, result.Count); + } + + [TestMethod] + public void GetProjectsByCustomer_liefert_alle_Projekte_eines_einzelnen_Kunden() + { + var result = ProjectService.GetProjectsByCustomer(34); + Assert.AreEqual(2, result.Count); + } + + [TestMethod] + public void GetAllArchivedProjects_liefert_alle_archivierten_Projekte_aus_dem_ProjectRepository() + { + var result = ProjectService.GetAllArchivedProjects(); + Assert.AreEqual(1, result.Count); + } + + [TestMethod] + public void DeleteProject_ruft_das_Repository_zum_Löschen_des_gewählten_Projektes_auf() + { + ProjectService.DeleteProject(123456); + ProjectRepository.AssertWasCalled(r => r.DeleteProject(123456)); + } + + [TestMethod] + public void GetProjectByID_liefert_ein_bestimmtes_Projekt_aus_dem_Repository_anhand_seiner_ID() + { + var result = ProjectService.GetProjectByID(1234); + Assert.IsNotNull(result); + Assert.AreEqual(1234, result.ID); + } + + [TestMethod] + public void CreateProject_liefert_einen_Fehler_wenn_der_name_des_Projektes_nicht_angegeben_wurde() + { + var project = new Project { Name = "" }; + var result = ProjectService.CreateProject(project, ""); + Assert.IsTrue(result.ContainsKey("Name")); + } + + [TestMethod] + public void CreateProject_liefert_keinen_Fehler_wenn_alles_okay() + { + var project = new Project { Name = "Testprojekt" }; + var result = ProjectService.CreateProject(project, ""); + Assert.AreEqual(0, result.Count); + } + + [TestMethod] + public void CreateProject_speichert_das_Projekt_im_Repository_wenn_alles_okay() + { + var project = new Project { Name = "Testprojekt" }; + ProjectService.CreateProject(project, ""); + ProjectRepository.AssertWasCalled(r => r.CreateProject(project)); + } + + [TestMethod] + public void CreateProject_übergibt_als_Budget_EUR_bei_Eingabe_von_25_komma_50_2550_ans_Repository() + { + var project = new Project { Name = "OK", BudgetType = "euro" }; + ProjectService.CreateProject(project, "25,50"); + ProjectRepository.AssertWasCalled(r => r.CreateProject(new Project { Name = "OK", BudgetType = "euro", Budget = 2550 })); + } + + [TestMethod] + public void CreateProject_übergibt_als_Budget_EUR_bei_Eingabe_von_25__komma_r_2500_ans_Repository() + { + var project = new Project { Name = "OK", BudgetType = "euro" }; + ProjectService.CreateProject(project, "25,r"); + ProjectRepository.AssertWasCalled(r => r.CreateProject(new Project { Name = "OK", BudgetType = "euro", Budget = 2500 })); + } + + [TestMethod] + public void CreateProject_übergibt_als_Budget_EUR_bei_Eingabe_von_25__komma_799_2580_ans_Repository() + { + var project = new Project { Name = "OK", BudgetType = "euro" }; + ProjectService.CreateProject(project, "25,799"); + ProjectRepository.AssertWasCalled(r => r.CreateProject(new Project { Name = "OK", BudgetType = "euro", Budget = 2580 })); + } + + [TestMethod] + public void CreateProject_übergibt_als_Budget_EUR_bei_Eingabe_von_r_komma_50_50_ans_Repository() + { + var project = new Project { Name = "OK", BudgetType = "euro" }; + ProjectService.CreateProject(project, "r,50"); + ProjectRepository.AssertWasCalled(r => r.CreateProject(new Project { Name = "OK", BudgetType = "euro", Budget = 50 })); + } + + [TestMethod] + public void CreateProject_übergibt_als_Budget_EUR_bei_Eingabe_von_einem_Leerstring_0_ans_Repository() + { + var project = new Project { Name = "OK", BudgetType = "euro" }; + ProjectService.CreateProject(project, ""); + ProjectRepository.AssertWasCalled(r => r.CreateProject(new Project { Name = "OK", BudgetType = "euro", Budget = 0 })); + } + + [TestMethod] + public void CreateProject_übergibt_als_Budget_EUR_bei_Eingabe_von_100_10000_ans_Repository() + { + var project = new Project { Name = "OK", BudgetType = "euro" }; + ProjectService.CreateProject(project, "100"); + ProjectRepository.AssertWasCalled(r => r.CreateProject(new Project { Name = "OK", BudgetType = "euro", Budget = 10000 })); + } + + [TestMethod] + public void CreateProject_übergibt_als_Budget_Minutes_bei_Eingabe_von_3_180_ans_Repository() + { + var project = new Project { Name = "OK", BudgetType = "minutes" }; + ProjectService.CreateProject(project, "3"); + ProjectRepository.AssertWasCalled(r => r.CreateProject(new Project { Name = "OK", BudgetType = "minutes", Budget = 180 })); + } + + [TestMethod] + public void CreateProject_übergibt_als_Budget_Minutes_bei_Eingabe_von_3_20_200_ans_Repository() + { + var project = new Project { Name = "OK", BudgetType = "minutes" }; + ProjectService.CreateProject(project, "3:20"); + ProjectRepository.AssertWasCalled(r => r.CreateProject(new Project { Name = "OK", BudgetType = "minutes", Budget = 200 })); + } + + [TestMethod] + public void UpdateProject_liefert_einen_Fehler_wenn_der_Name_des_Projektes_nicht_angegeben_wurde() + { + var project = new Project { ID = 3, Name = "" }; + var result = ProjectService.UpdateProject(project, ""); + Assert.IsTrue(result.ContainsKey("Name")); + } + + [TestMethod] + public void UpdateProject_wirft_eine_ArgumentException_wenn_die_ID_des_Projektes_nicht_angegeben_wurde() + { + Exception exception = null; + var project = new Project { ID = 0 }; + try + { + ProjectService.UpdateProject(project, ""); + } + catch (Exception e) + { + exception = e; + } + Assert.IsNotNull(exception); + Assert.IsInstanceOfType(exception, typeof(ArgumentException)); + } + + [TestMethod] + public void UpdateProject_liefert_keinen_Fehler_wenn_alles_okay() + { + var project = new Project { ID = 3, Name = "Testprojekt" }; + var result = ProjectService.UpdateProject(project, ""); + Assert.AreEqual(0, result.Count); + } + + [TestMethod] + public void UpdateProject_speichert_das_Projekt_im_Repository_wenn_alles_okay() + { + var project = new Project { ID = 3, Name = "Testprojekt" }; + ProjectService.UpdateProject(project, ""); + ProjectRepository.AssertWasCalled(r => r.UpdateProject(project)); + } + + [TestMethod] + public void UpdateProject_übergibt_als_Budget_EUR_bei_Eingabe_von_25_komma_50_2550_ans_Repository() + { + var project = new Project { ID = 3, Name = "OK", BudgetType = "euro" }; + ProjectService.UpdateProject(project, "25,50"); + ProjectRepository.AssertWasCalled(r => r.UpdateProject(new Project { ID = 3, Name = "OK", BudgetType = "euro", Budget = 2550 })); + } + + [TestMethod] + public void UpdateProject_übergibt_als_Budget_EUR_bei_Eingabe_von_25__komma_r_2500_ans_Repository() + { + var project = new Project { ID = 3, Name = "OK", BudgetType = "euro" }; + ProjectService.UpdateProject(project, "25,r"); + ProjectRepository.AssertWasCalled(r => r.UpdateProject(new Project { ID = 3, Name = "OK", BudgetType = "euro", Budget = 2500 })); + } + + [TestMethod] + public void UpdateProject_übergibt_als_Budget_EUR_bei_Eingabe_von_25__komma_799_2580_ans_Repository() + { + var project = new Project { ID = 3, Name = "OK", BudgetType = "euro" }; + ProjectService.UpdateProject(project, "25,799"); + ProjectRepository.AssertWasCalled(r => r.UpdateProject(new Project { ID = 3, Name = "OK", BudgetType = "euro", Budget = 2580 })); + } + + [TestMethod] + public void UpdateProject_übergibt_als_Budget_EUR_bei_Eingabe_von_r_komma_50_50_ans_Repository() + { + var project = new Project { ID = 3, Name = "OK", BudgetType = "euro" }; + ProjectService.UpdateProject(project, "r,50"); + ProjectRepository.AssertWasCalled(r => r.UpdateProject(new Project { ID = 3, Name = "OK", BudgetType = "euro", Budget = 50 })); + } + + [TestMethod] + public void UpdateProject_übergibt_als_Budget_EUR_bei_Eingabe_von_einem_Leerstring_0_ans_Repository() + { + var project = new Project { ID = 3, Name = "OK", BudgetType = "euro" }; + ProjectService.UpdateProject(project, ""); + ProjectRepository.AssertWasCalled(r => r.UpdateProject(new Project { ID = 3, Name = "OK", BudgetType = "euro", Budget = 0 })); + } + + [TestMethod] + public void UpdateProject_übergibt_als_Budget_EUR_bei_Eingabe_von_100_10000_ans_Repository() + { + var project = new Project { ID = 3, Name = "OK", BudgetType = "euro" }; + ProjectService.UpdateProject(project, "100"); + ProjectRepository.AssertWasCalled(r => r.UpdateProject(new Project { ID = 3, Name = "OK", BudgetType = "euro", Budget = 10000 })); + } + + [TestMethod] + public void UpdateProject_übergibt_als_Budget_Minutes_bei_Eingabe_von_3_180_ans_Repository() + { + var project = new Project { ID = 3, Name = "OK", BudgetType = "minutes" }; + ProjectService.UpdateProject(project, "3"); + ProjectRepository.AssertWasCalled(r => r.UpdateProject(new Project { ID = 3, Name = "OK", BudgetType = "minutes", Budget = 180 })); + } + + [TestMethod] + public void UpdateProject_übergibt_als_Budget_Minutes_bei_Eingabe_von_3_20_200_ans_Repository() + { + var project = new Project { ID = 3, Name = "OK", BudgetType = "minutes" }; + ProjectService.UpdateProject(project, "3:20"); + ProjectRepository.AssertWasCalled(r => r.UpdateProject(new Project { ID = 3, Name = "OK", BudgetType = "minutes", Budget = 200 })); + } + + } +} diff --git a/Tests/MiteDesk.Core.Services.Tests/Properties/AssemblyInfo.cs b/Tests/MiteDesk.Core.Services.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..0789cb0 --- /dev/null +++ b/Tests/MiteDesk.Core.Services.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,34 @@ +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 mit einer Assembly verknüpft sind. +[assembly: AssemblyTitle("MiteDesk.Core.Services.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("MiteDesk.Core.Services.Tests")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("61e43971-6af0-4a7e-b4c8-4c952958b0a1")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Revisions- und Buildnummern +// übernehmen, indem Sie "*" eingeben: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Tests/MiteDesk.Core.Services.Tests/TimeEntryServiceTests.cs b/Tests/MiteDesk.Core.Services.Tests/TimeEntryServiceTests.cs new file mode 100644 index 0000000..2aad0f9 --- /dev/null +++ b/Tests/MiteDesk.Core.Services.Tests/TimeEntryServiceTests.cs @@ -0,0 +1,295 @@ +using System; +using System.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Rhino.Mocks; +using SixtyNineDegrees.MiteDesk.Core.Data; +using SixtyNineDegrees.MiteDesk.Core.Model; + +namespace SixtyNineDegrees.MiteDesk.Core.Services.Tests +{ + [TestClass] + public class TimeEntryServiceTests + { + + #region Setup + + private ITimeEntryService TimeEntryService; + private MockRepository MockRepository; + private ITimeEntryRepository TimeEntryRepository; + + [TestInitialize] + public void Setup() + { + + MockRepository = new MockRepository(); + + TimeEntryRepository = MockRepository.DynamicMock(); + IProjectRepository projectRepository = MockRepository.DynamicMock(); + IActivityRepository activityRepository = MockRepository.DynamicMock(); + + TimeEntryService = new TimeEntryService(TimeEntryRepository, new ProjectService(projectRepository), new ActivityService(activityRepository)); + + using (MockRepository.Record()) + { + Expect.Call(projectRepository.GetAllActiveProjects()).Return(new List(new[] { new Project(), new Project { ID = 2 } })).Repeat.Any(); + Expect.Call(activityRepository.GetAllActiveActivities()).Return(new List(new[] { new Activity(), new Activity { ID = 2 } })).Repeat.Any(); + Expect.Call(TimeEntryRepository.GetTimeEntryByID(15)).Return(new TimeEntry { ID = 15 }).Repeat.Any(); + Expect.Call(TimeEntryRepository.GetTimeEntryCurrentlyTrackedByStopwatch()).Return(new TimeEntry { ID = 34 }).Repeat.Any(); + Expect.Call(TimeEntryRepository.GetTimeEntriesByDate(new DateTime(2009, 7, 14))).Return(new List(new [] { new TimeEntry(), new TimeEntry(), new TimeEntry() })).Repeat.Any(); + Expect.Call(TimeEntryRepository.GetTimeEntriesByActivityID(35)).Return(new List(new[] { new TimeEntry(), new TimeEntry(), })).Repeat.Any(); + Expect.Call(TimeEntryRepository.GetTimeEntriesByProjectID(2)).Return(new List(new[] { new TimeEntry(), new TimeEntry(), })).Repeat.Any(); + Expect.Call(TimeEntryService.GetTimeEntryDatesByRange(new DateTime(2009, 8, 1), new DateTime(2009, 8, 15), 18352)).Return(new List(new[] { new DateTime(2009, 8, 1), new DateTime(2009, 8, 12) })); + } + + } + + #endregion + + [TestMethod] + public void CreateTimeEntry_liefert_einen_Fehler_wenn_die_ProjektID_nicht_in_der_Liste_der_verfügbaren_Projekte_vorhanden_ist() + { + var entry = new TimeEntry { ProjectID = -1 }; + var result = TimeEntryService.CreateTimeEntry(ref entry, ""); + Assert.IsTrue(result.ContainsKey("ProjectID")); + } + + [TestMethod] + public void CreateTimeEntry_liefert_einen_Fehler_wenn_die_ActivityID_nicht_in_der_Liste_der_verfügbaren_Leistungen_vorhanden_ist() + { + var entry = new TimeEntry { ActivityID = -1 }; + var result = TimeEntryService.CreateTimeEntry(ref entry, ""); + Assert.IsTrue(result.ContainsKey("ActivityID")); + } + + [TestMethod] + public void CreateTimeEntry_liefert_einen_Fehler_wenn_das_Datum_nicht_angegeben_ist() + { + var entry = new TimeEntry(); + var result = TimeEntryService.CreateTimeEntry(ref entry, ""); + Assert.IsTrue(result.ContainsKey("Date")); + } + + [TestMethod] + public void CreateTimeEntry_liefert_einen_Fehler_wenn_die_Zeit_nicht_korrekt_formatiert_ist() + { + var entry = new TimeEntry(); + var result = TimeEntryService.CreateTimeEntry(ref entry, "ccxc"); + Assert.IsTrue(result.ContainsKey("Time")); + } + + [TestMethod] + public void CreateTimeEntry_übergibt_den_neuen_TimeEntry_ans_Repository_wenn_Validierung_erfolgreich() + { + var entry = new TimeEntry { ActivityID = 2, Date = DateTime.Now, Minutes = 30, ProjectID = 2 }; + TimeEntryService.CreateTimeEntry(ref entry, "1:30"); + TimeEntryRepository.AssertWasCalled(r => r.CreateTimeEntry(ref entry)); + } + + [TestMethod] + public void GetTimeEntriesByDate_liefert_alle_Einträge_zu_einem_Datum() + { + var result = TimeEntryService.GetTimeEntriesByDate(new DateTime(2009, 7, 14)); + Assert.AreEqual(3, result.Count); + } + + [TestMethod] + public void GetTimeEntriesByActivityID_liefert_alle_Einträge_zu_einer_leistung() + { + var result = TimeEntryService.GetTimeEntriesByActivityID(35); + Assert.AreEqual(2, result.Count); + } + + [TestMethod] + public void GetTimeEntriesByProjectID_liefert_alle_Einträge_zu_einem_Projekt() + { + var result = TimeEntryService.GetTimeEntriesByProjectID(2); + Assert.AreEqual(2, result.Count); + } + + [TestMethod] + public void GetTimeEntryByID_liefert_einen_bestimmten_Eintrag_anhand_seiner_ID() + { + var result = TimeEntryService.GetTimeEntryByID(15); + Assert.IsNotNull(result); + Assert.AreEqual(15, result.ID); + } + + [TestMethod] + public void DeleteTimeEntry_ruft_das_Repository_zum_Löschen_des_gewählten_Eintrags_auf() + { + TimeEntryService.DeleteTimeEntry(34); + TimeEntryRepository.AssertWasCalled(r => r.DeleteTimeEntry(34)); + } + + [TestMethod] + public void UpdateTimeEntry_liefert_einen_Fehler_wenn_die_ProjektID_nicht_in_der_Liste_der_verfügbaren_Projekte_vorhanden_ist() + { + var entry = new TimeEntry { ProjectID = -1 }; + var result = TimeEntryService.UpdateTimeEntry(entry, ""); + Assert.IsTrue(result.ContainsKey("ProjectID")); + } + + [TestMethod] + public void UpdateTimeEntry_liefert_einen_Fehler_wenn_die_ActivityID_nicht_in_der_Liste_der_verfügbaren_Leistungen_vorhanden_ist() + { + var entry = new TimeEntry { ActivityID = -1 }; + var result = TimeEntryService.UpdateTimeEntry(entry, ""); + Assert.IsTrue(result.ContainsKey("ActivityID")); + } + + [TestMethod] + public void UpdateTimeEntry_liefert_einen_Fehler_wenn_das_Datum_nicht_angegeben_ist() + { + var entry = new TimeEntry(); + var result = TimeEntryService.UpdateTimeEntry(entry, ""); + Assert.IsTrue(result.ContainsKey("Date")); + } + + [TestMethod] + public void UpdateTimeEntry_liefert_einen_Fehler_wenn_die_Zeit_nicht_korrekt_formatiert_ist() + { + var entry = new TimeEntry(); + var result = TimeEntryService.UpdateTimeEntry(entry, "ccxc"); + Assert.IsTrue(result.ContainsKey("Time")); + } + + [TestMethod] + public void UpdateTimeEntry_übergibt_den_neuen_TimeEntry_ans_Repository_wenn_Validierung_erfolgreich() + { + var entry = new TimeEntry { ActivityID = 2, Date = DateTime.Now, Minutes = 30, ProjectID = 2 }; + TimeEntryService.UpdateTimeEntry(entry, "1:30"); + TimeEntryRepository.AssertWasCalled(r => r.UpdateTimeEntry(entry)); + } + + [TestMethod] + public void StartStopwatch_ruft_StartStopwatch_im_Repository_für_die_übergebene_ID_auf() + { + TimeEntryService.StartStopwatch(456); + TimeEntryRepository.AssertWasCalled(r => r.StartStopwatch(456)); + } + + [TestMethod] + public void StopStopwatch_ruft_StopStopwatch_im_Repository_für_die_übergebene_ID_auf() + { + TimeEntryService.StopStopwatch(456); + TimeEntryRepository.AssertWasCalled(r => r.StopStopwatch(456)); + } + + [TestMethod] + public void GetTimeEntryCurrentlyTrackedByStopwatch_liefert_den_Zeiteintrag_aus_dem_Repository_der_gerade_gestoppt_wird() + { + var result = TimeEntryService.GetTimeEntryCurrentlyTrackedByStopwatch(); + Assert.IsNotNull(result); + Assert.AreEqual(34, result.ID); + } + + [TestMethod] + public void GetTimeEntryDatesByRange_liefert_eine_Liste_von_Dates_in_einem_bestimmten_Datums_bereich() + { + var result = TimeEntryService.GetTimeEntryDatesByRange(new DateTime(2009, 8, 1), new DateTime(2009, 8, 15), 18352); + Assert.AreEqual(new DateTime(2009, 8, 1), result[0]); + Assert.AreEqual(new DateTime(2009, 8, 12), result[1]); + } + + [TestMethod] + public void ValidateTimeEntry_errechnet_die_Minuten_aus_korrekter_Zeiteingabe_richtig_Format1() + { + // 01:35 = 95 + var time = "01:35"; + var timeEntry = new TimeEntry(); + var result = TimeEntryService.ValidateTimeEntry(timeEntry, time); + Assert.AreEqual(95, timeEntry.Minutes); + Assert.IsFalse(result.ContainsKey("Time")); + } + + [TestMethod] + public void ValidateTimeEntry_errechnet_die_Minuten_aus_korrekter_Zeiteingabe_richtig_Format2() + { + // 01:00 = 60 + var time = "01:00"; + var timeEntry = new TimeEntry(); + var result = TimeEntryService.ValidateTimeEntry(timeEntry, time); + Assert.AreEqual(60, timeEntry.Minutes); + Assert.IsFalse(result.ContainsKey("Time")); + } + + [TestMethod] + public void ValidateTimeEntry_errechnet_die_Minuten_aus_korrekter_Zeiteingabe_richtig_Format3() + { + // 0:00 = 0 + var time = "0:00"; + var timeEntry = new TimeEntry(); + var result = TimeEntryService.ValidateTimeEntry(timeEntry, time); + Assert.AreEqual(0, timeEntry.Minutes); + Assert.IsFalse(result.ContainsKey("Time")); + } + + [TestMethod] + public void ValidateTimeEntry_errechnet_die_Minuten_aus_korrekter_Zeiteingabe_richtig_Format4() + { + // 0 = 0 + var time = "0"; + var timeEntry = new TimeEntry(); + var result = TimeEntryService.ValidateTimeEntry(timeEntry, time); + Assert.AreEqual(0, timeEntry.Minutes); + Assert.IsFalse(result.ContainsKey("Time")); + } + + [TestMethod] + public void ValidateTimeEntry_errechnet_die_Minuten_aus_korrekter_Zeiteingabe_richtig_Format5() + { + // 13 = 780 + var time = "13"; + var timeEntry = new TimeEntry(); + var result = TimeEntryService.ValidateTimeEntry(timeEntry, time); + Assert.AreEqual(780, timeEntry.Minutes); + Assert.IsFalse(result.ContainsKey("Time")); + } + + [TestMethod] + public void ValidateTimeEntry_errechnet_die_Minuten_aus_korrekter_Zeiteingabe_richtig_Format6() + { + // 4,25 = 255 + var time = "4,25"; + var timeEntry = new TimeEntry(); + var result = TimeEntryService.ValidateTimeEntry(timeEntry, time); + Assert.AreEqual(timeEntry.Minutes, 255); + Assert.IsFalse(result.ContainsKey("Time")); + } + + [TestMethod] + public void ValidateTimeEntry_errechnet_die_Minuten_aus_korrekter_Zeiteingabe_richtig_Format7() + { + // 5.5 = 330 + var time = "5.5"; + var timeEntry = new TimeEntry(); + var result = TimeEntryService.ValidateTimeEntry(timeEntry, time); + Assert.AreEqual(330, timeEntry.Minutes); + Assert.IsFalse(result.ContainsKey("Time")); + } + + [TestMethod] + public void ValidateTimeEntry_errechnet_die_Minuten_aus_korrekter_Zeiteingabe_richtig_Format8() + { + // 1,30 = 78 + var time = "1,30"; + var timeEntry = new TimeEntry(); + var result = TimeEntryService.ValidateTimeEntry(timeEntry, time); + Assert.AreEqual(78, timeEntry.Minutes); + Assert.IsFalse(result.ContainsKey("Time")); + } + + [TestMethod] + public void ValidateTimeEntry_errechnet_die_Minuten_aus_korrekter_Zeiteingabe_richtig_Format9() + { + // = 0 + var time = ""; + var timeEntry = new TimeEntry(); + var result = TimeEntryService.ValidateTimeEntry(timeEntry, time); + Assert.AreEqual(0, timeEntry.Minutes); + Assert.IsFalse(result.ContainsKey("Time")); + } + + } +} + diff --git a/Tools/MiteDesk.Tools.Connector/Connector.cs b/Tools/MiteDesk.Tools.Connector/Connector.cs new file mode 100644 index 0000000..96b54bd --- /dev/null +++ b/Tools/MiteDesk.Tools.Connector/Connector.cs @@ -0,0 +1,146 @@ +using System; +using System.IO; +using System.Net; +using System.Text; +using System.Xml.Linq; +using SixtyNineDegrees.MiteDesk.Core.Model; + +namespace SixtyNineDegrees.MiteDesk.Tools.Connector +{ + public class Connector + { + + public Connector(AppSettings settings) + { + Settings = settings; + } + + private readonly AppSettings Settings; + + private string MiteAPIBaseURL + { + get { return "https://" + Settings.AccountName + ".mite.yo.lk/"; } + } + + private void Authenticate(ref HttpWebRequest request) + { + + if (!string.IsNullOrEmpty(Settings.APIKey)) + request.Headers.Add("X-MiteApiKey", Settings.APIKey); + else + request.Credentials = new NetworkCredential(Settings.Email, Settings.Password); + + if (Settings.UseProxy && !string.IsNullOrEmpty(Settings.ProxyServer)) + { + WebProxy proxy = new WebProxy(Settings.ProxyServer, Settings.ProxyPort); + if (!string.IsNullOrEmpty(Settings.ProxyUser) && !string.IsNullOrEmpty(Settings.ProxyPassword)) + { + proxy.UseDefaultCredentials = false; + proxy.Credentials = new NetworkCredential(Settings.ProxyUser, Settings.ProxyUser); + } + else + { + proxy.UseDefaultCredentials = false; + proxy.Credentials = null; + } + request.Proxy = proxy; + } + + // User-Agent + request.UserAgent = "mite.desk/1.2.14"; + + } + + public XElement HttpGet(string url) + { + try + { + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(MiteAPIBaseURL + url); + Authenticate(ref request); + request.Timeout = 15000; + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + using (StreamReader reader = new StreamReader(response.GetResponseStream())) + { + return XElement.Parse(reader.ReadToEnd()); + } + } + } + catch (Exception exception) + { + throw new MiteConnectorException(exception.Message); + } + } + + public XElement HttpPost(string url, string xml) + { + try + { + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(MiteAPIBaseURL + url); + Authenticate(ref request); + request.Timeout = 5000; + request.Method = "POST"; + request.ContentType = "application/xml"; + + byte[] data = Encoding.UTF8.GetBytes(xml); + + using (var requestStream = request.GetRequestStream()) + { + requestStream.Write(data, 0, data.Length); + } + + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + return XElement.Parse(new StreamReader(response.GetResponseStream()).ReadToEnd()); + } + } + catch (Exception exception) + { + throw new MiteConnectorException(exception.Message); + } + } + + public void HttpDelete(string url) + { + try + { + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(MiteAPIBaseURL + url); + Authenticate(ref request); + request.Timeout = 5000; + request.Method = "DELETE"; + request.ContentType = "application/xml"; + using (request.GetResponse()) { } + } + catch (Exception exception) + { + throw new MiteConnectorException(exception.Message); + } + } + + public void HttpPut(string url, string xml) + { + try + { + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(MiteAPIBaseURL + url); + Authenticate(ref request); + request.Timeout = 5000; + request.Method = "PUT"; + request.ContentType = "application/xml"; + + byte[] data = Encoding.UTF8.GetBytes(xml); + + using (var requestStream = request.GetRequestStream()) + { + requestStream.Write(data, 0, data.Length); + } + + using (request.GetResponse()) { } + } + catch (Exception exception) + { + throw new MiteConnectorException(exception.Message); + } + } + + } +} \ No newline at end of file diff --git a/Tools/MiteDesk.Tools.Connector/MiteConnectorException.cs b/Tools/MiteDesk.Tools.Connector/MiteConnectorException.cs new file mode 100644 index 0000000..9fa4d62 --- /dev/null +++ b/Tools/MiteDesk.Tools.Connector/MiteConnectorException.cs @@ -0,0 +1,21 @@ +using System; + +namespace SixtyNineDegrees.MiteDesk.Tools.Connector +{ + public class MiteConnectorException : Exception + { + + public MiteConnectorException(string message) + { + this.message = message; + } + + private readonly string message; + + public override string Message + { + get { return message; } + } + + } +} diff --git a/Tools/MiteDesk.Tools.Connector/MiteDesk.Tools.Connector.csproj b/Tools/MiteDesk.Tools.Connector/MiteDesk.Tools.Connector.csproj new file mode 100644 index 0000000..ad089f8 --- /dev/null +++ b/Tools/MiteDesk.Tools.Connector/MiteDesk.Tools.Connector.csproj @@ -0,0 +1,101 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {1B74B3CE-8969-4157-8B86-AEDA27494109} + Library + Properties + SixtyNineDegrees.MiteDesk.Tools.Connector + mite.desk.tools.connector + v3.5 + 512 + true + + + + + 3.5 + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AllRules.ruleset + + + + + + + 3.5 + + + + + + + + + + {AE69F9FC-F9D4-41D8-9B00-1261F492FB89} + MiteDesk.Core.Model + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + \ No newline at end of file diff --git a/Tools/MiteDesk.Tools.Connector/Properties/AssemblyInfo.cs b/Tools/MiteDesk.Tools.Connector/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..ce91e2d --- /dev/null +++ b/Tools/MiteDesk.Tools.Connector/Properties/AssemblyInfo.cs @@ -0,0 +1,14 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("mite.desk.tools.connector")] +[assembly: AssemblyDescription("mite.desk..tools.connector")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("mite.desk")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] +[assembly: Guid("469493a7-3d64-4c65-82ba-db1f9bfc95fb")] + +[assembly: AssemblyVersion("1.2.17")] +[assembly: AssemblyFileVersion("1.2.17")] diff --git a/Tools/MiteDesk.Tools.RegistryCleaner/MiteDesk.Tools.RegistryCleaner.csproj b/Tools/MiteDesk.Tools.RegistryCleaner/MiteDesk.Tools.RegistryCleaner.csproj new file mode 100644 index 0000000..04f225e --- /dev/null +++ b/Tools/MiteDesk.Tools.RegistryCleaner/MiteDesk.Tools.RegistryCleaner.csproj @@ -0,0 +1,97 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {CA4BEC83-51A1-4E67-B2C7-3598E51A98AF} + Exe + Properties + MiteDesk.Tools.RegistryCleaner + MiteDesk.Tools.RegistryCleaner + v3.5 + 512 + + + 3.5 + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AllRules.ruleset + + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + \ No newline at end of file diff --git a/Tools/MiteDesk.Tools.RegistryCleaner/Program.cs b/Tools/MiteDesk.Tools.RegistryCleaner/Program.cs new file mode 100644 index 0000000..04f3add --- /dev/null +++ b/Tools/MiteDesk.Tools.RegistryCleaner/Program.cs @@ -0,0 +1,22 @@ +using System; +using Microsoft.Win32; + +namespace MiteDesk.Tools.RegistryCleaner +{ + class Program + { + static void Main(string[] args) + { + if (Registry.CurrentUser.OpenSubKey("Software\\69 Grad") != null) + { + Registry.CurrentUser.DeleteSubKeyTree("Software\\69 Grad"); + Console.WriteLine("Aktion erfolgreich. Du kannst die Anwendung jetzt schließen."); + } + else + { + Console.WriteLine("Nichts zum Aufräumen gefunden. Du kannst die Anwendung jetzt schließen."); + } + Console.ReadLine(); + } + } +} diff --git a/Tools/MiteDesk.Tools.RegistryCleaner/Properties/AssemblyInfo.cs b/Tools/MiteDesk.Tools.RegistryCleaner/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..5bc101a --- /dev/null +++ b/Tools/MiteDesk.Tools.RegistryCleaner/Properties/AssemblyInfo.cs @@ -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 mit einer Assembly verknüpft sind. +[assembly: AssemblyTitle("MiteDesk.Tools.RegistryCleaner")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("69°")] +[assembly: AssemblyProduct("MiteDesk.Tools.RegistryCleaner")] +[assembly: AssemblyCopyright("Copyright © 69° 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("241206ef-9009-4fb1-b81c-1dbd90434b53")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/mite.desk.sln b/mite.desk.sln new file mode 100644 index 0000000..f08ef63 --- /dev/null +++ b/mite.desk.sln @@ -0,0 +1,113 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.30723.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{3E096A6F-58B7-4CC9-AE27-BC39AB46A999}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "App", "App", "{162AA04A-FA08-44DE-B642-25EBE5F7FB4C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{40B8DF61-C708-4EF3-89A1-F74AEED41DBF}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{EC53198E-BB8B-4D88-A8A1-55C0A3ED752A}" + ProjectSection(SolutionItems) = preProject + LocalTestRun.testrunconfig = LocalTestRun.testrunconfig + mite.desk.vsmdi = mite.desk.vsmdi + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Setup", "Setup", "{C7B9AE1C-36C3-4367-8BB2-308A4C763885}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{43527D84-2933-43D7-AB30-7721020FFF27}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiteDesk.Core.Model", "Core\MiteDesk.Core.Model\MiteDesk.Core.Model.csproj", "{AE69F9FC-F9D4-41D8-9B00-1261F492FB89}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiteDesk.Core.Services", "Core\MiteDesk.Core.Services\MiteDesk.Core.Services.csproj", "{B7B67B5C-47F1-4E3D-8759-EA8E31AC951E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiteDesk.Core.Data", "Core\MiteDesk.Core.Data\MiteDesk.Core.Data.csproj", "{AD8740D8-14A7-4A31-8F96-7DF850D3D957}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiteDesk.WinForms", "App\MiteDesk.WinForms\MiteDesk.WinForms.csproj", "{352CDDB3-9A72-43FE-84B4-7021C6CBCAD4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiteDesk.Core.Services.Tests", "Tests\MiteDesk.Core.Services.Tests\MiteDesk.Core.Services.Tests.csproj", "{C9BD6574-4771-4068-B321-40943DCD3C88}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiteDesk.Core.Data.Tests", "Tests\MiteDesk.Core.Data.Tests\MiteDesk.Core.Data.Tests.csproj", "{DFD37C08-9F36-4534-96B2-4A67AEE85F00}" +EndProject +Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "MiteDesk.WinForms.Setup", "Deployment\MiteDesk.WinForms.Setup\MiteDesk.WinForms.Setup.vdproj", "{D3F6F4AB-5890-4646-9DD6-C0DD31008ACA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiteDesk.Tools.Connector", "Tools\MiteDesk.Tools.Connector\MiteDesk.Tools.Connector.csproj", "{1B74B3CE-8969-4157-8B86-AEDA27494109}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiteDesk.Core.Infrastructure", "Core\MiteDesk.Core.Infrastructure\MiteDesk.Core.Infrastructure.csproj", "{733C8DE9-36B5-4C9F-9701-FDD19B64B966}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiteDesk.Core.Infrastructure.Tests", "Tests\MiteDesk.Core.Infrastructure.Tests\MiteDesk.Core.Infrastructure.Tests.csproj", "{BEE63E05-FFC7-4F80-A938-2B4272683E0F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiteDesk.Tools.RegistryCleaner", "Tools\MiteDesk.Tools.RegistryCleaner\MiteDesk.Tools.RegistryCleaner.csproj", "{CA4BEC83-51A1-4E67-B2C7-3598E51A98AF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AE69F9FC-F9D4-41D8-9B00-1261F492FB89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AE69F9FC-F9D4-41D8-9B00-1261F492FB89}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AE69F9FC-F9D4-41D8-9B00-1261F492FB89}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AE69F9FC-F9D4-41D8-9B00-1261F492FB89}.Release|Any CPU.Build.0 = Release|Any CPU + {B7B67B5C-47F1-4E3D-8759-EA8E31AC951E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B7B67B5C-47F1-4E3D-8759-EA8E31AC951E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B7B67B5C-47F1-4E3D-8759-EA8E31AC951E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B7B67B5C-47F1-4E3D-8759-EA8E31AC951E}.Release|Any CPU.Build.0 = Release|Any CPU + {AD8740D8-14A7-4A31-8F96-7DF850D3D957}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AD8740D8-14A7-4A31-8F96-7DF850D3D957}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AD8740D8-14A7-4A31-8F96-7DF850D3D957}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AD8740D8-14A7-4A31-8F96-7DF850D3D957}.Release|Any CPU.Build.0 = Release|Any CPU + {352CDDB3-9A72-43FE-84B4-7021C6CBCAD4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {352CDDB3-9A72-43FE-84B4-7021C6CBCAD4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {352CDDB3-9A72-43FE-84B4-7021C6CBCAD4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {352CDDB3-9A72-43FE-84B4-7021C6CBCAD4}.Release|Any CPU.Build.0 = Release|Any CPU + {C9BD6574-4771-4068-B321-40943DCD3C88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C9BD6574-4771-4068-B321-40943DCD3C88}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C9BD6574-4771-4068-B321-40943DCD3C88}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C9BD6574-4771-4068-B321-40943DCD3C88}.Release|Any CPU.Build.0 = Release|Any CPU + {DFD37C08-9F36-4534-96B2-4A67AEE85F00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DFD37C08-9F36-4534-96B2-4A67AEE85F00}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DFD37C08-9F36-4534-96B2-4A67AEE85F00}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DFD37C08-9F36-4534-96B2-4A67AEE85F00}.Release|Any CPU.Build.0 = Release|Any CPU + {D3F6F4AB-5890-4646-9DD6-C0DD31008ACA}.Debug|Any CPU.ActiveCfg = Debug + {D3F6F4AB-5890-4646-9DD6-C0DD31008ACA}.Release|Any CPU.ActiveCfg = Release + {1B74B3CE-8969-4157-8B86-AEDA27494109}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1B74B3CE-8969-4157-8B86-AEDA27494109}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1B74B3CE-8969-4157-8B86-AEDA27494109}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1B74B3CE-8969-4157-8B86-AEDA27494109}.Release|Any CPU.Build.0 = Release|Any CPU + {733C8DE9-36B5-4C9F-9701-FDD19B64B966}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {733C8DE9-36B5-4C9F-9701-FDD19B64B966}.Debug|Any CPU.Build.0 = Debug|Any CPU + {733C8DE9-36B5-4C9F-9701-FDD19B64B966}.Release|Any CPU.ActiveCfg = Release|Any CPU + {733C8DE9-36B5-4C9F-9701-FDD19B64B966}.Release|Any CPU.Build.0 = Release|Any CPU + {BEE63E05-FFC7-4F80-A938-2B4272683E0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BEE63E05-FFC7-4F80-A938-2B4272683E0F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BEE63E05-FFC7-4F80-A938-2B4272683E0F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BEE63E05-FFC7-4F80-A938-2B4272683E0F}.Release|Any CPU.Build.0 = Release|Any CPU + {CA4BEC83-51A1-4E67-B2C7-3598E51A98AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CA4BEC83-51A1-4E67-B2C7-3598E51A98AF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CA4BEC83-51A1-4E67-B2C7-3598E51A98AF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CA4BEC83-51A1-4E67-B2C7-3598E51A98AF}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {EC53198E-BB8B-4D88-A8A1-55C0A3ED752A} = {40B8DF61-C708-4EF3-89A1-F74AEED41DBF} + {AE69F9FC-F9D4-41D8-9B00-1261F492FB89} = {3E096A6F-58B7-4CC9-AE27-BC39AB46A999} + {B7B67B5C-47F1-4E3D-8759-EA8E31AC951E} = {3E096A6F-58B7-4CC9-AE27-BC39AB46A999} + {AD8740D8-14A7-4A31-8F96-7DF850D3D957} = {3E096A6F-58B7-4CC9-AE27-BC39AB46A999} + {352CDDB3-9A72-43FE-84B4-7021C6CBCAD4} = {162AA04A-FA08-44DE-B642-25EBE5F7FB4C} + {C9BD6574-4771-4068-B321-40943DCD3C88} = {40B8DF61-C708-4EF3-89A1-F74AEED41DBF} + {DFD37C08-9F36-4534-96B2-4A67AEE85F00} = {40B8DF61-C708-4EF3-89A1-F74AEED41DBF} + {D3F6F4AB-5890-4646-9DD6-C0DD31008ACA} = {C7B9AE1C-36C3-4367-8BB2-308A4C763885} + {1B74B3CE-8969-4157-8B86-AEDA27494109} = {43527D84-2933-43D7-AB30-7721020FFF27} + {733C8DE9-36B5-4C9F-9701-FDD19B64B966} = {3E096A6F-58B7-4CC9-AE27-BC39AB46A999} + {BEE63E05-FFC7-4F80-A938-2B4272683E0F} = {40B8DF61-C708-4EF3-89A1-F74AEED41DBF} + {CA4BEC83-51A1-4E67-B2C7-3598E51A98AF} = {43527D84-2933-43D7-AB30-7721020FFF27} + EndGlobalSection + GlobalSection(TestCaseManagementSettings) = postSolution + CategoryFile = mite.desk.vsmdi + EndGlobalSection +EndGlobal diff --git a/mite.desk.vsmdi b/mite.desk.vsmdi new file mode 100644 index 0000000..03611a8 --- /dev/null +++ b/mite.desk.vsmdi @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file