ZauberCMS
  • 🪄Welcome to ZauberCMS
  • Getting Started
    • 💡Quick Start
      • Databases
  • Creating A Website
    • 📹Website Build Video Tutorial
    • Content Types
      • Element Types
      • Compositions
    • Current Content Properties
      • Textbox & Textarea
      • Text Editor (Radzen)
      • Text Editor (TinyMCE)
      • Numeric
      • True / False
      • Rating
      • Checkbox, Dropdown & Radio Lists
      • Media Picker
      • Navigation
      • Material Icon Picker
      • Content Picker
      • Date Picker
      • Custom Component Picker
      • Api Key Picker
      • Colour Picker
      • Block List Editor
      • Editor Notes
      • Google Map Location Picker
      • Simple List Manager
      • Simple Dictionary Manager
      • SEO Property
      • Code Editor
      • Colour Theme Picker
    • Content
      • Publish & Unpublish
    • Querying Data
      • Extension Methods
    • Views
    • Controllers (Route Hi-Jacking)
    • Custom Components
    • Users & Roles
      • Restrict Access By Role
    • Logs
    • Audit
    • Global Settings
      • Using Global Settings
    • SEO Sitemaps
    • Hosting
  • Extending ZauberCMS
    • Overview
    • BlockListEditor
      • Content Block Preview
      • Content Block
    • Custom List Data
    • Custom Content Property
    • Custom Validation
    • Custom Admin Sections
      • Section Nav Group Menu
      • Trees
        • Tree Context Menus
      • Reusing Content Editors
    • Saving Custom Data
    • Using AppState Events
    • Before & After Save
    • Email & Storage Providers
    • Seed Data
    • SEO Checks
  • Identity
    • Overview
    • External Authentication Providers
    • Account Layout
  • Language
    • Overview
    • Adding Language Dictionaries
    • Setting The Language
    • Using Language Dictionaries
  • AppSettings
    • Detailed Errors
    • Media Settings
    • Enable Path Urls
Powered by GitBook
On this page
  • IStorageProvider
  • IEmailProvider
  1. Extending ZauberCMS

Email & Storage Providers

PreviousBefore & After SaveNextSeed Data

Last updated 3 months ago

There are two providers currently added for handling email and file storage (The media uses this provider for saving media).

IStorageProvider

Responsible for saving media and files, the built in provider is the DiskStorageProvider that saves files to the local disk

You can replace this by creating your own provider that implements IStorageProvider and then update the appSettings to point to your provider instead of the Disk provider.

"Plugins": {
  "StorageProvider": "ZauberCMS.Core.Providers.DiskStorageProvider",
}

IEmailProvider

The email provider is responsible for sending all emails within the CMS, currently the default provider is an SMTP provider

Where you can set you your SMTP details in the appSettings

  "Plugins": {
    "EmailProvider": "ZauberCMS.Core.Providers.SmtpEmailProvider"
  }

Just like the StorageProvider you can create your own if you implement IEmailProvider and then replace the default one in the appSettings

"Email": {
  "SenderEmail": "name@websitename.co.uk",
  "Smtp": {
    "Host": "smtp.sendgrid.net",
    "Port": 587,
    "Username": "mail_username",
    "Password": "mail_password"
  }
},
https://github.com/YodasMyDad/ZauberCMS/blob/main-mvc/ZauberCMS.Core/Providers/DiskStorageProvider.cs
using MediatR;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using ZauberCMS.Core.Extensions;
using ZauberCMS.Core.Settings;
using ZauberCMS.Core.Shared.Models;

namespace ZauberCMS.Core.Providers;

public class DiskStorageProvider(
    IWebHostEnvironment env,
    IMediator mediator,
    IOptions<ZauberSettings> settings)
    : IStorageProvider
{
    private readonly ZauberSettings _settings = settings.Value;

    /// <inheritdoc />
    public async Task<HandlerResult<Media.Models.Media>> CanUseFile(IBrowserFile file, bool onlyImages = false)
    {
        var globalSettingsRequest = await mediator.GetGlobalSettings();

        var result = new HandlerResult<Media.Models.Media> { Success = true };

        if (onlyImages && !file.IsImage())
        {
            result.Messages.Add(new ResultMessage
            {
                Message = "File must be an image only",
                MessageType = ResultMessageType.Error
            });
            result.Success = false;
        }
        else
        {
            // Check allowed filetypes
            if (file.Name.Contains(globalSettingsRequest.AllowedFileTypes))
            {
                if (file.Size > globalSettingsRequest.MaxUploadFileSizeInBytes)
                {
                    result.Messages.Add(new ResultMessage
                    {
                        Message = "File is too large",
                        MessageType = ResultMessageType.Error
                    });
                    result.Success = false;
                }
            }
            else
            {
                result.Messages.Add(new ResultMessage
                {
                    Message = "File not allowed",
                    MessageType = ResultMessageType.Error
                });
                result.Success = false;
            }
        }

        return result;
    }

    /// <inheritdoc />
    public Task<bool> DeleteFile(string? url)
    {
        return Task.Run(() =>
        {
            if (!url.IsNullOrWhiteSpace())
            {
                var fullFilePath = Path.Combine(env.WebRootPath, url.Replace("/", "\\"));
                if (File.Exists(fullFilePath))
                {
                    File.Delete(fullFilePath);
                    return true;
                }
            }

            return false;
        });
    }

    /// <inheritdoc />
    public async Task<HandlerResult<Media.Models.Media>> SaveFile(IBrowserFile file,
        Media.Models.Media? existingMedia = null, string? folderName = null, bool overwrite = true)
    {
        var result = await CanUseFile(file);
        if (result.Success)
        {
            // Clear any messages
            result.Messages.Clear();

            try
            {
                var media = new Media.Models.Media();
                if (existingMedia != null)
                {
                    media = existingMedia;
                }

                if (media.Name.IsNullOrEmpty())
                {
                    media.Name = file.Name;
                }

                var relativePath = folderName.IsNullOrWhiteSpace()
                    ? Path.Combine(_settings.UploadFolderName ?? "media", media.Id.ToString())
                    : Path.Combine(_settings.UploadFolderName ?? "media", folderName);

                var dirToSave = Path.Combine(env.WebRootPath, relativePath);
                var di = new DirectoryInfo(dirToSave);
                if (!di.Exists)
                {
                    di.Create();
                }
                
                var globalSettingsRequest = await mediator.GetGlobalSettings();
                var filePath = Path.Combine(dirToSave, file.Name);
                await using (var stream = file.OpenReadStream(globalSettingsRequest.MaxUploadFileSizeInBytes))
                {
                    if (file.IsImage())
                    {
                        using var image = await Image.LoadAsync(stream);
                        image.OverMaxSizeCheck(globalSettingsRequest.MaxImageSizeInPixels);
                        await image.SaveAsync(filePath);
                        media.Width = image.Width;
                        media.Height = image.Height;
                    }
                    else
                    {
                        using var mstream = new MemoryStream();
                        await stream.CopyToAsync(mstream);
                        await File.WriteAllBytesAsync(filePath, mstream.ToArray());
                    }
                }

                media.Url = Path.Combine(relativePath, file.Name).Replace("\\", "/");
                media.FileSize = file.Size;
                media.MediaType = file.Name.ToFileType();
                result.Entity = media;
            }
            catch (Exception ex)
            {
                result.AddMessage(ex.Message, ResultMessageType.Error);
                result.Success = false;
            }

            return result;
        }

        return result;
    }

    /*public Task<Media.Models.Media> ToMedia(FileSaveResult fileSaveResult, Guid? id = null, Guid? parentId = null)
    {
        return Task.Run(() =>
        {
            var mediaItem = new Media.Models.Media
            {
                FileSize = fileSaveResult.FileSize ?? fileSaveResult.OriginalFile?.Size ?? 0,
                Name = fileSaveResult.Name ?? fileSaveResult.OriginalFile?.Name
            };
            if (id != null)
            {
                mediaItem.Id = id.Value;
            }
            if (parentId != null)
            {
                mediaItem.ParentId = parentId.Value;
            }
            mediaItem.Width = fileSaveResult.Width;
            mediaItem.Height = fileSaveResult.Height;
            mediaItem.MediaType = fileSaveResult.MediaType ?? fileSaveResult.OriginalFile?.Name.ToFileType() ?? MediaType.Unknown;
            mediaItem.DateCreated = DateTime.UtcNow;
            mediaItem.Url = fileSaveResult.SavedFileUrl;
            return mediaItem;
        });
    }*/
}
https://github.com/YodasMyDad/ZauberCMS/blob/main-mvc/ZauberCMS.Core/Providers/SmtpEmailProvider.cs
using System.Text;
using MailKit.Security;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using ZauberCMS.Core.Plugins.Interfaces;
using ZauberCMS.Core.Settings;
using MimeKit;
using MimeKit.Text;
using SmtpClient = MailKit.Net.Smtp.SmtpClient;

namespace ZauberCMS.Core.Providers;

public class SmtpEmailProvider(
    IOptions<ZauberSettings> settings,
    IServiceProvider serviceProvider)
    : IEmailProvider
{
        private readonly ZauberSettings _gabSettings = settings.Value;
        
        public async Task SendEmailWithTemplateAsync(string toEmail, string subject, List<string> paragraphs)
        {
            using var scope = serviceProvider.CreateScope();

            // send email
            using var smtp = new SmtpClient();

            var emailMessage = new MimeMessage();
            emailMessage.From.Add(MailboxAddress.Parse(_gabSettings.Email.SenderEmail));
            emailMessage.To.Add(MailboxAddress.Parse(toEmail));
            emailMessage.Subject = subject;
            emailMessage.Body = new TextPart(TextFormat.Html)
            {
                Text = HtmlBody(paragraphs, Template, string.Empty)
            };

            await smtp.ConnectAsync(_gabSettings.Email.Smtp.Host, _gabSettings.Email.Smtp.Port, SecureSocketOptions.StartTls);
            await smtp.AuthenticateAsync(_gabSettings.Email.Smtp.Username, _gabSettings.Email.Smtp.Password);
            await smtp.SendAsync(emailMessage);
            await smtp.DisconnectAsync(true);
        }

        public async Task SendEmailAsync(string toEmail, string subject, string message)
        {
            // send email
            using var smtp = new SmtpClient();

            var emailMessage = new MimeMessage();
            emailMessage.From.Add(MailboxAddress.Parse(_gabSettings.Email.SenderEmail));
            emailMessage.To.Add(MailboxAddress.Parse(toEmail));
            emailMessage.Subject = subject;
            emailMessage.Body = new TextPart(TextFormat.Html)
            {
                Text = message
            };

            await smtp.ConnectAsync(_gabSettings.Email.Smtp.Host, _gabSettings.Email.Smtp.Port, SecureSocketOptions.StartTls);
            await smtp.AuthenticateAsync(_gabSettings.Email.Smtp.Username, _gabSettings.Email.Smtp.Password);
            await smtp.SendAsync(emailMessage);
            await smtp.DisconnectAsync(true);
        }

        private static string FormatParagraphs(string text)
        {
            return $"<p style=\"Margin:0;-webkit-text-size-adjust:none;-ms-text-size-adjust:none;mso-line-height-rule:exactly;font-size:17px;font-family:helvetica, 'helvetica neue', arial, verdana, sans-serif;line-height:28px;color:#333333\">{text}</p>";
        }

        private static string HtmlBody(List<string> paragraphs, string emailTemplate, string logoUrl)
        {
            // Replace logo
            emailTemplate = emailTemplate.Replace("##LOGO##", logoUrl);

            var sb = new StringBuilder();
            foreach (var para in paragraphs)
            {
                sb.AppendLine(FormatParagraphs(para));
            }

            // Replace content
            emailTemplate = emailTemplate.Replace("##CONTENT##", sb.ToString());

            return emailTemplate;
        }

        public const string Template = """
                                  
                                          <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                                          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
                                  <html xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office"
                                        style="width:100%;font-family:helvetica, 'helvetica neue', arial, verdana, sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;padding:0;Margin:0;background-color:#F9FAFB;">
                                  <head>
                                      <meta charset="UTF-8">
                                      <meta content="width=device-width, initial-scale=1" name="viewport">
                                      <meta name="x-apple-disable-message-reformatting">
                                      <meta http-equiv="X-UA-Compatible" content="IE=edge">
                                      <meta content="telephone=no" name="format-detection">
                                      <title>Email</title>
                                      <!--[if (mso 16)]>
                                      <style type="text/css">
                                          a {
                                              text-decoration: none;
                                          }
                                      </style>
                                      <![endif]-->
                                      <!--[if gte mso 9]>
                                      <style>sup {
                                          font-size: 100% !important;
                                      }</style><![endif]-->
                                      <!--[if gte mso 9]>
                                      <xml>
                                          <o:OfficeDocumentSettings>
                                              <o:AllowPNG></o:AllowPNG>
                                              <o:PixelsPerInch>96</o:PixelsPerInch>
                                          </o:OfficeDocumentSettings>
                                      </xml>
                                      <![endif]-->
                                      <style type="text/css">
                                          #outlook a {
                                              padding: 0;
                                          }
                                  
                                          .ExternalClass {
                                              width: 100%;
                                          }
                                  
                                          .ExternalClass,
                                          .ExternalClass p,
                                          .ExternalClass span,
                                          .ExternalClass font,
                                          .ExternalClass td,
                                          .ExternalClass div {
                                              line-height: 100%;
                                          }
                                  
                                          .es-button {
                                              mso-style-priority: 100 !important;
                                              text-decoration: none !important;
                                          }
                                  
                                          a[x-apple-data-detectors] {
                                              color: inherit !important;
                                              text-decoration: none !important;
                                              font-size: inherit !important;
                                              font-family: inherit !important;
                                              font-weight: inherit !important;
                                              line-height: inherit !important;
                                          }
                                  
                                          .es-desk-hidden {
                                              display: none;
                                              float: left;
                                              overflow: hidden;
                                              width: 0;
                                              max-height: 0;
                                              line-height: 0;
                                              mso-hide: all;
                                          }
                                  
                                          @media only screen and (max-width: 600px) {
                                              p, ul li, ol li, a {
                                                  font-size: 16px !important;
                                                  line-height: 150% !important
                                              }
                                  
                                              h1 {
                                                  font-size: 30px !important;
                                                  text-align: center;
                                                  line-height: 120%
                                              }
                                  
                                              h2 {
                                                  font-size: 26px !important;
                                                  text-align: center;
                                                  line-height: 120%
                                              }
                                  
                                              h3 {
                                                  font-size: 20px !important;
                                                  text-align: center;
                                                  line-height: 120%
                                              }
                                  
                                              h1 a {
                                                  font-size: 30px !important
                                              }
                                  
                                              h2 a {
                                                  font-size: 26px !important
                                              }
                                  
                                              h3 a {
                                                  font-size: 20px !important
                                              }
                                  
                                              .es-menu td a {
                                                  font-size: 16px !important
                                              }
                                  
                                              .es-header-body p, .es-header-body ul li, .es-header-body ol li, .es-header-body a {
                                                  font-size: 16px !important
                                              }
                                  
                                              .es-footer-body p, .es-footer-body ul li, .es-footer-body ol li, .es-footer-body a {
                                                  font-size: 16px !important
                                              }
                                  
                                              .es-infoblock p, .es-infoblock ul li, .es-infoblock ol li, .es-infoblock a {
                                                  font-size: 12px !important
                                              }
                                  
                                              *[class="gmail-fix"] {
                                                  display: none !important
                                              }
                                  
                                              .es-m-txt-c, .es-m-txt-c h1, .es-m-txt-c h2, .es-m-txt-c h3 {
                                                  text-align: center !important
                                              }
                                  
                                              .es-m-txt-r, .es-m-txt-r h1, .es-m-txt-r h2, .es-m-txt-r h3 {
                                                  text-align: right !important
                                              }
                                  
                                              .es-m-txt-l, .es-m-txt-l h1, .es-m-txt-l h2, .es-m-txt-l h3 {
                                                  text-align: left !important
                                              }
                                  
                                              .es-m-txt-r img, .es-m-txt-c img, .es-m-txt-l img {
                                                  display: inline !important
                                              }
                                  
                                              .es-button-border {
                                                  display: block !important
                                              }
                                  
                                              .es-btn-fw {
                                                  border-width: 10px 0px !important;
                                                  text-align: center !important
                                              }
                                  
                                              .es-adaptive table, .es-btn-fw, .es-btn-fw-brdr, .es-left, .es-right {
                                                  width: 100% !important
                                              }
                                  
                                              .es-content table, .es-header table, .es-footer table, .es-content, .es-footer, .es-header {
                                                  width: 100% !important;
                                                  max-width: 600px !important
                                              }
                                  
                                              .es-adapt-td {
                                                  display: block !important;
                                                  width: 100% !important
                                              }
                                  
                                              .adapt-img {
                                                  width: 100% !important;
                                                  height: auto !important
                                              }
                                  
                                              .es-m-p0 {
                                                  padding: 0px !important
                                              }
                                  
                                              .es-m-p0r {
                                                  padding-right: 0px !important
                                              }
                                  
                                              .es-m-p0l {
                                                  padding-left: 0px !important
                                              }
                                  
                                              .es-m-p0t {
                                                  padding-top: 0px !important
                                              }
                                  
                                              .es-m-p0b {
                                                  padding-bottom: 0 !important
                                              }
                                  
                                              .es-m-p20b {
                                                  padding-bottom: 20px !important
                                              }
                                  
                                              .es-mobile-hidden, .es-hidden {
                                                  display: none !important
                                              }
                                  
                                              tr.es-desk-hidden, td.es-desk-hidden, table.es-desk-hidden {
                                                  width: auto !important;
                                                  overflow: visible !important;
                                                  float: none !important;
                                                  max-height: inherit !important;
                                                  line-height: inherit !important
                                              }
                                  
                                              tr.es-desk-hidden {
                                                  display: table-row !important
                                              }
                                  
                                              table.es-desk-hidden {
                                                  display: table !important
                                              }
                                  
                                              td.es-desk-menu-hidden {
                                                  display: table-cell !important
                                              }
                                  
                                              .es-menu td {
                                                  width: 1% !important
                                              }
                                  
                                              table.es-table-not-adapt, .esd-block-html table {
                                                  width: auto !important
                                              }
                                  
                                              table.es-social {
                                                  display: inline-block !important
                                              }
                                  
                                              table.es-social td {
                                                  display: inline-block !important
                                              }
                                  
                                              a.es-button, button.es-button {
                                                  font-size: 20px !important;
                                                  display: block !important;
                                                  border-width: 10px 0px 10px 0px !important
                                              }
                                          }
                                      </style>
                                  </head>
                                  <body style="width:100%;font-family:helvetica, 'helvetica neue', arial, verdana, sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;padding-top:20px;padding-bottom:20px;padding-left:0;padding-right:0;Margin:0"
                                        style="background-color:#F9FAFB">
                                  <div class="es-wrapper-color" style="background-color:#F9FAFB">
                                      <!--[if gte mso 9]>
                                      <v:background xmlns:v="urn:schemas-microsoft-com:vml" fill="t">
                                          <v:fill type="tile" color="#F9FAFB" origin="0.5, 0" position="0.5,0"></v:fill>
                                      </v:background>
                                      <![endif]-->
                                      <table class="es-wrapper" width="100%" cellspacing="0" cellpadding="0"
                                             style="mso-table-lspace:0pt;mso-table-rspace:0pt;border-collapse:collapse;border-spacing:0px;padding:0;Margin:0;width:100%;height:100%;background-repeat:repeat;background-position:center top">
                                          <tr style="border-collapse:collapse">
                                              <td valign="top" style="padding:0;Margin:0">
                                                  <table class="es-content" cellspacing="0" cellpadding="0" align="center"
                                                         style="mso-table-lspace:0pt;mso-table-rspace:0pt;border-collapse:collapse;border-spacing:0px;table-layout:fixed !important;width:100%">
                                                      <tr style="border-collapse:collapse">
                                                          <td align="center" style="padding:0;Margin:0">
                                                              <table class="es-content-body" cellspacing="0" cellpadding="0" bgcolor="#ffffff"
                                                                     align="center"
                                                                     style="mso-table-lspace:0pt;mso-table-rspace:0pt;border-collapse:collapse;border-spacing:0px;background-color:#FFFFFF;border-top:1px solid #EEECEC;border-right:1px solid #EEECEC;border-left:1px solid #EEECEC;width:600px;border-bottom:1px solid #EEECEC">
                                                                  <tr style="border-collapse:collapse">
                                                                      <td align="left" style="padding:20px;Margin:0">
                                                                          <table width="100%" cellspacing="0" cellpadding="0"
                                                                                 style="mso-table-lspace:0pt;mso-table-rspace:0pt;border-collapse:collapse;border-spacing:0px">
                                                                              <tr style="border-collapse:collapse">
                                                                                  <td valign="top" align="center" style="padding:0;Margin:0;width:558px">
                                                                                      <table width="100%" cellspacing="0" cellpadding="0"
                                                                                             role="presentation"
                                                                                             style="mso-table-lspace:0pt;mso-table-rspace:0pt;border-collapse:collapse;border-spacing:0px">
                                                                                          <tr style="border-collapse:collapse">
                                                                                              <td align="center"
                                                                                                  style="padding: 0; Margin: 0; padding-bottom: 10px; font-size: 0px">
                                                                                                  <img class="adapt-img" src="##LOGO##" alt=alt
                                                                                                       style="display:block;border:0;outline:none;text-decoration:none;-ms-interpolation-mode:bicubic"
                                                                                                       width="60"/>
                                                                                              </td>
                                                                                          </tr>
                                                                                          <tr style="border-collapse:collapse">
                                                                                              <td align="center" style="padding:0;Margin:0">
                                                                                                  ##CONTENT##
                                                                                              </td>
                                                                                          </tr>
                                                                                      </table>
                                                                                  </td>
                                                                              </tr>
                                                                          </table>
                                                                      </td>
                                                                  </tr>
                                                              </table>
                                                          </td>
                                                      </tr>
                                                  </table>
                                              </td>
                                          </tr>
                                      </table>
                                  </div>
                                  </body>
                                  </html>

                                  """;
    }