diff --git a/Emails/EmailBase.cs b/Emails/EmailBase.cs index 77c3709..3f8d477 100644 --- a/Emails/EmailBase.cs +++ b/Emails/EmailBase.cs @@ -2,6 +2,7 @@ using BlazorTemplater; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Mvc; using NejCommon.Services.Email; +using System.Net.Mail; using System.Runtime.CompilerServices; namespace NejCommon.Emails @@ -33,5 +34,7 @@ namespace NejCommon.Emails { return "DEV: " + this.ToString(); } + + public virtual List GetAttachments() => new List(); } } diff --git a/Emails/wwwroot/output.css b/Emails/wwwroot/output.css index 86a9ef8..6ce011d 100644 --- a/Emails/wwwroot/output.css +++ b/Emails/wwwroot/output.css @@ -1,113 +1,5 @@ -*, ::before, ::after { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-gradient-from-position: ; - --tw-gradient-via-position: ; - --tw-gradient-to-position: ; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(59 130 246 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; - --tw-contain-size: ; - --tw-contain-layout: ; - --tw-contain-paint: ; - --tw-contain-style: ; -} - -::backdrop { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-gradient-from-position: ; - --tw-gradient-via-position: ; - --tw-gradient-to-position: ; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(59 130 246 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; - --tw-contain-size: ; - --tw-contain-layout: ; - --tw-contain-paint: ; - --tw-contain-style: ; -} - /* -! tailwindcss v3.4.13 | MIT License | https://tailwindcss.com +! tailwindcss v3.2.4 | MIT License | https://tailwindcss.com */ /* @@ -139,12 +31,9 @@ 3. Use a more readable tab size. 4. Use the user's configured `sans` font-family by default. 5. Use the user's configured `sans` font-feature-settings by default. -6. Use the user's configured `sans` font-variation-settings by default. -7. Disable tap highlights on iOS */ -html, -:host { +html { line-height: 1.5; /* 1 */ -webkit-text-size-adjust: 100%; @@ -154,14 +43,10 @@ html, -o-tab-size: 4; tab-size: 4; /* 3 */ - font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-family: "Noto Sans", "Roboto", "Segoe UI", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; /* 4 */ font-feature-settings: normal; /* 5 */ - font-variation-settings: normal; - /* 6 */ - -webkit-tap-highlight-color: transparent; - /* 7 */ } /* @@ -233,10 +118,8 @@ strong { } /* -1. Use the user's configured `mono` font-family by default. -2. Use the user's configured `mono` font-feature-settings by default. -3. Use the user's configured `mono` font-variation-settings by default. -4. Correct the odd `em` font sizing in all browsers. +1. Use the user's configured `mono` font family by default. +2. Correct the odd `em` font sizing in all browsers. */ code, @@ -245,12 +128,8 @@ samp, pre { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; /* 1 */ - font-feature-settings: normal; - /* 2 */ - font-variation-settings: normal; - /* 3 */ font-size: 1em; - /* 4 */ + /* 2 */ } /* @@ -309,18 +188,12 @@ select, textarea { font-family: inherit; /* 1 */ - font-feature-settings: inherit; - /* 1 */ - font-variation-settings: inherit; - /* 1 */ font-size: 100%; /* 1 */ font-weight: inherit; /* 1 */ line-height: inherit; /* 1 */ - letter-spacing: inherit; - /* 1 */ color: inherit; /* 1 */ margin: 0; @@ -344,9 +217,9 @@ select { */ button, -input:where([type='button']), -input:where([type='reset']), -input:where([type='submit']) { +[type='button'], +[type='reset'], +[type='submit'] { -webkit-appearance: button; /* 1 */ background-color: transparent; @@ -465,14 +338,6 @@ menu { padding: 0; } -/* -Reset default styling for dialogs. -*/ - -dialog { - padding: 0; -} - /* Prevent resizing textareas horizontally by default. */ @@ -554,24 +419,306 @@ video { display: none; } -.visible { - visibility: visible; +*, ::before, ::after { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; +} + +::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; } .static { position: static; } +.absolute { + position: absolute; +} + +.ml-2 { + margin-left: 0.5rem; +} + .inline { display: inline; } +.flex { + display: flex; +} + .table { display: table; } -.hidden { - display: none; +.contents { + display: contents; +} + +.h-full { + height: 100%; +} + +.h-16 { + height: 4rem; +} + +.h-48 { + height: 12rem; +} + +.w-full { + width: 100%; +} + +.w-16 { + width: 4rem; +} + +.w-48 { + width: 12rem; +} + +.flex-grow { + flex-grow: 1; +} + +.flex-col { + flex-direction: column; +} + +.flex-wrap { + flex-wrap: wrap; +} + +.items-end { + align-items: flex-end; +} + +.items-center { + align-items: center; +} + +.justify-start { + justify-content: flex-start; +} + +.justify-end { + justify-content: flex-end; +} + +.justify-center { + justify-content: center; +} + +.justify-between { + justify-content: space-between; +} + +.self-end { + align-self: flex-end; +} + +.rounded-md { + border-radius: 0.375rem; +} + +.rounded { + border-radius: 0.25rem; +} + +.border-4 { + border-width: 4px; +} + +.border { + border-width: 1px; +} + +.bg-primary { + --tw-bg-opacity: 1; + background-color: rgb(var(--primary) / var(--tw-bg-opacity)); +} + +.py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.py-4 { + padding-top: 1rem; + padding-bottom: 1rem; +} + +.px-10 { + padding-left: 2.5rem; + padding-right: 2.5rem; +} + +.pt-2 { + padding-top: 0.5rem; +} + +.pb-2 { + padding-bottom: 0.5rem; +} + +.pl-8 { + padding-left: 2rem; +} + +.pr-8 { + padding-right: 2rem; +} + +.pr-4 { + padding-right: 1rem; +} + +.pt-1 { + padding-top: 0.25rem; +} + +.text-left { + text-align: left; +} + +.text-sm { + font-size: 0.875rem; + line-height: 1.25rem; +} + +.text-xs { + font-size: 0.75rem; + line-height: 1rem; +} + +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} + +.text-3xl { + font-size: 1.875rem; + line-height: 2.25rem; +} + +.text-xl { + font-size: 1.25rem; + line-height: 1.75rem; +} + +.text-2xl { + font-size: 1.5rem; + line-height: 2rem; +} + +.font-semibold { + font-weight: 600; +} + +.font-bold { + font-weight: 700; +} + +.text-secondary { + --tw-text-opacity: 1; + color: rgb(var(--secondary-text) / var(--tw-text-opacity)); +} + +.text-primary { + --tw-text-opacity: 1; + color: rgb(var(--primary-text) / var(--tw-text-opacity)); +} + +.line-through { + text-decoration-line: line-through; } .filter { diff --git a/Services/Email/IEmailService.cs b/Services/Email/IEmailService.cs index 2a77095..638a354 100644 --- a/Services/Email/IEmailService.cs +++ b/Services/Email/IEmailService.cs @@ -14,10 +14,11 @@ public interface IEmailService var obj = Activator.CreateInstance(); obj.Data = data; var subject = obj.GetSubject(); + var atts = obj.GetAttachments(); //invoke the GetHTML function asynchronously and wait for the result var html = await (Task)func.Invoke(null, new object[] { data }); - await SendEmailAsync(recipient, subject, html, attachments); + await SendEmailAsync(recipient, subject, html, attachments == null ? atts : atts.Concat(attachments).ToList()); } } \ No newline at end of file diff --git a/Services/Email/SMTPService.cs b/Services/Email/SMTPService.cs index fbdc91c..0be8c78 100644 --- a/Services/Email/SMTPService.cs +++ b/Services/Email/SMTPService.cs @@ -38,6 +38,7 @@ public class SMTPService : IEmailService using (var client = new MailKit.Net.Smtp.SmtpClient()) { + await Task.Delay(100); await client.ConnectAsync(_settings.Host, _settings.Port); // Note: only needed if the SMTP server requires authentication @@ -45,6 +46,8 @@ public class SMTPService : IEmailService await client.SendAsync(mess); await client.DisconnectAsync(true); + + await Task.Delay(100); using (var imap = new MailKit.Net.Imap.ImapClient()) {