Browse Source

Added "Email Logs" button to MessageWindow. Extracted email utils into InABox.Wpf

Kenric Nugteren 1 năm trước cách đây
mục cha
commit
9ba36a2ec8

+ 1 - 1
InABox.Core/Classes/Document/Document.cs

@@ -59,7 +59,7 @@ namespace InABox.Core
             return new Document
             {
                 Data = data,
-                FileName = filename,
+                FileName = Path.GetFileName(filename),
                 CRC = CoreUtils.CalculateCRC(data),
                 TimeStamp = new FileInfo(filename).LastWriteTime
             };

+ 34 - 3
inabox.wpf/Forms/MessageWindow.xaml.cs

@@ -293,8 +293,11 @@ public partial class MessageWindow : Window, INotifyPropertyChanged
             .Message(message ?? exception.Message)
             .Title(title)
             .Details(CoreUtils.FormatException(exception))
-            .Image(image ?? _warning);
-        //    .AddButton(new MessageWindowButton("Show Logs", ShowLogs_Click, MessageWindowButtonPosition.Left));
+            .Image(image ?? _warning)
+            .AddButton(new MessageWindowButton("Email Logs", (window, button) =>
+            {
+                EmailLogs_Click(exception);
+            }, MessageWindowButtonPosition.Left));
 
         var showDetailsButton = new MessageWindowButton("Show Details", (win, button) =>
         {
@@ -308,6 +311,31 @@ public partial class MessageWindow : Window, INotifyPropertyChanged
             .AddOKButton();
     }
 
+    private static void EmailLogs_Click(Exception e)
+    {
+        var logFile = Path.Combine(CoreUtils.GetPath(), string.Format("{0:yyyy-MM-dd}.log", DateTime.Today));
+
+        const int nRead = 1024 * 1024;
+
+        byte[] data;
+        using (var stream = File.OpenRead(logFile))
+        {
+            if (stream.Length > nRead)
+            {
+                stream.Seek(-nRead, SeekOrigin.End);
+            }
+
+            data = new BinaryReader(stream).ReadBytes(Math.Min(nRead, (int)stream.Length));
+        }
+
+        var message = EmailUtils.CreateMessage(
+            subject: "Error logs",
+            to: "support@prsdigital.com.au",
+            body: $"Error logs for PRS:\n\nException: {CoreUtils.FormatException(e)}");
+        message.AddAttachment("Error Logs.txt", data);
+        EmailUtils.OpenEmail(message);
+    }
+
     /// <summary>
     /// Display a message box for a non-exception error, giving options to view the logs.
     /// </summary>
@@ -332,7 +360,10 @@ public partial class MessageWindow : Window, INotifyPropertyChanged
         }
 
         window.Image(image ?? _warning)
-            .AddButton(new MessageWindowButton("Show Logs", ShowLogs_Click, MessageWindowButtonPosition.Left));
+            .AddButton(new MessageWindowButton(
+                "Email Logs",
+                (window, button) => EmailLogs_Click(new Exception(details ?? message)),
+                MessageWindowButtonPosition.Left));
 
         if(details is not null)
         {

+ 163 - 0
inabox.wpf/Utils/EmailUtils.cs

@@ -0,0 +1,163 @@
+using InABox.Clients;
+using InABox.Core;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Net.Mail;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Controls;
+using System.Windows;
+using System.Windows.Forms;
+using System.Drawing;
+using InABox.WPF;
+using MessageBox = System.Windows.Forms.MessageBox;
+using TextBox = System.Windows.Controls.TextBox;
+using InABox.Wpf.Reports;
+
+namespace InABox.Wpf;
+
+public static class EmailUtils
+{
+    /// <summary>
+     /// Creates and opens an email with the default email app - selected by the user.
+     /// This method is for emails with a PDF attachment. Provide the file name and data.
+     /// Optionally provide from, subject and body.
+     /// If from is not provided, an attempt will be made to find the User's email address - if empty it will throw an error (cannot be empty)
+     /// </summary>
+     /// <param name="attachmentname"></param>
+     /// <param name="attachmentdata"></param>
+     /// <param name="from"></param>
+     /// <param name="subject"></param>
+     /// <param name="body"></param>
+    public static void CreateEMLFile(string attachmentname, byte[] attachmentdata, string from = "", string subject = "", string body = "", string to = "")
+    {
+        var message = CreateMessage(from, subject, body, to);
+
+        message = AddAttachment(message, attachmentname, attachmentdata);
+
+        OpenEmail(message, attachmentname);
+    }
+    
+    /// <summary>
+    /// Creates and opens an email with the default email app - selected by the user.
+    /// This method is for emails with multiple PDF attachments. Provide the a Dictionary of names and byte arrays
+    /// Optionally provide from, subject and body.
+    /// If from is not provided, an attempt will be made to find the User's email address - if empty it will throw an error (cannot be empty)
+    /// </summary>
+    /// <param name="attachmentname"></param>
+    /// <param name="attachmentdata"></param>
+    /// <param name="from"></param>
+    /// <param name="subject"></param>
+    /// <param name="body"></param>
+    public static void CreateEMLFile(Dictionary<string,byte[]> attachments, string from = "", string subject = "", string body = "", string to = "")
+    {
+        var message = CreateMessage(from, subject, body, to);
+
+        foreach (var key in attachments.Keys)
+            AddAttachment(message, key, attachments[key]);
+
+        OpenEmail(message);
+    }
+
+    /// <summary>
+    /// Creates and opens an email with the default email app - selected by the user.
+    /// This method is for emails with no attachments.
+    /// Optionally provide from, subject and body.
+    /// If from is not provided, an attempt will be made to find the User's email address - if empty it will throw an error (cannot be empty)
+    /// </summary>
+    /// <param name="from"></param>
+    /// <param name="subject"></param>
+    /// <param name="body"></param>
+
+    public static void CreateEMLFile(string? from, string? subject, string? body)
+    {
+        var message = CreateMessage(from, subject, body);
+
+        OpenEmail(message, "Message from " + message.From);
+    }
+
+    public static void OpenEmail(MailMessage message, string? name = null)
+    {
+        var filename = Path.Combine(
+            Path.GetTempPath(), 
+            Path.ChangeExtension(String.IsNullOrWhiteSpace(name) ? Guid.NewGuid().ToString() : name, ".eml")
+        );
+
+        using (var filestream = File.Open(filename, FileMode.Create))
+        {
+            var binaryWriter = new BinaryWriter(filestream);
+            //Write the Unsent header to the file so the mail client knows this mail must be presented in "New message" mode
+            binaryWriter.Write(Encoding.UTF8.GetBytes("X-Unsent: 1" + Environment.NewLine));
+
+            var assembly = typeof(SmtpClient).Assembly;
+            var mailWriterType = assembly.GetType("System.Net.Mail.MailWriter")!;
+
+            // Get reflection info for MailWriter contructor
+            var mailWriterConstructor =
+                mailWriterType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(Stream), typeof(bool) }, null)!;
+
+            // Construct MailWriter object with our FileStream
+            var mailWriter = mailWriterConstructor.Invoke(new object[] { filestream, true });
+
+            // Get reflection info for Send() method on MailMessage
+            var sendMethod = typeof(MailMessage).GetMethod("Send", BindingFlags.Instance | BindingFlags.NonPublic)!;
+
+            sendMethod.Invoke(message, BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { mailWriter, true, true }, null);
+
+            // Finally get reflection info for Close() method on our MailWriter
+            var closeMethod = mailWriter.GetType().GetMethod("Close", BindingFlags.Instance | BindingFlags.NonPublic)!;
+
+            // Call close method
+            closeMethod.Invoke(mailWriter, BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { }, null);
+        }
+
+        // Open the file with the default associated application registered on the local machine
+        Process.Start(new ProcessStartInfo(filename) { UseShellExecute = true });
+    }
+
+    public static MailMessage CreateMessage(string? from = null, string? subject = null, string? body = null, string? to = null)
+    {
+        return new MailMessage(
+            from.NotWhiteSpaceOr(GetAddressFromUser()).NotWhiteSpaceOr("example@outlook.com.au"),
+            to.NotWhiteSpaceOr("example@outlook.com.au"),
+            subject.NotWhiteSpaceOr("Enter subject"),
+            body.NotWhiteSpaceOr("Enter message"))
+        {
+            IsBodyHtml = false
+        };
+    }
+
+    private static string GetAddressFromUser()
+    {
+        CoreTable table = new Client<User>().Query(new Filter<User>(x => x.ID).IsEqualTo(ClientFactory.UserGuid)
+            , new Columns<User>(x => x.EmailAddress));
+        User user = table.Rows.FirstOrDefault().ToObject<User>();
+
+        if (!string.IsNullOrWhiteSpace(user.EmailAddress))
+            return user.EmailAddress;
+        else
+            MessageWindow.ShowMessage("Current User Email Address is blank - please fill in (Human Resources -> User Accounts -> Choose your User -> Email Settings -> Email Address", "Error");
+
+        return "";
+
+    }
+
+    public static MailMessage AddAttachment(this MailMessage message, string attachmentname, byte[] attachmentdata)
+    {
+        var attachment = Path.Combine(
+            Path.GetTempPath(), 
+            String.IsNullOrWhiteSpace(Path.GetExtension(attachmentname)) 
+                ? Path.ChangeExtension(attachmentname, ".pdf") 
+                : attachmentname 
+        );
+        File.WriteAllBytes(attachment, attachmentdata);
+
+        message.Attachments.Add(new Attachment(attachment));
+
+        return message;
+    }
+}