Skip to content

Commit

Permalink
Merge pull request #4 from Lombiq/issue/OSOE-1011
Browse files Browse the repository at this point in the history
OSOE-1011: Add Lombiq.EmailClient to OSOCE
  • Loading branch information
barthamark authored Feb 28, 2025
2 parents 960a78e + ecb17fd commit bf47c53
Show file tree
Hide file tree
Showing 23 changed files with 568 additions and 13 deletions.
21 changes: 10 additions & 11 deletions .github/workflows/validate-nuget-publish.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
name: Validate NuGet Publish

# Temporarily commented out until making it a fully compliant OSS module.
## on:
## pull_request:
## push:
## branches:
## - dev
##
## jobs:
## validate-nuget-publish:
## name: Validate NuGet Publish
## uses: Lombiq/GitHub-Actions/.github/workflows/validate-nuget-publish.yml@dev
on:
pull_request:
push:
branches:
- dev

jobs:
validate-nuget-publish:
name: Validate NuGet Publish
uses: Lombiq/GitHub-Actions/.github/workflows/validate-nuget-publish.yml@dev
30 changes: 30 additions & 0 deletions Lombiq.EmailClient.Samples/Controllers/EmailSyncController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Lombiq.EmailClient.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using OrchardCore.BackgroundJobs;
using System.Threading.Tasks;

namespace Lombiq.EmailClient.Samples.Controllers;

// If the Email Sync feature is enabled, a background task automatically fetches emails from the configured IMAP server.
// An email sync event handler can be implemented to process these emails (you'll see it shortly). You can trigger the
// email sync manually by using the IEmailSyncService service. This controller demonstrates how to do that.
public class EmailSyncController : Controller
{
public async Task<ActionResult> Index()
{
// The email sync operation might take a while so it's recommended to run it after the request.
await HttpBackgroundJob.ExecuteAfterEndOfRequestAsync(nameof(EmailSyncController), scope =>
{
var service = scope.ServiceProvider.GetService<IEmailSyncService>();

// This method fetches the next batch of emails from the IMAP server. When it syncs an email it calls the
// EmailSyncedAsync method of the registered email sync event handlers.
return service.SyncNextEmailsAsync();
});

return Ok();
}
}

// NEXT STATION: Services/SampleEmailSyncEventHandler.cs
31 changes: 31 additions & 0 deletions Lombiq.EmailClient.Samples/Controllers/ImapController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Lombiq.EmailClient.Models;
using Lombiq.EmailClient.Services;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;

namespace Lombiq.EmailClient.Samples.Controllers;

// This controller demonstrates how to fetch emails from an IMAP server.
public class ImapController : Controller
{
// The IEmailClient service is used to fetch emails. Check the interface to see what you can do with it.
private readonly IEmailClient _emailClient;

public ImapController(IEmailClient emailClient) =>
_emailClient = emailClient;

public async Task<ActionResult> Index()
{
// You can filter emails by subject. For testing purposes we filter by "important".
var parameters = new EmailFilterParameters
{
Subject = "important",
};

// Fetch emails from the IMAP server.
var emails = await _emailClient.GetEmailsAsync(parameters);

// NEXT STATION: Go to Views/Imap/Index.cshtml.
return View(emails);
}
}
37 changes: 37 additions & 0 deletions Lombiq.EmailClient.Samples/Lombiq.EmailClient.Samples.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
</PropertyGroup>

<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

<PropertyGroup>
<Title>Lombiq Email Client for Orchard Core - Samples</Title>
<Authors>Lombiq Technologies</Authors>
<Copyright>Copyright © 2025, Lombiq Technologies Ltd.</Copyright>
<Description>Lombiq Email Client for Orchard Core - Samples: Sample content that demonstrates the features of Lombiq Email Client module for Orchard Core.</Description>
<PackageIcon>NuGetIcon.png</PackageIcon>
<PackageTags>OrchardCore;Lombiq;AspNetCore;Emails;IMAP</PackageTags>
<RepositoryUrl>https://github.com/Lombiq/Orchard-Email-Client</RepositoryUrl>
<PackageProjectUrl>https://github.com/Lombiq/Orchard-Email-Client/blob/dev/Lombiq.EmailClient.Samples/Readme.md</PackageProjectUrl>
<PackageLicenseExpression>BSD-3-Clause</PackageLicenseExpression>
</PropertyGroup>

<ItemGroup>
<None Include="Readme.md" />
<None Include="NuGetIcon.png" Pack="true" PackagePath="" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="OrchardCore.Module.Targets" Version="2.1.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Lombiq.EmailClient\Lombiq.EmailClient.csproj" />
</ItemGroup>

</Project>
15 changes: 15 additions & 0 deletions Lombiq.EmailClient.Samples/Manifest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using OrchardCore.Modules.Manifest;
using static Lombiq.EmailClient.Constants.FeatureIds;

[assembly: Module(
Name = "Lombiq Email Client - Samples",
Author = "Lombiq Technologies",
Website = "https://github.com/Lombiq/Orchard-Email-Client",
Version = "0.0.1",
Description = "Samples for Lombiq Email Client.",
Category = "Email",
Dependencies = [
Imap,
EmailSync
]
)]
Binary file added Lombiq.EmailClient.Samples/NuGetIcon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions Lombiq.EmailClient.Samples/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Lombiq Email Client for Orchard Core - Samples

## About

Example Orchard Core module that makes use of Lombiq Email Client for Orchard Core.

For general details about and usage instructions see the [root Readme](../Readme.md).

Do you want to quickly try out this project and see it in action? Check it out in our [Open-Source Orchard Core Extensions](https://github.com/Lombiq/Open-Source-Orchard-Core-Extensions) full Orchard Core solution and also see our other useful Orchard Core-related open-source projects!

## Training sections

You can start with any of these sections, they demonstrate different approaches that best fit different use-cases.

- [Fetching emails from IMAP](../Lombiq.EmailClient.Samples/Controllers/ImapController.cs)
- [Email sync](../Lombiq.EmailClient.Samples/Controllers/EmailSyncController.cs)

## Contributing and support

Bug reports, feature requests, comments, questions, code contributions and love letters are warmly welcome. You can send them to us via GitHub issues and pull requests. Please adhere to our [open-source guidelines](https://lombiq.com/open-source-guidelines) while doing so.

This project is developed by [Lombiq Technologies](https://lombiq.com/). Commercial-grade support is available through Lombiq.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "Lombiq.EmailClient.Samples",
"displayName": "Lombiq Email Client - Samples",
"description": "Sample feature utilizing the Lombiq Email Client feature.",
"author": "Lombiq Technologies",
"website": "https://github.com/Lombiq/Orchard-Chart.js",
"version": "1.0",
"issetuprecipe": false,
"categories": [
"training"
],
"tags": [
"developer",
"training",
"sample"
],
"steps": [
{
"name": "feature",
"enable": [
"Lombiq.EmailClient.Samples"
]
},
{
"name": "Settings",
"ImapSettings": {
"Host": "127.0.0.1",
"Port": 143
}
}
]
}
30 changes: 30 additions & 0 deletions Lombiq.EmailClient.Samples/Services/SampleEmailSyncEventHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Lombiq.EmailClient.Models;
using Lombiq.EmailClient.Services;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

namespace Lombiq.EmailClient.Samples.Services;

// This is a sample implementation of an email sync event handler. It's called when the Email Sync feature is enabled
// and the background task syncs the next batch of emails (i.e., emails that have been received since the last sync).
public class SampleEmailSyncEventHandler : IEmailSyncEventHandler
{
private readonly ILogger<SampleEmailSyncEventHandler> _logger;

public SampleEmailSyncEventHandler(ILogger<SampleEmailSyncEventHandler> logger) =>
_logger = logger;

public Task EmailSyncedAsync(EmailMessage emailMessage)
{
// For testing purposes we just log the email's subject and sender. However, in a real-world scenario you'd most
// likely want to create a content item that stores the email's data.
_logger.LogDebug(
"Email synced (subject: {Subject}, sender: {Sender})",
emailMessage.Header.Subject,
emailMessage.Header.Sender.Address);

return Task.CompletedTask;
}
}

// END OF TRAINING SECTION: Email sync
12 changes: 12 additions & 0 deletions Lombiq.EmailClient.Samples/Startup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Lombiq.EmailClient.Samples.Services;
using Lombiq.EmailClient.Services;
using Microsoft.Extensions.DependencyInjection;
using OrchardCore.Modules;

namespace Lombiq.EmailClient.Samples;

public class Startup : StartupBase
{
public override void ConfigureServices(IServiceCollection services) =>
services.AddScoped<IEmailSyncEventHandler, SampleEmailSyncEventHandler>();
}
27 changes: 27 additions & 0 deletions Lombiq.EmailClient.Samples/Views/Imap/Index.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
@using System.Globalization
@model IEnumerable<Lombiq.EmailClient.Models.EmailMessage>

<h1>@T["Important email messages"]</h1>
<small>@T["Email messages where the subject contains the \"important\" word."]</small>

<ul>
@* The EmailMessage object has a lot of information about the email, but we're only displaying a few fields here. *@
@foreach (var emailMessage in Model)
{
<li class="email">
<div class="email__sender"><strong>@T["Sender:"]</strong> @emailMessage.Header.Sender.Address</div>
<div class="email__sent"><strong>@T["Sent UTC:"]</strong> @emailMessage.Header.SentDateUtc?.ToString("o", CultureInfo.InvariantCulture)</div>
<div class="email__subject"><strong>@T["Subject:"]</strong> @emailMessage.Header.Subject</div>
<div class="email__body">
<strong>@T["Body:"]</strong>
<code>
@Html.Raw(emailMessage.Content.Body.Body)
</code>
</div>
</li>
}
</ul>

@* END OF TRAINING SECTION: Fetching emails from IMAP server. @*
@* NEXT STATION: Controllers/EmailSyncController.cs *@
9 changes: 9 additions & 0 deletions Lombiq.EmailClient.Samples/Views/_ViewImports.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@inherits OrchardCore.DisplayManagement.Razor.RazorPage<TModel>

@using OrchardCore
@using OrchardCore.ContentManagement.Display.ViewModels
@using OrchardCore.DisplayManagement.Views

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, OrchardCore.DisplayManagement
@addTagHelper *, OrchardCore.ResourceManagement
24 changes: 24 additions & 0 deletions Lombiq.EmailClient.Tests.UI/Constants/TestEmailPaths.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;
using System.IO;

namespace Lombiq.EmailClient.Tests.UI.Constants;

public static class TestEmailPaths
{
private static readonly string BasePath = Path.Combine(Environment.CurrentDirectory, "TestEmails");

public static readonly string SampleImportant1FileName = "sample_important_1.eml";
public static readonly string SampleImportant2FileName = "sample_important_2.eml";
public static readonly string SampleNotImportantFileName = "sample_not_important.eml";

public static readonly string SampleImportant1Path = Path.Combine(BasePath, SampleImportant1FileName);
public static readonly string SampleImportant2Path = Path.Combine(BasePath, SampleImportant2FileName);
public static readonly string SampleNotImportantPath = Path.Combine(BasePath, SampleNotImportantFileName);

public static readonly string[] SampleEmailPaths =
[
SampleImportant1Path,
SampleImportant2Path,
SampleNotImportantPath
];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Lombiq.Tests.UI.Extensions;
using Lombiq.Tests.UI.Services;
using OpenQA.Selenium;
using Shouldly;
using System.Threading.Tasks;

namespace Lombiq.EmailClient.Tests.UI.Extensions;

public static class TestCaseUITestContextExtensions
{
public static async Task TestImapEmailFetchingAsync(this UITestContext context)
{
await context.InitSampleEmailsAsync();
await context.SignInDirectlyAsync();
await context.ExecuteEmailClientSampleRecipeDirectlyAsync();
await context.SetImapPortOnAdminAsync();
await context.GoToImapTestAsync();

context.GetAll(By.ClassName("email")).Count.ShouldBe(2);
context.Exists(By.XPath($"//div[contains(text(), '[email protected]')]"));
context.Exists(By.XPath($"//div[contains(text(), '2025-02-24T12:35:19')]"));
context.Exists(By.XPath($"//div[contains(text(), 'Very important sample')]"));
context.Exists(By.XPath($"//code[contains(text(), 'This email is sent for testing purposes.')]"));
}
}
31 changes: 31 additions & 0 deletions Lombiq.EmailClient.Tests.UI/Extensions/UITestContextExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Lombiq.EmailClient.Samples.Controllers;
using Lombiq.EmailClient.Tests.UI.Constants;
using Lombiq.HelpfulLibraries.OrchardCore.Mvc;
using Lombiq.Tests.UI.Extensions;
using Lombiq.Tests.UI.Services;
using OpenQA.Selenium;
using System;
using System.Threading.Tasks;

namespace Lombiq.EmailClient.Tests.UI.Extensions;

public static class UITestContextExtensions
{
public static Task ExecuteEmailClientSampleRecipeDirectlyAsync(this UITestContext context) =>
context.ExecuteRecipeDirectlyAsync("Lombiq.EmailClient.Samples");

public static Task InitSampleEmailsAsync(this UITestContext context) =>
context.CreateAndUseLocalSmtpClientToSendEmailsFromFilesAsync(TestEmailPaths.SampleEmailPaths);

public static Task GoToImapTestAsync(this UITestContext context) =>
context.GoToAsync<ImapController>(controller => controller.Index());

public static async Task SetImapPortOnAdminAsync(this UITestContext context)
{
await context.GoToAdminRelativeUrlAsync("/Settings/ImapSettings");
await context.FillInWithRetriesAsync(
By.Id("ISite_ImapSettings_Port"),
context.SmtpServiceRunningContext.ImapPort.ToTechnicalString());
await context.ClickReliablyOnSubmitAsync();
}
}
Loading

0 comments on commit bf47c53

Please sign in to comment.