Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ public interface IEmailAccountService
Task<EmailAccount> GetEmailAccountById(string emailAccountId);

/// <summary>
/// Gets all email accounts
/// Gets all email accounts, optionally filtered by store.
/// </summary>
/// <param name="storeId">Store identifier; pass empty string to return all accounts</param>
/// <returns>Email accounts list</returns>
Task<IList<EmailAccount>> GetAllEmailAccounts();
Task<IList<EmailAccount>> GetAllEmailAccounts(string storeId = "");
}
Original file line number Diff line number Diff line change
Expand Up @@ -134,16 +134,22 @@ public virtual async Task<EmailAccount> GetEmailAccountById(string emailAccountI
}

/// <summary>
/// Gets all email accounts
/// Gets all email accounts, optionally filtered by store.
/// </summary>
/// <param name="storeId">Store identifier; pass empty string to return all accounts</param>
/// <returns>Email accounts list</returns>
public virtual async Task<IList<EmailAccount>> GetAllEmailAccounts()
public virtual async Task<IList<EmailAccount>> GetAllEmailAccounts(string storeId = "")
{
return await _cacheBase.GetAsync(CacheKey.EMAILACCOUNT_ALL_KEY, async () =>
var accounts = await _cacheBase.GetAsync(CacheKey.EMAILACCOUNT_ALL_KEY, async () =>
{
var query = from ea in _emailAccountRepository.Table
select ea;
return await Task.FromResult(query.ToList());
});

if (string.IsNullOrEmpty(storeId))
return accounts;

return accounts.Where(ea => ea.StoreId == storeId).ToList();
}
Comment on lines +150 to 154
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,9 @@ protected virtual async Task<EmailAccount> GetEmailAccountOfMessageTemplate(Mess
string languageId)
{
var emailAccounId = messageTemplate.GetTranslation(mt => mt.EmailAccountId, languageId);
var emailAccount = (await _emailAccountService.GetEmailAccountById(emailAccounId) ??
await _emailAccountService.GetEmailAccountById(_emailAccountSettings
.DefaultEmailAccountId)) ??
(await _emailAccountService.GetAllEmailAccounts()).FirstOrDefault();
var emailAccount = await _emailAccountService.GetEmailAccountById(emailAccounId);
emailAccount ??= await _emailAccountService.GetEmailAccountById(_emailAccountSettings.DefaultEmailAccountId);
Comment thread
KrzysztofPajak marked this conversation as resolved.
emailAccount ??= (await _emailAccountService.GetAllEmailAccounts()).FirstOrDefault();
return emailAccount;
}

Expand Down
6 changes: 6 additions & 0 deletions src/Core/Grand.Domain/Messages/EmailAccount.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,10 @@ public class EmailAccount : BaseEntity
/// Provides a way of specifying the SSL and/or TLS encryption that should be used for a connection
/// </summary>
public int SecureSocketOptionsId { get; set; }

/// <summary>
/// Gets or sets the store identifier this email account belongs to.
/// An empty string means this is a global/shared email account.
/// </summary>
public string StoreId { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public void Init()
new List<Language> { new() { Name = "English" }, new() { Name = "Polish" } } as IList<Language>));

_emailAccountServiceMock = new Mock<IEmailAccountService>();
_emailAccountServiceMock.Setup(x => x.GetAllEmailAccounts())
_emailAccountServiceMock.Setup(x => x.GetAllEmailAccounts(It.IsAny<string>()))
.Returns(Task.FromResult(
new List<EmailAccount> { new() { Email = "sdfsdf@mail.com" } } as IList<EmailAccount>));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@
<admin-select asp-for="SecureSocketOptionsId" asp-items="EnumTranslationService.ToSelectList((SecureSocketOptions)Model.SecureSocketOptionsId)"/>
</div>
</div>
<div class="form-group">
<admin-label asp-for="StoreId"/>
<div class="col-md-9 col-sm-9">
<select asp-for="StoreId" asp-items="Model.AvailableStores" class="form-control"></select>
</div>
Comment thread
KrzysztofPajak marked this conversation as resolved.
</div>
@if (!string.IsNullOrEmpty(Model.Id))
{
<div class="form-group">
Expand Down
8 changes: 5 additions & 3 deletions src/Web/Grand.Web.Admin/Controllers/EmailAccountController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ public async Task<IActionResult> MarkAsDefaultEmail(string id)
}

[PermissionAuthorizeAction(PermissionActionName.Create)]
public IActionResult Create()
public async Task<IActionResult> Create()
{
var model = _emailAccountViewModelService.PrepareEmailAccountModel();
var model = await _emailAccountViewModelService.PrepareEmailAccountModel();
return View(model);
}

Expand Down Expand Up @@ -108,7 +108,9 @@ public async Task<IActionResult> Edit(string id)
//No email account found with the specified id
return RedirectToAction("List");

return View(emailAccount.ToModel());
var model = emailAccount.ToModel();
await _emailAccountViewModelService.PrepareAvailableStores(model);
return View(model);
}

[HttpPost]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ namespace Grand.Web.AdminShared.Interfaces;

public interface IEmailAccountViewModelService
{
EmailAccountModel PrepareEmailAccountModel();
Task<EmailAccountModel> PrepareEmailAccountModel();
Task PrepareAvailableStores(EmailAccountModel model);
Task<EmailAccount> InsertEmailAccountModel(EmailAccountModel model);
Task<EmailAccount> UpdateEmailAccountModel(EmailAccount emailAccount, EmailAccountModel model);
Task SendTestEmail(EmailAccount emailAccount, EmailAccountModel model);
Expand Down
3 changes: 2 additions & 1 deletion src/Web/Grand.Web.AdminShared/Mapper/EmailAccountProfile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ public EmailAccountProfile()
CreateMap<EmailAccount, EmailAccountModel>()
.ForMember(dest => dest.Password, mo => mo.Ignore())
.ForMember(dest => dest.IsDefaultEmailAccount, mo => mo.Ignore())
.ForMember(dest => dest.SendTestEmailTo, mo => mo.Ignore());
.ForMember(dest => dest.SendTestEmailTo, mo => mo.Ignore())
.ForMember(dest => dest.AvailableStores, mo => mo.Ignore());

CreateMap<EmailAccountModel, EmailAccount>()
.ForMember(dest => dest.Id, mo => mo.Ignore())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Grand.Infrastructure.ModelBinding;
using Grand.Infrastructure.Models;
using Microsoft.AspNetCore.Mvc.Rendering;

namespace Grand.Web.AdminShared.Models.Messages;

Expand Down Expand Up @@ -34,4 +35,9 @@ public class EmailAccountModel : BaseEntityModel

[GrandResourceDisplayName("Admin.Configuration.EmailAccounts.Fields.SendTestEmailTo")]
public string SendTestEmailTo { get; set; }

[GrandResourceDisplayName("Admin.Configuration.EmailAccounts.Fields.Store")]
public string StoreId { get; set; }

public IList<SelectListItem> AvailableStores { get; set; } = new List<SelectListItem>();
}
Original file line number Diff line number Diff line change
@@ -1,31 +1,45 @@
using Grand.Business.Core.Interfaces.Messages;
using Grand.Business.Core.Interfaces.Common.Localization;
using Grand.Business.Core.Interfaces.Common.Stores;
using Grand.Business.Core.Interfaces.Messages;
using Grand.Domain.Messages;
using Grand.Web.AdminShared.Extensions.Mapping;
using Grand.Web.AdminShared.Interfaces;
using Grand.Web.AdminShared.Models.Messages;
using Microsoft.AspNetCore.Mvc.Rendering;

namespace Grand.Web.AdminShared.Services;

public class EmailAccountViewModelService : IEmailAccountViewModelService
{
private readonly IEmailAccountService _emailAccountService;
private readonly IEmailSender _emailSender;
private readonly IStoreService _storeService;
private readonly ITranslationService _translationService;

public EmailAccountViewModelService(IEmailAccountService emailAccountService, IEmailSender emailSender)
public EmailAccountViewModelService(IEmailAccountService emailAccountService, IEmailSender emailSender,
IStoreService storeService, ITranslationService translationService)
{
_emailAccountService = emailAccountService;
_emailSender = emailSender;
_storeService = storeService;
_translationService = translationService;
}

public virtual EmailAccountModel PrepareEmailAccountModel()
public virtual async Task<EmailAccountModel> PrepareEmailAccountModel()
{
var model = new EmailAccountModel {
//default values
Port = 25
};
await PopulateAvailableStores(model);
return model;
}

public virtual async Task PrepareAvailableStores(EmailAccountModel model)
{
await PopulateAvailableStores(model);
}

public virtual async Task<EmailAccount> InsertEmailAccountModel(EmailAccountModel model)
{
var emailAccount = model.ToEntity();
Expand All @@ -52,4 +66,17 @@ public virtual async Task SendTestEmail(EmailAccount emailAccount, EmailAccountM
await _emailSender.SendEmail(emailAccount, subject, body, emailAccount.Email, emailAccount.DisplayName,
model.SendTestEmailTo, null);
}

private async Task PopulateAvailableStores(EmailAccountModel model)
{
model.AvailableStores.Add(new SelectListItem {
Value = "",
Text = _translationService.GetResource("Admin.Settings.StoreScope.AllStores")
});
foreach (var store in await _storeService.GetAllStores())
model.AvailableStores.Add(new SelectListItem {
Value = store.Id,
Text = store.Name
});
Comment on lines +70 to +80
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
@model EmailAccountModel
@{
//page title
ViewBag.Title = Loc["Admin.Configuration.EmailAccounts.AddNew"];
}
<form asp-area="@Constants.AreaStore" asp-controller="EmailAccount" asp-action="Create" method="post">

<div class="row">
<div class="col-md-12">
<div class="x_panel light form-fit">
<div class="x_title">
<div class="caption">
<i class="fa fa-envelope"></i>
@Loc["Admin.Configuration.EmailAccounts.AddNew"]
<small>
<i class="fa fa-arrow-circle-left"></i> @Html.ActionLink(Loc["Admin.Configuration.EmailAccounts.BackToList"], "List")
</small>
</div>
<div class="actions">
<button class="btn btn-success" type="submit" name="save">
<i class="fa fa-check"></i> @Loc["Admin.Common.Save"]
</button>
<button class="btn btn-success" type="submit" name="save-continue">
<i class="fa fa-check-circle"></i> @Loc["Admin.Common.SaveContinue"]
</button>
</div>
</div>
<div class="x_content form">
<partial name="Partials/CreateOrUpdate" model="Model"/>
</div>
</div>
</div>
</div>
</form>
40 changes: 40 additions & 0 deletions src/Web/Grand.Web.Store/Areas/Store/Views/EmailAccount/Edit.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
@model EmailAccountModel
@{
//page title
ViewBag.Title = Loc["Admin.Configuration.EmailAccounts.EditEmailAccountDetails"];
}
<form asp-area="@Constants.AreaStore" asp-controller="EmailAccount" asp-action="Edit" method="post">

<div class="row">
<div class="col-md-12">
<div class="x_panel light form-fit">
<div class="x_title">
<div class="caption">
<i class="fa fa-envelope"></i>
@Loc["Admin.Configuration.EmailAccounts.EditEmailAccountDetails"]
<small>
<i class="fa fa-arrow-circle-left"></i> @Html.ActionLink(Loc["Admin.Configuration.EmailAccounts.BackToList"], "List")
</small>
</div>
<div class="actions">
<div class="btn-group btn-group-devided util-btn-margin-bottom-5">
<button class="btn btn-success" type="submit" name="save">
<i class="fa fa-check"></i> @Loc["Admin.Common.Save"]
</button>
<button class="btn btn-success" type="submit" name="save-continue">
<i class="fa fa-check-circle"></i> @Loc["Admin.Common.SaveContinue"]
</button>
<span id="emailAccount-delete" class="btn red">
<i class="fa fa-trash-o"></i><span class="d-none d-sm-inline"> @Loc["Admin.Common.Delete"]</span>
</span>
</div>
</div>
</div>
<div class="x_content form">
<partial name="Partials/CreateOrUpdate" model="Model"/>
</div>
</div>
</div>
</div>
</form>
<admin-delete-confirmation button-id="emailAccount-delete"/>
71 changes: 71 additions & 0 deletions src/Web/Grand.Web.Store/Areas/Store/Views/EmailAccount/List.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
@{
//page title
ViewBag.Title = Loc["Admin.Configuration.EmailAccounts"];
}

<div class="row">
<div class="col-md-12">
<div class="x_panel light form-fit">
<div class="x_title">
<div class="caption">
<i class="fa fa-envelope"></i>
@Loc["Admin.Configuration.EmailAccounts"]
</div>
<div class="actions">
<a href="@Url.Action("Create", new { area = Constants.AreaStore })" class="btn green">
<i class="fa fa-plus"></i><span class="d-none d-sm-inline"> @Loc["Admin.Common.AddNew"] </span>
</a>
</div>
</div>
<div class="x_content form">
<div id="email-accounts-grid"></div>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function () {
$("#email-accounts-grid").kendoGrid({
dataSource: {
transport: {
read: {
url: "@Html.Raw(Url.Action("List", "EmailAccount", new { area = Constants.AreaStore }))",
type: "POST",
dataType: "json",
data: addAntiForgeryToken
}
},
schema: {
data: "Data",
total: "Total",
errors: "Errors"
},
error: function(e) {
display_kendoui_grid_error(e);
this.cancelChanges();
},
serverPaging: true,
serverFiltering: true,
serverSorting: true
},
pageable: {
refresh: true,
numeric: false,
previousNext: false,
info: false
},
scrollable: false,
columns: [{
field: "Email",
title: "@Loc["Admin.Configuration.EmailAccounts.Fields.Email"]",
template: '<a class="k-link" href="Edit/#=Id#">#=Email#</a>'
Comment thread
KrzysztofPajak marked this conversation as resolved.
}, {
field: "DisplayName",
title: "@Loc["Admin.Configuration.EmailAccounts.Fields.DisplayName"]"
}, {
field: "Host",
title: "@Loc["Admin.Configuration.EmailAccounts.Fields.Host"]"
}]
});
});
</script>
Loading
Loading