Skip to content

Commit

Permalink
edit custom email templates
Browse files Browse the repository at this point in the history
  • Loading branch information
Eliran Turgeman authored and Eliran Turgeman committed Dec 14, 2024
1 parent cd7fdb4 commit c3112f2
Show file tree
Hide file tree
Showing 9 changed files with 260 additions and 15 deletions.
1 change: 1 addition & 0 deletions EmailCollector.Api/EmailCollector.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<PackageReference Include="Blazorise.Charts" Version="1.6.1" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.6.1" />
<PackageReference Include="DnsClient" Version="1.8.0" />
<PackageReference Include="HtmlSanitizer" Version="8.1.870" />
<PackageReference Include="Microsoft.AspNetCore.ApiAuthorization.IdentityServer" Version="7.0.20" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="8.0.8" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using EmailCollector.Api.Services.CustomEmailTemplates;
using EmailCollector.Domain.Entities;
using EmailCollector.Domain.Enums;
using Ganss.Xss;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
Expand Down Expand Up @@ -49,7 +50,10 @@ public async Task<IActionResult> OnPostAsync()
{
return Page();
}


var sanitizer = new HtmlSanitizer();
CustomEmailTemplate.TemplateBody = sanitizer.Sanitize(CustomEmailTemplate.TemplateBody);

var templateToSave = new CustomEmailTemplateDto
{
Id = Guid.NewGuid(),
Expand Down
49 changes: 49 additions & 0 deletions EmailCollector.Api/Pages/CustomEmailTemplates/Edit.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
@page "{id:guid}"
@model EmailCollector.Api.Pages.CustomEmailTemplates.Edit

@{
ViewData["Title"] = "Edit Custom Email Template";
}

<h1>Edit Custom Email Template</h1>

<form method="post">
<input type="hidden" asp-for="CustomEmailTemplate.FormId" />

<div class="form-group">
<label asp-for="CustomEmailTemplate.FormId" class="control-label">Form</label>
<select asp-for="CustomEmailTemplate.FormId" class="form-control" asp-items="Model.FormOptions" disabled>
<option value="">-- Select Form --</option>
</select>
<span asp-validation-for="CustomEmailTemplate.FormId" class="text-danger"></span>
<small class="form-text text-muted">Form cannot be changed.</small>
</div>
<div class="form-group">
<label asp-for="CustomEmailTemplate.Event" class="control-label">Event</label>
<select asp-for="CustomEmailTemplate.Event" class="form-control" asp-items="Model.EventOptions">
<option value="">-- Select Event --</option>
</select>
<span asp-validation-for="CustomEmailTemplate.Event" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CustomEmailTemplate.TemplateSubject" class="control-label">Subject</label>
<input asp-for="CustomEmailTemplate.TemplateSubject" class="form-control" />
<span asp-validation-for="CustomEmailTemplate.TemplateSubject" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CustomEmailTemplate.TemplateBody" class="control-label">HTML Content</label>
<textarea asp-for="CustomEmailTemplate.TemplateBody" class="form-control" rows="10"></textarea>
<span asp-validation-for="CustomEmailTemplate.TemplateBody" class="text-danger"></span>
<small class="form-text text-muted">Enter the HTML content for the email template.</small>
</div>
<div class="form-group form-check">
<input asp-for="CustomEmailTemplate.IsActive" class="form-check-input" />
<label asp-for="CustomEmailTemplate.IsActive" class="form-check-label">Is Active</label>
</div>
<button type="submit" class="btn btn-primary">Save</button>
<a asp-page="./Index" class="btn btn-secondary">Cancel</a>
</form>

@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
158 changes: 158 additions & 0 deletions EmailCollector.Api/Pages/CustomEmailTemplates/Edit.cshtml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
using System.ComponentModel.DataAnnotations;
using EmailCollector.Api.DTOs;
using EmailCollector.Api.Services;
using EmailCollector.Api.Services.CustomEmailTemplates;
using EmailCollector.Domain.Entities;
using EmailCollector.Domain.Enums;
using Ganss.Xss;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;

namespace EmailCollector.Api.Pages.CustomEmailTemplates;

public class Edit : PageModel
{
private readonly ICustomEmailTemplatesService _emailTemplatesService;
private readonly IFormService _formService;
private readonly UserManager<EmailCollectorApiUser> _userManager;


public Edit(ICustomEmailTemplatesService emailTemplatesService,
IFormService formService,
UserManager<EmailCollectorApiUser> userManager)
{
_emailTemplatesService = emailTemplatesService;
_formService = formService;
_userManager = userManager;
}

[BindProperty]
public EditCustomEmailTemplateViewModel CustomEmailTemplate { get; set; }

public IEnumerable<SelectListItem> FormOptions { get; set; }
public IEnumerable<SelectListItem> EventOptions { get; set; }

public async Task<IActionResult> OnGetAsync(Guid id)
{
var currentUser = await _userManager.GetUserAsync(User);
var userId = new Guid(currentUser?.Id!);

// Fetch the template
var customEmailTemplateDto = await _emailTemplatesService.GetCustomEmailTemplateById(id);

if (customEmailTemplateDto == null)
{
return NotFound();
}

CustomEmailTemplate = new EditCustomEmailTemplateViewModel
{
FormId = customEmailTemplateDto.FormId,
Event = customEmailTemplateDto.Event,
TemplateBody = customEmailTemplateDto.TemplateBody,
TemplateSubject = customEmailTemplateDto.TemplateSubject,
IsActive = customEmailTemplateDto.IsActive
};

// Verify that the template's FormId belongs to the current user
var userForms = await _formService.GetFormsByUserAsync(userId);
var userFormIds = userForms.Select(f => f.Id).ToHashSet();

if (!userFormIds.Contains(CustomEmailTemplate.FormId))
{
return Forbid();
}

// Populate FormOptions (disabled in UI)
FormOptions = userForms.Select(f => new SelectListItem
{
Value = f.Id.ToString(),
Text = f.FormName,
Selected = f.Id == CustomEmailTemplate.FormId
});

// Populate event options
PopulateEventOptions();

return Page();
}

public async Task<IActionResult> OnPostAsync(Guid id)
{
var currentUser = await _userManager.GetUserAsync(User);
var userId = new Guid(currentUser?.Id!);

// Fetch the existing template
var existingTemplate = await _emailTemplatesService.GetCustomEmailTemplateById(id);
if (existingTemplate == null)
{
return NotFound();
}

// Verify that the template's FormId belongs to the current user
var userForms = await _formService.GetFormsByUserAsync(userId);
var userFormIds = userForms.Select(f => f.Id).ToHashSet();

if (!userFormIds.Contains(existingTemplate.FormId))
{
return Forbid();
}

// Since FormId is not editable, ensure it's not altered
CustomEmailTemplate.FormId = existingTemplate.FormId;

// Sanitize HTML content
var sanitizer = new HtmlSanitizer();
CustomEmailTemplate.TemplateBody = sanitizer.Sanitize(CustomEmailTemplate.TemplateBody);

// Update the template DTO
var updatedDto = new CustomEmailTemplateDto
{
Id = existingTemplate.Id,
FormId = existingTemplate.FormId,
Event = CustomEmailTemplate.Event,
TemplateSubject = CustomEmailTemplate.TemplateSubject,
TemplateBody = CustomEmailTemplate.TemplateBody,
TemplateBodyUri = existingTemplate.TemplateBodyUri, // Assuming URI remains same
IsActive = CustomEmailTemplate.IsActive,
CreatedAt = existingTemplate.CreatedAt,
UpdatedAt = DateTime.UtcNow
};

Console.WriteLine("saving");
await _emailTemplatesService.SaveCustomEmailTemplate(updatedDto);
Console.WriteLine("saved");
return RedirectToPage("./Index");
}

private void PopulateEventOptions()
{
EventOptions = Enum.GetValues(typeof(TemplateEvent))
.Cast<TemplateEvent>()
.Select(e => new SelectListItem
{
Value = e.ToString(),
Text = e.ToString().Replace('_', ' ')
});
}

public class EditCustomEmailTemplateViewModel
{
[Required(ErrorMessage = "Event is required.")]
public TemplateEvent Event { get; set; }

[Required(ErrorMessage = "Form is required.")]
public Guid FormId { get; set; }

[Required(ErrorMessage = "Template Subject is required.")]
[StringLength(100, ErrorMessage = "Template Subject cannot exceed 100 characters.")]
public string TemplateSubject { get; set; }

[Required(ErrorMessage = "Template Body is required.")]
public string TemplateBody { get; set; }

public bool IsActive { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,32 +106,55 @@ public async Task SaveCustomEmailTemplate(CustomEmailTemplateDto customEmailTemp
if (customEmailTemplateDto == null)
throw new ArgumentNullException(nameof(customEmailTemplateDto));

string newUri = await _templateStorageProvider.SaveTemplateBodyAsync(customEmailTemplateDto.TemplateBody,
customEmailTemplateDto.TemplateBodyUri);

var templateEntity = new CustomEmailTemplate
// Check if the entity already exists
var existingEntity = await _customEmailTemplatesRepository.GetByIdAsync(customEmailTemplateDto.Id);
if (existingEntity != null)
{
Id = customEmailTemplateDto.Id == Guid.Empty ? Guid.NewGuid() : customEmailTemplateDto.Id,
CreatedAt = customEmailTemplateDto.Id == Guid.Empty ? DateTime.UtcNow : customEmailTemplateDto.CreatedAt,
Event = customEmailTemplateDto.Event,
FormId = customEmailTemplateDto.FormId,
IsActive = customEmailTemplateDto.IsActive,
TemplateSubject = customEmailTemplateDto.TemplateSubject,
TemplateBodyUri = newUri,
UpdatedAt = DateTime.UtcNow
};
// Update existing entity
existingEntity.Event = customEmailTemplateDto.Event;
existingEntity.TemplateSubject = customEmailTemplateDto.TemplateSubject;
existingEntity.TemplateBodyUri = await _templateStorageProvider.SaveTemplateBodyAsync(
customEmailTemplateDto.TemplateBody,
customEmailTemplateDto.TemplateBodyUri
);
existingEntity.IsActive = customEmailTemplateDto.IsActive;
existingEntity.UpdatedAt = DateTime.UtcNow;

await _customEmailTemplatesRepository.Update(existingEntity);
}
else
{
// Create new entity
var newEntity = new CustomEmailTemplate
{
Id = Guid.NewGuid(),
CreatedAt = DateTime.UtcNow,
Event = customEmailTemplateDto.Event,
FormId = customEmailTemplateDto.FormId,
IsActive = customEmailTemplateDto.IsActive,
TemplateSubject = customEmailTemplateDto.TemplateSubject,
TemplateBodyUri = await _templateStorageProvider.SaveTemplateBodyAsync(
customEmailTemplateDto.TemplateBody,
customEmailTemplateDto.TemplateBodyUri
),
UpdatedAt = DateTime.UtcNow
};

await _customEmailTemplatesRepository.AddAsync(templateEntity);
await _customEmailTemplatesRepository.AddAsync(newEntity);
}
}

public async Task DeleteCustomEmailTemplate(Guid templateId)
{
await _customEmailTemplatesRepository.RemoveById(templateId);
_templateStorageProvider.DeleteTemplateBodyAsync(templateId.ToString());
}

public async Task<CustomEmailTemplateDto> GetCustomEmailTemplateById(Guid id)
{
var entity = await _customEmailTemplatesRepository.GetByIdAsync(id);
var templateBody = await _templateStorageProvider.GetTemplateBodyAsync(entity.TemplateBodyUri);

return new CustomEmailTemplateDto
{
Id = entity.Id,
Expand All @@ -141,6 +164,7 @@ public async Task<CustomEmailTemplateDto> GetCustomEmailTemplateById(Guid id)
IsActive = entity.IsActive,
TemplateSubject = entity.TemplateSubject,
TemplateBodyUri = entity.TemplateBodyUri,
TemplateBody = templateBody,
UpdatedAt = entity.UpdatedAt
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,10 @@ public async Task<string> SaveTemplateBodyAsync(string templateBody, string curr

return fileName;
}

public void DeleteTemplateBodyAsync(string uri)
{
var filePath = Path.Combine(_baseDirectory, uri);
File.Delete(filePath);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ public interface ITemplateStorageProvider
{
Task<string> GetTemplateBodyAsync(string uri);
Task<string> SaveTemplateBodyAsync(string templateBody, string currentUri = null);
void DeleteTemplateBodyAsync(string uri);
}
1 change: 1 addition & 0 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ services:
- "5001:8080"
volumes:
- ./EmailCollector.Api/Data:/app/data
- ./CustomEmailTemplates:/app/CustomEmailTemplates
depends_on:
- redis
environment:
Expand Down
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ services:
- "80:80"
volumes:
- ./EmailCollector.Api/Data:/app/data
- ./CustomEmailTemplates:/app/CustomEmailTemplates
depends_on:
- redis
environment:
Expand Down

0 comments on commit c3112f2

Please sign in to comment.