Skip to content

Commit

Permalink
add feature for a user to send a private message to site admin and mo…
Browse files Browse the repository at this point in the history
…derator roles
  • Loading branch information
adelikat committed Jul 8, 2023
1 parent 3d86ab6 commit 61968d2
Show file tree
Hide file tree
Showing 6 changed files with 258 additions and 51 deletions.
1 change: 1 addition & 0 deletions TASVideos.Core/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ private static IServiceCollection AddServices(this IServiceCollection services,
services.AddScoped<IMovieFormatDeprecator, MovieFormatDeprecator>();
services.AddScoped<IForumService, ForumService>();
services.AddScoped<IGameSystemService, GameSystemService>();
services.AddScoped<IPrivateMessageService, PrivateMessageService>();

services.AddScoped<IJwtAuthenticator, JwtAuthenticator>();

Expand Down
101 changes: 101 additions & 0 deletions TASVideos.Core/Services/PrivateMessageService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
using TASVideos.Core.Services.Email;

namespace TASVideos.Core.Services;

public interface IPrivateMessageService
{
Task SendMessage(int fromUserId, string toUserName, string subject, string text);
Task SendMessageToRole(int fromUserId, string roleName, string subject, string text);

/// <summary>
/// Returns a list of roles that are allowed for bulk sending
/// </summary>
Task<string[]> AllowedRoles();
}

internal class PrivateMessageService : IPrivateMessageService
{
// TODO: this does not belong in code, move to a system wiki page, or database table
private static readonly string[] AllowedBulkRoles = { "site admin", "moderator" };

private readonly ApplicationDbContext _db;
private readonly IEmailService _emailService;

public PrivateMessageService(ApplicationDbContext db, IEmailService emailService)
{
_db = db;
_emailService = emailService;
}

public async Task SendMessage(int fromUserId, string toUserName, string subject, string text)
{
var toUser = await _db.Users
.Where(u => u.UserName == toUserName)
.Select(u => new
{
u.Id,
u.UserName,
u.Email,
u.EmailOnPrivateMessage
})
.SingleOrDefaultAsync();

if (toUser is null)
{
return;
}

var message = new PrivateMessage
{
FromUserId = fromUserId,
ToUserId = toUser.Id,
Subject = subject,
Text = text,
EnableBbCode = true
};

_db.PrivateMessages.Add(message);
await _db.SaveChangesAsync();

if (toUser.EmailOnPrivateMessage)
{
await _emailService.NewPrivateMessage(toUser.Email, toUser.UserName);
}
}

public async Task SendMessageToRole(int fromUserId, string roleName, string subject, string text)
{
roleName = roleName.ToLower().Trim();

var allowed = await AllowedRoles();
if (!allowed.Contains(roleName))
{
return;
}

var role = await _db.Roles
.Where(r => r.Name.ToLower() == roleName)
.SingleOrDefaultAsync();

if (role is null)
{
return;
}

var users = await _db.Users
.Where(u => u.UserRoles.Any(ur => ur.RoleId == role.Id))
.ToListAsync();

string bulkSubject = $"Sent to role {role}: {subject}";

foreach (var user in users)
{
await SendMessage(fromUserId, user.UserName, bulkSubject, text);
}
}

public async Task<string[]> AllowedRoles()
{
return await Task.FromResult(AllowedBulkRoles);
}
}
2 changes: 2 additions & 0 deletions TASVideos.Core/Services/UserManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -517,4 +517,6 @@ public async Task UserNameChanged(User user, string oldName)

await _db.SaveChangesAsync();
}

public Task<bool> Exists(string userName) => _db.Users.Exists(userName);
}
34 changes: 30 additions & 4 deletions TASVideos/Pages/Messages/Create.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,14 @@
<span asp-validation-for="Subject" class="text-danger"></span>
</form-group>
<form-group class="col-lg-6">
<label asp-for="ToUser" class="form-control-label"></label>
<input type="text" asp-for="ToUser" class="form-control" autocomplete="off" placeholder="@Html.DescriptionFor(m => m.ToUser)" />
<span asp-validation-for="ToUser" class="text-danger"></span>
<div id="user-form-group" class="mb-2">
<label asp-for="ToUser" class="form-control-label"></label>
<input type="text" asp-for="ToUser" class="form-control" autocomplete="off" placeholder="@Html.DescriptionFor(m => m.ToUser)"/>
<span asp-validation-for="ToUser" class="text-danger"></span>
</div>
<label for="group-select">To Group</label>
<select id="group-select" name="@Html.NameFor(m => m.ToUser)" class="form-control" asp-items="@Model.AvailableGroupRoles">
</select>
</form-group>
</row>
<fullrow>
Expand All @@ -53,12 +58,33 @@
<partial name="_ValidationScriptsPartial" />
<script src="/js/user-search.js"></script>
<script>
searchUsers("@Html.IdFor(m => m.ToUser)");
const userBoxElemId = '@Html.IdFor(m => m.ToUser)';
const userFormGroup = document.getElementById('user-form-group');
const userBox = document.getElementById(userBoxElemId);
const formSubmitBtn = userBox
.closest('form')
.querySelector("button[type='Submit']");
searchUsers(userBoxElemId);
if (document.getElementById('quote-btn')) {
document.getElementById('quote-btn').onclick = function () {
const text = document.getElementById('replying-to-text').innerHTML;
document.getElementById('@Html.IdFor(m => m.Text)').innerHTML = `[quote]${text}[/quote]`;
};
}
const groupSelect = document.getElementById('group-select');
groupSelect.addEventListener('change', () => {
if (groupSelect.value) {
userBox.value = groupSelect.value;
formSubmitBtn.removeAttribute('disabled');
userFormGroup.classList.add('d-none');
} else {
userBox.value = '';
userFormGroup.classList.remove('d-none');
formSubmitBtn.setAttribute('disabled', 'disabled');
}
});
</script>
}
81 changes: 34 additions & 47 deletions TASVideos/Pages/Messages/Create.cshtml.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using TASVideos.Core.Services;
using TASVideos.Core.Services.Email;
using TASVideos.Data;
using TASVideos.Data.Entity;
using TASVideos.Models;

Expand All @@ -11,18 +10,15 @@ namespace TASVideos.Pages.Messages;
[RequirePermission(PermissionTo.SendPrivateMessages)]
public class CreateModel : BasePageModel
{
private readonly ApplicationDbContext _db;
private readonly IPrivateMessageService _privateMessageService;
private readonly UserManager _userManager;
private readonly IEmailService _emailService;

public CreateModel(
ApplicationDbContext db,
UserManager userManager,
IEmailService emailService)
IPrivateMessageService privateMessageService)
{
_db = db;
_userManager = userManager;
_emailService = emailService;
_privateMessageService = privateMessageService;
}

[FromQuery]
Expand Down Expand Up @@ -50,12 +46,14 @@ public CreateModel(

public PrivateMessageModel? ReplyingTo { get; set; }

public IEnumerable<SelectListItem> AvailableGroupRoles { get; set; } = new List<SelectListItem>();

public bool IsReply => ReplyingTo is not null;

public async Task OnGet()
{
await SetReplyingTo();

await SetAvailableGroupRoles();
ToUser = DefaultToUser ?? "";
if (IsReply)
{
Expand All @@ -74,28 +72,48 @@ public async Task<IActionResult> OnPost()
if (!ModelState.IsValid)
{
await SetReplyingTo();
await SetAvailableGroupRoles();
return Page();
}

var exists = await _db.Users.Exists(ToUser);
if (!exists)
var allowedRoles = await _privateMessageService.AllowedRoles();
if (allowedRoles.Contains(ToUser))
{
ModelState.AddModelError(nameof(ToUser), "User does not exist");
await SetReplyingTo();
return Page();
await _privateMessageService.SendMessageToRole(User.GetUserId(), ToUser, Subject, Text);
}
else
{
var exists = await _userManager.Exists(ToUser);
if (!exists)
{
ModelState.AddModelError(nameof(ToUser), "User does not exist");
await SetReplyingTo();
return Page();
}

await SendMessage();
await _privateMessageService.SendMessage(User.GetUserId(), ToUser, Subject, Text);
}

return BasePageRedirect("Inbox");
}

private async Task SetAvailableGroupRoles()
{
AvailableGroupRoles = UiDefaults.DefaultEntry
.Concat((await _privateMessageService.AllowedRoles())
.Select(m => new SelectListItem
{
Text = m,
Value = m
}));
}

private async Task SetReplyingTo()
{
if (ReplyTo > 0)
{
var message = await _userManager.GetMessage(User.GetUserId(), ReplyTo.Value);
if (message != null)
if (message is not null)
{
DefaultToUser = message.FromUserName;
ReplyingTo = new PrivateMessageModel
Expand All @@ -111,35 +129,4 @@ private async Task SetReplyingTo()
}
}
}

private async Task SendMessage()
{
var toUser = await _db.Users
.Where(u => u.UserName == ToUser)
.Select(u => new
{
u.Id,
u.UserName,
u.Email,
u.EmailOnPrivateMessage
})
.SingleAsync();

var message = new PrivateMessage
{
FromUserId = User.GetUserId(),
ToUserId = toUser.Id,
Subject = Subject,
Text = Text,
EnableBbCode = true
};

_db.PrivateMessages.Add(message);
await _db.SaveChangesAsync();

if (toUser.EmailOnPrivateMessage)
{
await _emailService.NewPrivateMessage(toUser.Email, toUser.UserName);
}
}
}
Loading

0 comments on commit 61968d2

Please sign in to comment.