();
for (var i = 1; i <= settingsManager.Current.MailerQuantity; i++)
{
var payload = await emailQueueRepository.Dequeue();
if (payload == null)
break;
if (payload.EmailQueuePayloadType == EmailQueuePayloadType.DeleteMassMessage)
throw new NotImplementedException($"EmailQueuePayloadType {payload.EmailQueuePayloadType} not implemented.");
var queuedMessage = await queuedEmailMessageRepository.GetMessage(payload.MessageID);
if (payload.EmailQueuePayloadType == EmailQueuePayloadType.MassMessage)
{
queuedMessage.ToEmail = payload.ToEmail;
queuedMessage.ToName = payload.ToName;
}
if (queuedMessage == null)
break;
messageGroup.Add(queuedMessage);
await queuedEmailMessageRepository.DeleteMessage(queuedMessage.MessageID);
}
Parallel.ForEach(messageGroup, message =>
{
try
{
smtpWrapper.Send(message);
}
catch (Exception exc)
{
if (message == null)
errorLog.Log(exc, ErrorSeverity.Email, "There was no message for the MailWorker to send.");
else
errorLog.Log(exc, ErrorSeverity.Email, $"MessageID: {message.MessageID}, To: <{message.ToEmail}> {message.ToName}, Subject: {message.Subject}");
}
});
}
catch (Exception exc)
{
errorLog.Log(exc, ErrorSeverity.Error);
}
}
}
================================================
FILE: src/PopForums/Email/ForgotPasswordMailer.cs
================================================
namespace PopForums.Email;
public interface IForgotPasswordMailer
{
Task ComposeAndQueue(User user, string resetLink);
}
public class ForgotPasswordMailer : IForgotPasswordMailer
{
public ForgotPasswordMailer(ISettingsManager settingsManager, IQueuedEmailService queuedEmailService)
{
_settingsManager = settingsManager;
_queuedEmailService = queuedEmailService;
}
private readonly ISettingsManager _settingsManager;
private readonly IQueuedEmailService _queuedEmailService;
public async Task ComposeAndQueue(User user, string resetLink)
{
if (user == null)
throw new ArgumentNullException("user");
if (String.IsNullOrEmpty(resetLink))
throw new ArgumentException("resetLink");
var settings = _settingsManager.Current;
var body = String.Format(Resources.ForgotPasswordEmail
, settings.ForumTitle, resetLink, settings.MailSignature, Environment.NewLine);
var message = new QueuedEmailMessage
{
Body = body,
Subject = String.Format(Resources.ForgotPasswordSubject, settings.ForumTitle),
ToEmail = user.Email,
ToName = user.Name,
FromName = settings.ForumTitle,
QueueTime = DateTime.UtcNow
};
await _queuedEmailService.CreateAndQueueEmail(message);
}
}
================================================
FILE: src/PopForums/Email/MailingListComposer.cs
================================================
namespace PopForums.Email;
public interface IMailingListComposer
{
void ComposeAndQueue(User user, string subject, string body, string htmlBody, string unsubscribeLink);
}
public class MailingListComposer : IMailingListComposer
{
public MailingListComposer(ISettingsManager settingsManager, IQueuedEmailService queuedEmailService)
{
_settingsManager = settingsManager;
_queuedEmailService = queuedEmailService;
}
private readonly ISettingsManager _settingsManager;
private readonly IQueuedEmailService _queuedEmailService;
public void ComposeAndQueue(User user, string subject, string body, string htmlBody, string unsubscribeLink)
{
var settings = _settingsManager.Current;
var ps = $"{Environment.NewLine}{Environment.NewLine}Unsubscribe: {unsubscribeLink}";
var message = new QueuedEmailMessage
{
Body = body + ps,
Subject = subject,
ToEmail = user.Email,
ToName = user.Name,
FromName = settings.ForumTitle,
QueueTime = DateTime.UtcNow
};
if (!string.IsNullOrWhiteSpace(htmlBody))
message.HtmlBody = $"{htmlBody}Unsubscribe: {unsubscribeLink}
";
_queuedEmailService.CreateAndQueueEmail(message);
}
}
================================================
FILE: src/PopForums/Email/NewAccountMailer.cs
================================================
namespace PopForums.Email;
public interface INewAccountMailer
{
SmtpStatusCode? Send(User user, string verifyUrl);
///
/// Used to deliver the text for a welcome e-mail, where the user is already
/// approved. The default implementation uses Resources.RegisterEmailThankYou.
/// It uses the following string format items:
/// {0} Forum title (from settings)
/// {1} Mail signature (from settings)
/// {2} Environment.NewLine
///
string NewUserApprovedEmail { get; }
///
/// Used to deliver the text for a welcome e-mail, where the user is must follow
/// a verification link. The default implementation uses Resources.RegisterEmailThankYou.
/// It uses the following string format items:
/// {0} Forum title (from settings)
/// {1} Verification URL + auth code (generated by calling code)
/// {2} Verification URL
/// {3} Authorization key (from user object)
/// {4} Mail signature (from settings)
/// {5} Environment.NewLine
///
string NewUserVerifyEmail { get; }
}
public class NewAccountMailer : INewAccountMailer
{
public NewAccountMailer(ISettingsManager settingsManager, ISmtpWrapper smtpWrapper)
{
_settingsManager = settingsManager;
_smtpWrapper = smtpWrapper;
}
private readonly ISettingsManager _settingsManager;
private readonly ISmtpWrapper _smtpWrapper;
public SmtpStatusCode? Send(User user, string verifyUrl)
{
var settings = _settingsManager.Current;
if (String.IsNullOrWhiteSpace(settings.MailerAddress))
throw new Exception("There is no MailerAddress to send e-mail from. Perhaps you didn't set up the settings.");
var message = new EmailMessage
{
ToEmail = user.Email,
ToName = user.Name,
FromName = settings.ForumTitle
};
message.Subject = String.Format(Resources.RegisterEmailSubject, settings.ForumTitle);
string body;
if (settings.IsNewUserApproved)
body = String.Format(NewUserApprovedEmail, settings.ForumTitle, settings.MailSignature, "\r\n");
else
body = String.Format(NewUserVerifyEmail, settings.ForumTitle, verifyUrl + "/" + user.AuthorizationKey, verifyUrl, user.AuthorizationKey, settings.MailSignature, "\r\n");
message.Body = body;
return _smtpWrapper.Send(message);
}
public virtual string NewUserApprovedEmail
{
get { return Resources.RegisterEmailThankYou; }
}
public virtual string NewUserVerifyEmail
{
get { return Resources.RegisterEmailThankYouVerify; }
}
}
================================================
FILE: src/PopForums/Email/SmtpStatusCode.cs
================================================
namespace PopForums.Email;
public enum SmtpStatusCode
{
SystemStatus = 211,
HelpMessage = 214,
ServiceReady = 220,
ServiceClosingTransmissionChannel = 221,
AuthenticationSuccessful = 235,
Ok = 250,
UserNotLocalWillForward = 251,
CannotVerifyUserWillAttemptDelivery = 252,
AuthenticationChallenge = 334,
StartMailInput = 354,
ServiceNotAvailable = 421,
PasswordTransitionNeeded = 432,
MailboxBusy = 450,
ErrorInProcessing = 451,
InsufficientStorage = 452,
TemporaryAuthenticationFailure = 454,
CommandUnrecognized = 500,
SyntaxError = 501,
CommandNotImplemented = 502,
BadCommandSequence = 503,
CommandParameterNotImplemented = 504,
AuthenticationRequired = 530,
AuthenticationMechanismTooWeak = 534,
AuthenticationInvalidCredentials = 535,
EncryptionRequiredForAuthenticationMechanism = 538,
MailboxUnavailable = 550,
UserNotLocalTryAlternatePath = 551,
ExceededStorageAllocation = 552,
MailboxNameNotAllowed = 553,
TransactionFailed = 554,
MailFromOrRcptToParametersNotRecognizedOrNotImplemented = 555
}
================================================
FILE: src/PopForums/Email/SmtpWrapper.cs
================================================
using MailKit.Net.Smtp;
using MailKit.Security;
using MimeKit;
namespace PopForums.Email;
public interface ISmtpWrapper
{
SmtpStatusCode? Send(EmailMessage message);
}
public class SmtpWrapper : ISmtpWrapper
{
public SmtpWrapper(ISettingsManager settingsManager, IErrorLog errorLog)
{
_settingsManager = settingsManager;
_errorLog = errorLog;
}
private readonly ISettingsManager _settingsManager;
private readonly IErrorLog _errorLog;
public SmtpStatusCode? Send(EmailMessage message)
{
if (message == null)
throw new ArgumentNullException(nameof(message));
var parsedMessage = ConvertEmailMessage(message);
var settings = _settingsManager.Current;
SmtpStatusCode? result = SmtpStatusCode.Ok;
using var client = new SmtpClient();
try
{
client.Connect(settings.SmtpServer, settings.SmtpPort, settings.UseSslSmtp ? SecureSocketOptions.StartTls : SecureSocketOptions.None);
if (settings.UseEsmtp)
client.Authenticate(settings.SmtpUser, settings.SmtpPassword);
client.Send(parsedMessage);
}
catch (SmtpCommandException exc)
{
var statusCode = (int)exc.StatusCode;
result = (SmtpStatusCode)statusCode;
_errorLog.Log(exc, ErrorSeverity.Email, $"To: {message.ToEmail}, Subject: {message.Subject}, SmtpCommandException: {statusCode}");
}
catch (SmtpProtocolException exc)
{
result = null;
_errorLog.Log(exc, ErrorSeverity.Email, $"To: {message.ToEmail}, Subject: {message.Subject}, SmtpProtocolException: {exc.Message}");
}
catch (Exception exc)
{
result = null;
_errorLog.Log(exc, ErrorSeverity.Email, $"To: {message.ToEmail}, Subject: {message.Subject}, Exception: {exc.Message}");
}
finally
{
client.Disconnect(true);
}
return result;
}
private MimeMessage ConvertEmailMessage(EmailMessage forumMessage)
{
var message = new MimeMessage();
message.Headers.Add("X-Mailer", "POP Forums");
message.To.Add(new MailboxAddress(forumMessage.ToName, forumMessage.ToEmail));
message.From.Add(new MailboxAddress(forumMessage.FromName, _settingsManager.Current.MailerAddress));
if (!string.IsNullOrWhiteSpace(forumMessage.ReplyTo))
message.ReplyTo.Add(new MailboxAddress(forumMessage.FromName, forumMessage.ReplyTo));
else
message.ReplyTo.Add(new MailboxAddress(forumMessage.FromName, _settingsManager.Current.ReplyToAddress));
message.Subject = forumMessage.Subject;
var builder = new BodyBuilder();
builder.TextBody = forumMessage.Body;
builder.HtmlBody = forumMessage.HtmlBody;
message.Body = builder.ToMessageBody();
return message;
}
}
================================================
FILE: src/PopForums/Extensions/ServiceCollections.cs
================================================
namespace PopForums.Extensions;
public static class ServiceCollections
{
public static void AddPopForumsBase(this IServiceCollection services)
{
// config
services.AddTransient();
services.AddTransient();
services.AddSingleton();
services.AddTransient();
// composers
services.AddTransient();
services.AddTransient();
services.AddTransient();
// email
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
// external auth?
services.AddTransient();
// feeds
services.AddTransient();
// scoring game
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
// services
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
}
}
================================================
FILE: src/PopForums/Extensions/Streams.cs
================================================
namespace PopForums.Extensions;
public static class Streams
{
public static byte[] ToBytes(this Stream stream)
{
var length = (int)stream.Length;
var bytes = new byte[length];
stream.ReadExactly(bytes, 0, length);
return bytes;
}
}
================================================
FILE: src/PopForums/Extensions/Strings.cs
================================================
namespace PopForums.Extensions;
public static class Strings
{
public static string GetSHA256Hash(this string text)
{
if (string.IsNullOrWhiteSpace(text))
{
return string.Empty;
}
var input = Encoding.UTF8.GetBytes(text);
using (var sha256 = SHA256.Create())
{
var output = sha256.ComputeHash(input);
return Convert.ToBase64String(output);
}
}
public static string GetSHA256Hash(this string text, Guid salt)
{
var concatString = text + salt;
return GetSHA256Hash(concatString);
}
public static string GetMD5Hash(this string text)
{
if (string.IsNullOrWhiteSpace(text))
{
return string.Empty;
}
var input = Encoding.UTF8.GetBytes(text);
using (var md5 = MD5.Create())
{
var output = md5.ComputeHash(input);
return Convert.ToBase64String(output);
}
}
public static string GetMD5Hash(this string text, Guid salt)
{
var concatString = text + salt;
return GetMD5Hash(concatString);
}
public static bool IsEmailAddress(this string text)
{
return Regex.IsMatch(text, @"^\S+?@([a-z0-9\-\.])+?\.([a-z0-9\-\.])+$", RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);
}
public static string ToUrlName(this string text)
{
if (text == null)
throw new Exception("Can't Url convert a null string.");
var result = text.Replace(" ", "-");
var replacer = new Regex(@"[^\w\-]", RegexOptions.None);
result = replacer.Replace(result, "").ToLower();
return result;
}
public static string ToUniqueUrlName(this string name, List matchingStartsWith)
{
var urlName = name.ToUrlName();
return ToUniqueName(urlName, matchingStartsWith);
}
public static string ToUniqueName(this string name, List matchingStartsWith)
{
name = Regex.Escape(name).Replace("\\ ", " ");
var originalName = name;
var matchTest = name.Replace("-", @"\-");
var count = matchingStartsWith.Count(m => Regex.IsMatch(m, @"^(" + matchTest + @")(\-\d)?$"));
if (count > 0)
name = name + "-" + (count + 1);
while (matchingStartsWith.Exists(x => x == name))
{
count++;
name = originalName + "-" + (count + 1);
}
return name;
}
public static string Trimmer(this string stringToTrim, int maxLength)
{
if (maxLength < 20) maxLength = 20;
if (stringToTrim.Length <= maxLength) return stringToTrim;
return stringToTrim.Substring(0, maxLength - 13) + "..." +
stringToTrim.Substring(stringToTrim.Length - 10, 10);
}
}
================================================
FILE: src/PopForums/Extensions/Users.cs
================================================
namespace PopForums.Extensions;
public static class Users
{
public static bool IsPostEditable(this User user, Post post)
{
if (user == null)
return false;
return user.IsInRole(PermanentRoles.Moderator) || user.UserID == post.UserID;
}
}
================================================
FILE: src/PopForums/ExternalLogin/ExternalAuthenticationResult.cs
================================================
namespace PopForums.ExternalLogin;
public class ExternalAuthenticationResult
{
public string Issuer { get; set; }
public string ProviderKey { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
================================================
FILE: src/PopForums/ExternalLogin/ExternalLoginInfo.cs
================================================
namespace PopForums.ExternalLogin;
public class ExternalLoginInfo
{
public ExternalLoginInfo(string loginProvider, string providerKey,
string displayName)
{
LoginProvider = loginProvider;
ProviderKey = providerKey;
ProviderDisplayName = displayName;
}
public string LoginProvider { get; set; }
public string ProviderKey { get; set; }
public string ProviderDisplayName { get; set; }
}
================================================
FILE: src/PopForums/ExternalLogin/ExternalUserAssociation.cs
================================================
namespace PopForums.ExternalLogin;
public class ExternalUserAssociation
{
public int ExternalUserAssociationID { get; set; }
public int UserID { get; set; }
public string Issuer { get; set; }
public string ProviderKey { get; set; }
public string Name { get; set; }
}
================================================
FILE: src/PopForums/ExternalLogin/ExternalUserAssociationManager.cs
================================================
namespace PopForums.ExternalLogin;
public interface IExternalUserAssociationManager
{
Task ExternalUserAssociationCheck(ExternalLoginInfo externalLoginInfo, string ip);
Task Associate(User user, ExternalLoginInfo externalLoginInfo, string ip);
Task> GetExternalUserAssociations(User user);
Task RemoveAssociation(User user, int externalUserAssociationID, string ip);
}
public class ExternalUserAssociationManager : IExternalUserAssociationManager
{
public ExternalUserAssociationManager(IExternalUserAssociationRepository externalUserAssociationRepository, IUserRepository userRepository, ISecurityLogService securityLogService)
{
_externalUserAssociationRepository = externalUserAssociationRepository;
_userRepository = userRepository;
_securityLogService = securityLogService;
}
private readonly IExternalUserAssociationRepository _externalUserAssociationRepository;
private readonly IUserRepository _userRepository;
private readonly ISecurityLogService _securityLogService;
public async Task ExternalUserAssociationCheck(ExternalLoginInfo externalLoginInfo, string ip)
{
if (externalLoginInfo == null)
throw new ArgumentNullException(nameof(externalLoginInfo));
var match = await _externalUserAssociationRepository.Get(externalLoginInfo.LoginProvider, externalLoginInfo.ProviderKey);
if (match == null)
{
await _securityLogService.CreateLogEntry((int?)null, null, ip, $"Issuer: {externalLoginInfo.LoginProvider}, Provider: {externalLoginInfo.ProviderKey}, Name: {externalLoginInfo.ProviderDisplayName}", SecurityLogType.ExternalAssociationCheckFailed);
return new ExternalUserAssociationMatchResult {Successful = false};
}
var user = await _userRepository.GetUser(match.UserID);
if (user == null)
{
await _securityLogService.CreateLogEntry((int?)null, null, ip, $"Issuer: {externalLoginInfo.LoginProvider}, Provider: {externalLoginInfo.ProviderKey}, Name: {externalLoginInfo.ProviderDisplayName}", SecurityLogType.ExternalAssociationCheckFailed);
return new ExternalUserAssociationMatchResult {Successful = false};
}
var result = new ExternalUserAssociationMatchResult
{
Successful = true,
ExternalUserAssociation = match,
User = user
};
await _securityLogService.CreateLogEntry(user, user, ip, $"Issuer: {match.Issuer}, Provider: {match.ProviderKey}, Name: {match.Name}", SecurityLogType.ExternalAssociationCheckSuccessful);
return result;
}
public async Task Associate(User user, ExternalLoginInfo externalLoginInfo, string ip)
{
if (user == null)
throw new ArgumentNullException(nameof(user));
if (externalLoginInfo != null)
{
if (string.IsNullOrEmpty(externalLoginInfo.LoginProvider))
throw new NullReferenceException("The external login info contains no provider.");
if (string.IsNullOrEmpty(externalLoginInfo.ProviderKey))
throw new NullReferenceException("The external login info contains no provider key.");
if (string.IsNullOrEmpty(externalLoginInfo.ProviderDisplayName))
externalLoginInfo.ProviderDisplayName = string.Empty;
await _externalUserAssociationRepository.Save(user.UserID, externalLoginInfo.LoginProvider, externalLoginInfo.ProviderKey, externalLoginInfo.ProviderDisplayName);
await _securityLogService.CreateLogEntry(user, user, ip, $"Provider: {externalLoginInfo.LoginProvider}, DisplayName: {externalLoginInfo.ProviderDisplayName}", SecurityLogType.ExternalAssociationSet);
}
}
public async Task> GetExternalUserAssociations(User user)
{
return await _externalUserAssociationRepository.GetByUser(user.UserID);
}
public async Task RemoveAssociation(User user, int externalUserAssociationID, string ip)
{
var association = await _externalUserAssociationRepository.Get(externalUserAssociationID);
if (association == null)
return;
if (association.UserID != user.UserID)
throw new Exception($"Can't delete external user association {externalUserAssociationID} because it doesn't match UserID {user.UserID}.");
await _externalUserAssociationRepository.Delete(externalUserAssociationID);
await _securityLogService.CreateLogEntry(user, user, ip, $"Issuer: {association.Issuer}, Provider: {association.ProviderKey}, Name: {association.Name}", SecurityLogType.ExternalAssociationRemoved);
}
}
================================================
FILE: src/PopForums/ExternalLogin/ExternalUserAssociationMatchResult.cs
================================================
namespace PopForums.ExternalLogin;
public class ExternalUserAssociationMatchResult
{
public bool Successful { get; set; }
public ExternalUserAssociation ExternalUserAssociation { get; set; }
public User User { get; set; }
}
================================================
FILE: src/PopForums/Feeds/FeedService.cs
================================================
namespace PopForums.Feeds;
public interface IFeedService
{
Task PublishToFeed(User user, string message, int points, DateTime timeStamp);
Task> GetFeed(User user);
}
public class FeedService : IFeedService
{
public FeedService(IFeedRepository feedRepository, IBroker broker)
{
_feedRepository = feedRepository;
_broker = broker;
}
private readonly IFeedRepository _feedRepository;
private readonly IBroker _broker;
public const int MaxFeedCount = 50;
public async Task PublishToFeed(User user, string message, int points, DateTime timeStamp)
{
if (user == null)
return;
await _feedRepository.PublishEvent(user.UserID, message, points, timeStamp);
var cutOff = await _feedRepository.GetOldestTime(user.UserID, MaxFeedCount);
await _feedRepository.DeleteOlderThan(user.UserID, cutOff);
}
public async Task> GetFeed(User user)
{
return await _feedRepository.GetFeed(user.UserID, MaxFeedCount);
}
}
================================================
FILE: src/PopForums/Global.cs
================================================
global using System;
global using System.Collections;
global using System.Collections.Generic;
global using System.IO;
global using System.Linq;
global using System.Net.Http;
global using System.Security;
global using System.Security.Cryptography;
global using System.Text;
global using System.Text.Encodings.Web;
global using System.Text.Json;
global using System.Text.Json.Serialization;
global using System.Text.RegularExpressions;
global using System.Threading;
global using System.Threading.Tasks;
global using Microsoft.Extensions.Configuration;
global using Microsoft.Extensions.DependencyInjection;
global using PopForums.Composers;
global using PopForums.Configuration;
global using PopForums.Email;
global using PopForums.Extensions;
global using PopForums.ExternalLogin;
global using PopForums.Feeds;
global using PopForums.Messaging;
global using PopForums.Models;
global using PopForums.Repositories;
global using PopForums.ScoringGame;
global using PopForums.Services;
================================================
FILE: src/PopForums/Messaging/IBroker.cs
================================================
namespace PopForums.Messaging;
public interface IBroker
{
void NotifyNewPosts(Topic topic, int lasPostID);
void NotifyForumUpdate(Forum forum);
void NotifyTopicUpdate(Topic topic, Forum forum, string topicLink);
void NotifyNewPost(Topic topic, int postID);
void NotifyPMCount(int userID, int pmCount);
void NotifyUser(Notification notification);
void NotifyUser(Notification notification, string tenantID);
void SendPMMessage(PrivateMessagePost post);
}
================================================
FILE: src/PopForums/Messaging/Models/AwardData.cs
================================================
namespace PopForums.Messaging.Models;
public class AwardData
{
public string Title { get; set; }
}
================================================
FILE: src/PopForums/Messaging/Models/AwardPayload.cs
================================================
namespace PopForums.Messaging.Models;
public class AwardPayload
{
public string Title { get; set; }
public int UserID { get; set; }
public string TenantID { get; set; }
}
================================================
FILE: src/PopForums/Messaging/Models/QuestionData.cs
================================================
namespace PopForums.Messaging.Models;
public class QuestionData
{
public string AskerName { get; set; }
public string Title { get; set; }
public int PostID { get; set; }
}
================================================
FILE: src/PopForums/Messaging/Models/ReplyData.cs
================================================
namespace PopForums.Messaging.Models;
public class ReplyData
{
public string PostName { get; set; }
public int TopicID { get; set; }
public string Title { get; set; }
}
================================================
FILE: src/PopForums/Messaging/Models/ReplyPayload.cs
================================================
namespace PopForums.Messaging.Models;
public class ReplyPayload
{
public string PostName { get; set; }
public string Title { get; set; }
public int TopicID { get; set; }
public int UserID { get; set; }
public string TenantID { get; set; }
}
================================================
FILE: src/PopForums/Messaging/Models/VoteData.cs
================================================
namespace PopForums.Messaging.Models;
public class VoteData
{
public string VoterName { get; set; }
public string Title { get; set; }
public int PostID { get; set; }
}
================================================
FILE: src/PopForums/Messaging/Notification.cs
================================================
namespace PopForums.Messaging;
public class Notification
{
public int UserID { get; set; }
public DateTime TimeStamp { get; set; }
public bool IsRead { get; set; }
public NotificationType NotificationType { get; set; }
public long ContextID { get; set; }
public JsonElement Data { get; set; }
public int UnreadCount { get; set; }
}
================================================
FILE: src/PopForums/Messaging/NotificationAdapter.cs
================================================
using PopForums.Messaging.Models;
namespace PopForums.Messaging;
public interface INotificationAdapter
{
Task Reply(string postName, string title, int topicID, int userID, string tenantID);
Task Vote(string voterName, string title, int postID, int userID);
Task QuestionAnswer(string askerName, string title, int postID, int userID);
Task Award(string title, int userID);
Task Award(string title, int userID, string tenantID);
}
public class NotificationAdapter : INotificationAdapter
{
private readonly INotificationManager _notificationManager;
public NotificationAdapter(INotificationManager notificationManager)
{
_notificationManager = notificationManager;
}
public async Task Reply(string postName, string title, int topicID, int userID, string tenantID)
{
var replyData = new ReplyData
{
PostName = postName,
Title = title,
TopicID = topicID
};
await _notificationManager.ProcessNotification(userID, NotificationType.NewReply, replyData.TopicID, replyData, tenantID);
}
public async Task Vote(string voterName, string title, int postID, int userID)
{
var voteData = new VoteData
{
VoterName = voterName,
Title = title,
PostID = postID
};
await _notificationManager.ProcessNotification(userID, NotificationType.VoteUp, postID, voteData);
}
public async Task QuestionAnswer(string askerName, string title, int postID, int userID)
{
var questionData = new QuestionData
{
AskerName = askerName,
Title = title,
PostID = postID
};
await _notificationManager.ProcessNotification(userID, NotificationType.QuestionAnswered, postID, questionData);
}
public async Task Award(string title, int userID)
{
await Award(title, userID, null);
}
public async Task Award(string title, int userID, string tenantID)
{
var awardData = new AwardData
{
Title = title
};
var sequentialContext = DateTime.UtcNow.Ticks;
await _notificationManager.ProcessNotification(userID, NotificationType.Award, sequentialContext, awardData, tenantID);
}
}
================================================
FILE: src/PopForums/Messaging/NotificationManager.cs
================================================
namespace PopForums.Messaging;
public interface INotificationManager
{
Task MarkNotificationRead(int userID, NotificationType notificationType, long contextID);
Task ProcessNotification(int userID, NotificationType notificationType, long contextID, dynamic data);
Task ProcessNotification(int userID, NotificationType notificationType, long contextID, dynamic data, string tenantID);
Task GetUnreadNotificationCount(int userID);
Task MarkAllRead(int userID);
Task> GetNotifications(int userID, DateTime afterDateTime);
}
public class NotificationManager : INotificationManager
{
private readonly INotificationRepository _notificationRepository;
private readonly IBroker _broker;
private const int PageSize = 20;
private const int MaxNotificationCount = 100;
public NotificationManager(INotificationRepository notificationRepository, IBroker broker)
{
_notificationRepository = notificationRepository;
_broker = broker;
}
public async Task ProcessNotification(int userID, NotificationType notificationType, long contextID, dynamic data)
{
await ProcessNotification(userID, notificationType, contextID, data, null);
}
public async Task ProcessNotification(int userID, NotificationType notificationType, long contextID, dynamic data, string tenantID)
{
var serializedData = JsonSerializer.SerializeToElement(data, new JsonSerializerOptions{ PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
var notification = new Notification
{
UserID = userID,
TimeStamp = DateTime.UtcNow,
IsRead = false,
NotificationType = notificationType,
ContextID = contextID,
Data = serializedData
};
var recordsUpdated = await _notificationRepository.UpdateNotification(notification);
if (recordsUpdated == 0)
await _notificationRepository.CreateNotification(notification);
notification.UnreadCount = await _notificationRepository.GetUnreadNotificationCount(userID);
if (tenantID == null || string.IsNullOrWhiteSpace(tenantID))
_broker.NotifyUser(notification);
else
_broker.NotifyUser(notification, tenantID);
}
public async Task MarkNotificationRead(int userID, NotificationType notificationType, long contextID)
{
await _notificationRepository.MarkNotificationRead(userID, notificationType, contextID);
}
public async Task> GetNotifications(int userID, DateTime afterDateTime)
{
return await _notificationRepository.GetNotifications(userID, afterDateTime, PageSize);
}
public async Task GetUnreadNotificationCount(int userID)
{
var count = await _notificationRepository.GetUnreadNotificationCount(userID);
return count > MaxNotificationCount ? MaxNotificationCount : count;
}
public async Task MarkAllRead(int userID)
{
await _notificationRepository.MarkAllRead(userID);
}
}
================================================
FILE: src/PopForums/Messaging/NotificationTunnel.cs
================================================
namespace PopForums.Messaging;
public interface INotificationTunnel
{
void SendNotificationForUserAward(string title, int userID, string tenantID);
void SendNotificationForReply(string postName, string title, int topicID, int userID, string tenantID);
}
public class NotificationTunnel : INotificationTunnel
{
public static string HeaderName = "PfApi";
private readonly INotificationAdapter _notificationAdapter;
public NotificationTunnel(INotificationAdapter notificationAdapter)
{
_notificationAdapter = notificationAdapter;
}
public async void SendNotificationForUserAward(string title, int userID, string tenantID)
{
await _notificationAdapter.Award(title, userID, tenantID);
}
public async void SendNotificationForReply(string postName, string title, int topicID, int userID, string tenantID)
{
await _notificationAdapter.Reply(postName, title, topicID, userID, tenantID);
}
}
================================================
FILE: src/PopForums/Messaging/NotificationType.cs
================================================
namespace PopForums.Messaging;
public enum NotificationType
{
NewReply = 0,
VoteUp = 1,
QuestionAnswered = 2,
Award = 3
}
================================================
FILE: src/PopForums/Models/AwardCalculationPayload.cs
================================================
namespace PopForums.Models;
public class AwardCalculationPayload
{
public string EventDefinitionID { get; set; }
public int UserID { get; set; }
public string TenantID { get; set; }
}
================================================
FILE: src/PopForums/Models/BasicJsonMessage.cs
================================================
namespace PopForums.Models;
public class BasicJsonMessage
{
public bool Result { get; set; }
public string Message { get; set; }
public object Data { get; set; }
public string Redirect { get; set; }
}
================================================
FILE: src/PopForums/Models/BasicServiceResponse.cs
================================================
namespace PopForums.Models;
public class BasicServiceResponse where T : class
{
public bool IsSuccessful { get; set; }
public string Message { get; set; }
public T Data { get; set; }
public string Redirect { get; set; }
}
================================================
FILE: src/PopForums/Models/CategorizedForumContainer.cs
================================================
namespace PopForums.Models;
public class CategorizedForumContainer
{
public CategorizedForumContainer(IEnumerable categories, IEnumerable forums)
{
ReadStatusLookup = new Dictionary();
AllCategories = categories;
AllForums = forums;
UncategorizedForums = forums.Where(f => !f.CategoryID.HasValue || f.CategoryID == 0).OrderBy(f => f.SortOrder).ToList();
CategoryDictionary = new Dictionary>();
foreach (var category in AllCategories.OrderBy(c => c.SortOrder))
{
var forumSet = AllForums.Where(f => f.CategoryID == category.CategoryID).OrderBy(f => f.SortOrder).ToList();
if (forumSet.Count > 0)
CategoryDictionary.Add(category, forumSet);
}
}
public IEnumerable AllCategories { get; private set; }
public IEnumerable AllForums { get; private set; }
public List UncategorizedForums { get; private set; }
public Dictionary> CategoryDictionary { get; private set; }
public string ForumTitle { get; set; }
public Dictionary ReadStatusLookup { get; private set; }
}
================================================
FILE: src/PopForums/Models/Category.cs
================================================
namespace PopForums.Models;
public class Category
{
public int CategoryID { get; set; }
public string Title { get; set; }
public int SortOrder { get; set; }
}
================================================
FILE: src/PopForums/Models/CategoryContainerWithForums.cs
================================================
namespace PopForums.Models;
public class CategoryContainerWithForums
{
public Category Category { get; set; }
public IEnumerable Forums { get; set; }
}
================================================
FILE: src/PopForums/Models/ClientPrivateMessagePost.cs
================================================
namespace PopForums.Models;
public class ClientPrivateMessagePost
{
public int PMPostID { get; set; }
public int UserID { get; set; }
public string Name { get; set; }
public string PostTime { get; set; }
public string FullText { get; set; }
public static ClientPrivateMessagePost[] MapForClient(List posts)
{
var messages = posts.Select(x => new ClientPrivateMessagePost { PMPostID = x.PMPostID, UserID = x.UserID, Name = x.Name, PostTime = x.PostTime.ToString("o"), FullText = x.FullText }).ToArray();
return messages;
}
public static ClientPrivateMessagePost MapForClient(PrivateMessagePost post)
{
var message = new ClientPrivateMessagePost { PMPostID = post.PMPostID, UserID = post.UserID, Name = post.Name, PostTime = post.PostTime.ToString("o"), FullText = post.FullText };
return message;
}
}
================================================
FILE: src/PopForums/Models/DisplayProfile.cs
================================================
namespace PopForums.Models;
public class DisplayProfile
{
public DisplayProfile(User user, Profile profile, UserImage userImage)
{
UserID = user.UserID;
Name = user.Name;
Joined = user.CreationDate;
Dob = profile.Dob;
Location = profile.Location;
Web = profile.Web;
Instagram = profile.Instagram;
Facebook = profile.Facebook;
AvatarID = profile.AvatarID;
ImageID = profile.ImageID;
ShowDetails = profile.ShowDetails;
if (userImage != null && userImage.IsApproved)
IsImageApproved = true;
Points = profile.Points;
IsApproved = user.IsApproved;
}
public int UserID { get; set; }
public string Name { get; set; }
public DateTime Joined { get; set; }
public DateTime? Dob { get; set; }
public string Location { get; set; }
public int PostCount { get; set; }
public string Web { get; set; }
public string Instagram { get; set; }
public string Facebook { get; set; }
public int? AvatarID { get; set; }
public int? ImageID { get; set; }
public bool ShowDetails { get; set; }
public bool IsImageApproved { get; set; }
public int Points { get; set; }
public List Feed { get; set; }
public List UserAwards { get; set; }
public bool IsApproved { get; set; }
}
================================================
FILE: src/PopForums/Models/EmailMessage.cs
================================================
namespace PopForums.Models;
public class EmailMessage
{
public string FromName { get; set; }
public string ToEmail { get; set; }
public string ToName { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
public string HtmlBody { get; set; }
public string ReplyTo { get; set; }
}
================================================
FILE: src/PopForums/Models/ErrorLogEntry.cs
================================================
namespace PopForums.Models;
public class ErrorLogEntry
{
public int ErrorID { get; set; }
public DateTime TimeStamp { get; set; }
public string Message { get; set; }
public string StackTrace { get; set; }
public string Data { get; set; }
public ErrorSeverity Severity { get; set; }
public string SeverityString => Severity.ToString();
}
================================================
FILE: src/PopForums/Models/ExpiredUserSession.cs
================================================
namespace PopForums.Models;
public class ExpiredUserSession
{
public int SessionID { get; set; }
public int? UserID { get; set; }
public DateTime LastTime { get; set; }
}
================================================
FILE: src/PopForums/Models/FeedEvent.cs
================================================
namespace PopForums.Models;
public class FeedEvent
{
public int UserID { get; set; }
public string Message { get; set; }
public int Points { get; set; }
public DateTime TimeStamp { get; set; }
}
================================================
FILE: src/PopForums/Models/Forum.cs
================================================
namespace PopForums.Models;
public class Forum
{
public int ForumID { get; set; }
public int? CategoryID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public bool IsVisible { get; set; }
public bool IsArchived { get; set; }
public int SortOrder { get; set; }
public int TopicCount { get; set; }
public int PostCount { get; set; }
public DateTime LastPostTime { get; set; }
public string LastPostName { get; set; }
public string UrlName { get; set; }
public string ForumAdapterName { get; set; }
public bool IsQAForum { get; set; }
}
================================================
FILE: src/PopForums/Models/ForumPermissionContainer.cs
================================================
namespace PopForums.Models;
public class ForumPermissionContainer
{
public int ForumID { get; set; }
public List AllRoles { get; set; }
public List PostRoles { get; set; }
public List ViewRoles { get; set; }
}
================================================
FILE: src/PopForums/Models/ForumPermissionContext.cs
================================================
namespace PopForums.Models;
public class ForumPermissionContext
{
public bool UserCanView { get; set; }
public bool UserCanPost { get; set; }
public bool UserCanModerate { get; set; }
public string DenialReason { get; set; }
}
================================================
FILE: src/PopForums/Models/ForumState.cs
================================================
namespace PopForums.Models;
public class ForumState
{
public int? ForumID { get; set; }
public int PageSize { get; set; }
public int PageIndex { get; set; }
}
================================================
FILE: src/PopForums/Models/ForumTopicContainer.cs
================================================
namespace PopForums.Models;
public class ForumTopicContainer : PagedTopicContainer
{
public Forum Forum { get; set; }
public ForumPermissionContext PermissionContext { get; set; }
public ForumState ForumState { get; set; }
}
================================================
FILE: src/PopForums/Models/IPHistoryEvent.cs
================================================
namespace PopForums.Models;
public class IPHistoryEvent
{
public DateTime EventTime { get; set; }
public string Type { get; set; }
public string Description { get; set; }
public int? UserID { get; set; }
public string Name { get; set; }
public object ID { get; set; }
}
================================================
FILE: src/PopForums/Models/IStreamResponse.cs
================================================
namespace PopForums.Models;
///
/// This interface is used to pass a Stream to send to the client. It implements IDisposable to allow for cleanup
/// of the Stream and any other unmanaged resources (like the database connections, for example). Because using
/// statements would fall out of scope when a controller action returns, the Stream would be disposed before it
/// could be used. This interface allows for the Stream to be used and then disposed of when the client is done by
/// registering it with the HttpResponse RegisterForDispose method.
///
public interface IStreamResponse : IDisposable
{
Stream Stream { get; }
}
================================================
FILE: src/PopForums/Models/Ignore.cs
================================================
namespace PopForums.Models;
public class Ignore
{
public int UserID { get; set; }
public int IgnoreUserID { get; set; }
}
public class IgnoreWithName : Ignore
{
public string Name { get; set; }
}
================================================
FILE: src/PopForums/Models/ModerationLogEntry.cs
================================================
namespace PopForums.Models;
public class ModerationLogEntry
{
public int ModerationID { get; set; }
public DateTime TimeStamp { get; set; }
public int UserID { get; set; }
public string UserName { get; set; }
public ModerationType ModerationType { get; set; }
public string ModerationTypeString => ModerationType.ToString();
public int? ForumID { get; set; }
public int TopicID { get; set; }
public int? PostID { get; set; }
public string Comment { get; set; }
public string OldText { get; set; }
}
================================================
FILE: src/PopForums/Models/ModerationType.cs
================================================
namespace PopForums.Models;
public enum ModerationType
{
NotSet = 0,
PostEdit = 1,
PostDelete = 2,
PostDeletePermanently = 3,
TopicEdit = 4,
TopicDelete = 5,
TopicDeletePermanently = 6,
TopicClose = 7,
TopicOpen = 8,
TopicPin = 9,
TopicUnpin = 10,
TopicMoved = 11,
TopicUndelete = 12,
PostUndelete = 13,
TopicRenamed = 14,
TopicCloseAuto = 15
}
================================================
FILE: src/PopForums/Models/ModifyForumRolesContainer.cs
================================================
namespace PopForums.Models;
public class ModifyForumRolesContainer
{
public int ForumID { get; set; }
public ModifyForumRolesType ModifyType { get; set; }
public string Role { get; set; }
}
================================================
FILE: src/PopForums/Models/ModifyForumRolesType.cs
================================================
namespace PopForums.Models;
public enum ModifyForumRolesType
{
AddView,
RemoveView,
AddPost,
RemovePost,
RemoveAllView,
RemoveAllPost
}
================================================
FILE: src/PopForums/Models/NewPost.cs
================================================
namespace PopForums.Models;
public class NewPost
{
public string Title { get; set; }
public string FullText { get; set; }
public bool IncludeSignature { get; set; }
///
/// The ForumID or TopicID the post will be associated with.
///
public int ItemID { get; set; }
public bool CloseOnReply { get; set; }
public bool IsPlainText { get; set; }
public bool IsImageEnabled { get; set; }
public int ParentPostID { get; set; }
public string[] PostImageIDs { get; set; }
}
================================================
FILE: src/PopForums/Models/PagedListOfT.cs
================================================
namespace PopForums.Models;
public class PagedList : PagerContext where T : class
{
public IEnumerable List { get; set; }
}
================================================
FILE: src/PopForums/Models/PagedTopicContainer.cs
================================================
namespace PopForums.Models;
public class PagedTopicContainer
{
public PagedTopicContainer()
{
ReadStatusLookup = new Dictionary();
}
public List Topics { get; set; }
public PagerContext PagerContext { get; set; }
public Dictionary ReadStatusLookup { get; private set; }
public Dictionary ForumTitles { get; set; }
}
================================================
FILE: src/PopForums/Models/PagerContext.cs
================================================
namespace PopForums.Models;
public class PagerContext
{
public int PageCount { get; set; }
public int PageIndex { get; set; }
public int PageSize { get; set; }
}
================================================
FILE: src/PopForums/Models/PasswordResetContainer.cs
================================================
namespace PopForums.Models;
public class PasswordResetContainer
{
public bool IsValidUser { get; set; }
public string Password { get; set; }
public string PasswordRetype { get; set; }
public string Result { get; set; }
}
================================================
FILE: src/PopForums/Models/PermanentRoles.cs
================================================
namespace PopForums.Models;
public static class PermanentRoles
{
public const string Admin = "Admin";
public const string Moderator = "Moderator";
}
================================================
FILE: src/PopForums/Models/Post.cs
================================================
namespace PopForums.Models;
public class Post
{
public int PostID { get; set; }
public int TopicID { get; set; }
public int ParentPostID { get; set; }
public string IP { get; set; }
public bool IsFirstInTopic { get; set; }
public bool ShowSig { get; set; }
public int UserID { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public string FullText { get; set; }
public DateTime PostTime { get; set; }
public bool IsEdited { get; set; }
public string LastEditName { get; set; }
public DateTime? LastEditTime { get; set; }
public bool IsDeleted { get; set; }
public int Votes { get; set; }
}
================================================
FILE: src/PopForums/Models/PostEdit.cs
================================================
namespace PopForums.Models;
public class PostEdit
{
public PostEdit() {}
public PostEdit(Post post)
{
Title = post.Title;
FullText = post.FullText;
ShowSig = post.ShowSig;
}
public string Title { get; set; }
public string FullText { get; set; }
public bool ShowSig { get; set; }
public string Comment { get; set; }
public bool IsPlainText { get; set; }
public bool IsFirstInTopic { get; set; }
public string[] PostImageIDs { get; set; }
}
================================================
FILE: src/PopForums/Models/PostImage.cs
================================================
namespace PopForums.Models;
public class PostImage
{
public string ID { get; init; }
public DateTime TimeStamp { get; init; }
public string TenantID { get; init; }
public string ContentType { get; init; }
public byte[] ImageData { get; init; }
}
================================================
FILE: src/PopForums/Models/PostImagePersistPayload.cs
================================================
namespace PopForums.Models;
public class PostImagePersistPayload
{
public string Url { get; set; }
public string ID { get; set; }
}
================================================
FILE: src/PopForums/Models/PostItemContainer.cs
================================================
namespace PopForums.Models;
public class PostItemContainer
{
public Post Post { get; set; }
public List VotedPostIDs { get; set; }
public Dictionary Signatures { get; set; }
public Dictionary Avatars { get; set; }
public User User { get; set; }
public Profile Profile { get; set; }
public Topic Topic { get; set; }
public List IgnoreUserIDs { get; set; }
}
================================================
FILE: src/PopForums/Models/PostWithChildren.cs
================================================
namespace PopForums.Models;
public class PostWithChildren
{
public Post Post { get; set; }
public List Children { get; set; }
public DateTime? LastReadTime { get; set; }
}
================================================
FILE: src/PopForums/Models/PrivateMessage.cs
================================================
namespace PopForums.Models;
public class PrivateMessage
{
public int PMID { get; set; }
public DateTime LastPostTime { get; set; }
public JsonElement Users { get; set; }
public DateTime LastViewDate { get; set; }
public static string GetUserNames(PrivateMessage pm, int excludeUserID)
{
var users = pm.Users.Deserialize(new JsonSerializerOptions{PropertyNamingPolicy = JsonNamingPolicy.CamelCase});
if (users == null)
return string.Empty;
var names = new List();
foreach (var item in users)
{
if (item.UserID != excludeUserID)
names.Add(item.Name as string);
}
var result = string.Join(", ", names);
return result;
}
private class UserNamePair
{
public int UserID { get; set; }
public string Name { get; set; }
}
}
================================================
FILE: src/PopForums/Models/PrivateMessageBoxType.cs
================================================
namespace PopForums.Models;
public enum PrivateMessageBoxType
{
Inbox = 1,
Archive = 2
}
================================================
FILE: src/PopForums/Models/PrivateMessagePost.cs
================================================
namespace PopForums.Models;
public class PrivateMessagePost
{
public int PMPostID { get; set; }
public int PMID { get; set; }
public int UserID { get; set; }
public string Name { get; set; }
public DateTime PostTime { get; set; }
public string FullText { get; set; }
}
================================================
FILE: src/PopForums/Models/PrivateMessageState.cs
================================================
namespace PopForums.Models;
public class PrivateMessageState
{
public int PmID { get; set; }
public JsonElement Users { get; set; }
public ClientPrivateMessagePost[] Messages { get; set; }
public int? NewestPostID { get; set; }
public bool IsUserNotFound { get; set; }
}
================================================
FILE: src/PopForums/Models/PrivateMessageUser.cs
================================================
namespace PopForums.Models;
public class PrivateMessageUser
{
public int PMID { get; set; }
public int UserID { get; set; }
public DateTime LastViewDate { get; set; }
public bool IsArchived { get; set; }
}
================================================
FILE: src/PopForums/Models/PrivateMessageView.cs
================================================
namespace PopForums.Models;
public class PrivateMessageView
{
public PrivateMessage PrivateMessage { get; set; }
public PrivateMessageState State { get; set; }
}
================================================
FILE: src/PopForums/Models/Profile.cs
================================================
namespace PopForums.Models;
public class Profile
{
public int UserID { get; set; }
public bool IsSubscribed { get; set; }
public string Signature { get; set; }
public bool ShowDetails { get; set; }
public string Location { get; set; }
public bool IsPlainText { get; set; }
public DateTime? Dob { get; set; }
public string Web { get; set; }
public string Facebook { get; set; }
public string Instagram { get; set; }
public bool IsTos { get; set; }
public int? AvatarID { get; set; }
public int? ImageID { get; set; }
public bool HideVanity { get; set; }
public int? LastPostID { get; set; }
public int Points { get; set; }
public bool IsAutoFollowOnReply { get; set; }
}
================================================
FILE: src/PopForums/Models/QAPostItemContainer.cs
================================================
namespace PopForums.Models;
public class QAPostItemContainer : PostItemContainer
{
public PostWithChildren PostWithChildren { get; set; }
}
================================================
FILE: src/PopForums/Models/QueuedEmailMessage.cs
================================================
namespace PopForums.Models;
public class QueuedEmailMessage : EmailMessage
{
public int MessageID { get; set; }
public DateTime QueueTime { get; set; }
}
================================================
FILE: src/PopForums/Models/ReadStatus.cs
================================================
namespace PopForums.Models;
[Flags]
public enum ReadStatus
{
NoNewPosts = 1,
NewPosts = 2,
Closed = 4,
Open = 8,
Pinned = 16,
NotPinned = 32
}
================================================
FILE: src/PopForums/Models/ResponseOfT.cs
================================================
namespace PopForums.Models;
///
/// A generic container for wrapping the response of external calls for consumption by internal service.
///
///
public class Response where T : class
{
///
/// Creates a generic Response with IsValid set to true and no debug information or exception.
///
/// The strongly typed result to be consumed by the caller.
public Response(T data)
{
Data = data;
IsValid = true;
}
///
/// Creates a generic response with all fields set.
///
///
/// Default is false.
/// Default is null.
/// Default is null.
public Response(T data, bool isValid = false, Exception exception = null, string debugInfo = null)
{
Data = data;
IsValid = isValid;
Exception = exception;
DebugInfo = debugInfo;
}
public T Data { get; }
public bool IsValid { get; }
public Exception Exception { get; }
public string DebugInfo { get; }
}
================================================
FILE: src/PopForums/Models/SearchIndexPayload.cs
================================================
namespace PopForums.Models;
public class SearchIndexPayload
{
public int TopicID { get; set; }
public string TenantID { get; set; }
public bool IsForRemoval { get; set; }
}
================================================
FILE: src/PopForums/Models/SearchType.cs
================================================
namespace PopForums.Models;
public enum SearchType
{
Rank,
Date,
Title,
Name,
Replies
}
================================================
FILE: src/PopForums/Models/SearchWord.cs
================================================
namespace PopForums.Models;
public class SearchWord
{
public string Word { get; set; }
public int TopicID { get; set; }
public int Rank { get; set; }
}
================================================
FILE: src/PopForums/Models/SecurityLogEntry.cs
================================================
namespace PopForums.Models;
public class SecurityLogEntry
{
public int SecurityLogID { get; set; }
public SecurityLogType SecurityLogType { get; set; }
public string SecurityLogTypeString => SecurityLogType.ToString();
public int? UserID { get; set; }
public int? TargetUserID { get; set; }
public string IP { get; set; }
public string Message { get; set; }
public DateTime ActivityDate { get; set; }
}
================================================
FILE: src/PopForums/Models/SecurityLogType.cs
================================================
namespace PopForums.Models;
public enum SecurityLogType
{
Undefined = 0,
Login = 1,
Logout = 2,
PasswordChange = 3,
EmailChange = 4,
FailedLogin = 5,
UserCreated = 6,
UserDeleted = 7,
RoleCreated = 8,
RoleDeleted = 9,
UserAddedToRole = 10,
UserRemovedFromRole = 11,
UserSessionStart = 12,
UserSessionEnd = 13,
NameChange = 14,
IsApproved = 15,
IsNotApproved = 16,
ExternalAssociationSet = 17,
ExternalAssociationRemoved = 18,
ExternalAssociationCheckSuccessful = 19,
ExternalAssociationCheckFailed = 20,
ReCaptchaFailed = 21,
ExternalLoginChallengeFailed = 22
}
================================================
FILE: src/PopForums/Models/ServiceHeartbeat.cs
================================================
namespace PopForums.Models;
public class ServiceHeartbeat
{
public string ServiceName { get; set; }
public string MachineName { get; set; }
public DateTime LastRun { get; set; }
}
================================================
FILE: src/PopForums/Models/SetupVariables.cs
================================================
namespace PopForums.Models;
public class SetupVariables
{
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string ForumTitle { get; set; }
public string SmtpServer { get; set; }
public int SmtpPort { get; set; }
public string MailerAddress { get; set; }
public bool UseEsmtp { get; set; }
public string SmtpUser { get; set; }
public string SmtpPassword { get; set; }
public bool UseSslSmtp { get; set; }
}
================================================
FILE: src/PopForums/Models/SignupData.cs
================================================
namespace PopForums.Models;
public class SignupData
{
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string PasswordRetype { get; set; }
public bool IsSubscribed { get; set; }
public bool IsCoppa { get; set; }
public bool IsTos { get; set; }
public string Token { get; set; }
public bool IsAutoFollowOnReply { get; set; }
public static string GetCoppaDate()
{
return DateTime.Now.AddYears(-13).ToString("D");
}
}
================================================
FILE: src/PopForums/Models/SingleString.cs
================================================
namespace PopForums.Models;
public class SingleString
{
public string String { get; set; }
}
================================================
FILE: src/PopForums/Models/SubscribeNotificationPayload.cs
================================================
namespace PopForums.Models;
public class SubscribeNotificationPayload
{
public int TopicID { get; set; }
public string TopicTitle { get; set; }
public int PostingUserID { get; set; }
public string PostingUserName { get; set; }
public string TenantID { get; set; }
}
================================================
FILE: src/PopForums/Models/TimeFormats.cs
================================================
namespace PopForums.Models;
public class TimeFormats
{
public string TodayTime { get; set; }
public string YesterdayTime { get; set; }
public string MinutesAgo { get; set; }
public string OneMinuteAgo { get; set; }
public string LessThanMinute { get; set; }
}
================================================
FILE: src/PopForums/Models/Topic.cs
================================================
namespace PopForums.Models;
public class Topic
{
public int TopicID { get; set; }
public int ForumID { get; set; }
public string Title { get; set; }
public int ReplyCount { get; set; }
public int ViewCount { get; set; }
public int StartedByUserID { get; set; }
public string StartedByName { get; set; }
public int LastPostUserID { get; set; }
public string LastPostName { get; set; }
public DateTime LastPostTime { get; set; }
public bool IsClosed { get; set; }
public bool IsPinned { get; set; }
public bool IsDeleted { get; set; }
public string UrlName { get; set; }
public int? AnswerPostID { get; set; }
}
================================================
FILE: src/PopForums/Models/TopicContainer.cs
================================================
namespace PopForums.Models;
public class TopicContainer
{
public Forum Forum { get; set; }
public Topic Topic { get; set; }
public List Posts { get; set; }
public PagerContext PagerContext { get; set; }
public ForumPermissionContext PermissionContext { get; set; }
public Dictionary Signatures { get; set; }
public Dictionary Avatars { get; set; }
public List VotedPostIDs { get; set; }
public DateTime? LastReadTime { get; set; }
public TopicState TopicState { get; set; }
public List IgnoreUserIDs { get; set; }
}
================================================
FILE: src/PopForums/Models/TopicContainerForQA.cs
================================================
namespace PopForums.Models;
public class TopicContainerForQA : TopicContainer
{
public PostWithChildren QuestionPostWithComments { get; set; }
public List AnswersWithComments { get; set; }
}
================================================
FILE: src/PopForums/Models/TopicState.cs
================================================
namespace PopForums.Models;
public class TopicState
{
public int TopicID { get; set; }
public bool IsImageEnabled { get; set; }
public bool IsSubscribed { get; set; }
public bool IsFavorite { get; set; }
public int? PageIndex { get; set; }
public int? PageCount { get; set; }
public int LastVisiblePostID { get; set; }
public int? AnswerPostID { get; set; }
}
================================================
FILE: src/PopForums/Models/TopicUnsubscribeContainer.cs
================================================
namespace PopForums.Models;
public class TopicUnsubscribeContainer
{
public User User { get; set; }
public Topic Topic { get; set; }
}
================================================
FILE: src/PopForums/Models/User.cs
================================================
namespace PopForums.Models;
public class User
{
public int UserID { get; set; }
public DateTime CreationDate { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public Guid AuthorizationKey { get; set; }
public bool IsApproved { get; set; }
public DateTime? TokenExpiration { get; set; }
public List Roles { get; set; }
public bool IsInRole(string role)
{
if (Roles == null)
throw new Exception("Roles not set for user.");
return Roles.Contains(role);
}
}
================================================
FILE: src/PopForums/Models/UserEdit.cs
================================================
namespace PopForums.Models;
public class UserEdit
{
public UserEdit() {}
public UserEdit(User user, Profile profile)
{
UserID = user.UserID;
Name = user.Name;
Email = user.Email;
IsApproved = user.IsApproved;
IsSubscribed = profile.IsSubscribed;
Signature = profile.Signature;
ShowDetails = profile.ShowDetails;
Location = profile.Location;
IsPlainText = profile.IsPlainText;
Dob = profile.Dob;
Web = profile.Web;
Instagram = profile.Instagram;
Facebook = profile.Facebook;
HideVanity = profile.HideVanity;
Roles = user.Roles.ToArray();
AvatarID = profile.AvatarID;
ImageID = profile.ImageID;
IsAutoFollowOnReply = profile.IsAutoFollowOnReply;
}
public int UserID { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string NewEmail { get; set; }
public string NewPassword { get; set; }
public bool IsApproved { get; set; }
public bool IsSubscribed { get; set; }
public string Signature { get; set; }
public bool ShowDetails { get; set; }
public string Location { get; set; }
public bool IsPlainText { get; set; }
public DateTime? Dob { get; set; }
public string Web { get; set; }
public string Instagram { get; set; }
public string Facebook { get; set; }
public bool HideVanity { get; set; }
public string[] Roles { get; set; }
public int? AvatarID { get; set; }
public int? ImageID { get; set; }
public bool DeleteAvatar { get; set; }
public bool DeleteImage { get; set; }
public bool IsAutoFollowOnReply { get; set; }
}
================================================
FILE: src/PopForums/Models/UserEditProfile.cs
================================================
namespace PopForums.Models;
public class UserEditProfile
{
public UserEditProfile() {}
public UserEditProfile(Profile profile)
{
IsSubscribed = profile.IsSubscribed;
Signature = profile.Signature;
ShowDetails = profile.ShowDetails;
Location = profile.Location;
IsPlainText = profile.IsPlainText;
Dob = profile.Dob;
Web = profile.Web;
Instagram = profile.Instagram;
Facebook = profile.Facebook;
HideVanity = profile.HideVanity;
IsAutoFollowOnReply = profile.IsAutoFollowOnReply;
}
public bool IsSubscribed { get; set; }
public string Signature { get; set; }
public bool ShowDetails { get; set; }
public string Location { get; set; }
public bool IsPlainText { get; set; }
public DateTime? Dob { get; set; }
public string Web { get; set; }
public string Instagram { get; set; }
public string Facebook { get; set; }
public bool HideVanity { get; set; }
public bool IsAutoFollowOnReply { get; set; }
}
================================================
FILE: src/PopForums/Models/UserEditSecurity.cs
================================================
namespace PopForums.Models;
public class UserEditSecurity
{
public UserEditSecurity() {}
public UserEditSecurity(User user, bool isNewUserApproved)
{
OldEmail = user.Email;
IsNewUserApproved = isNewUserApproved;
}
public string OldPassword { get; set; }
public string NewPassword { get; set; }
public string NewPasswordRetype { get; set; }
public string OldEmail { get; private set; }
public string NewEmail { get; set; }
public string NewEmailRetype { get; set; }
public bool IsNewUserApproved { get; set; }
public bool NewPasswordsMatch()
{
if (String.IsNullOrWhiteSpace(NewPassword) || String.IsNullOrWhiteSpace(NewPasswordRetype))
return false;
return NewPassword == NewPasswordRetype;
}
public bool NewEmailsMatch()
{
if (String.IsNullOrWhiteSpace(NewEmail) || String.IsNullOrWhiteSpace(NewEmailRetype))
return false;
return NewEmail == NewEmailRetype;
}
}
================================================
FILE: src/PopForums/Models/UserImage.cs
================================================
namespace PopForums.Models;
public class UserImage
{
public int UserImageID { get; set; }
public int UserID { get; set; }
public int SortOrder { get; set; }
public bool IsApproved { get; set; }
}
================================================
FILE: src/PopForums/Models/UserImageApprovalContainer.cs
================================================
namespace PopForums.Models;
public class UserImageApprovalContainer
{
public bool IsNewUserImageApproved { get; set; }
public List Unapproved { get; set; }
}
public class UserImagePair
{
public User User { get; set; }
public UserImage UserImage { get; set; }
}
================================================
FILE: src/PopForums/Models/UserResult.cs
================================================
namespace PopForums.Models;
public class UserResult
{
public int UserID { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public DateTime CreationDate { get; set; }
public string IP { get; set; }
}
================================================
FILE: src/PopForums/Models/UserSearch.cs
================================================
namespace PopForums.Models;
public class UserSearch
{
public string SearchText { get; set; }
public UserSearchType SearchType { get; set; }
public enum UserSearchType
{
Name, Email, Role
}
}
================================================
FILE: src/PopForums/Models/VotePostContainer.cs
================================================
namespace PopForums.Models;
public class VotePostContainer
{
public int PostID { get; set; }
public int Votes { get; set; }
public Dictionary Voters { get; set; }
}
================================================
FILE: src/PopForums/PopForums.csproj
================================================
PopForums Class Library
22.0.0
Jeff Putz
net10.0
PopForums
PopForums
true
https://github.com/POPWorldMedia/POPForums
https://github.com/POPWorldMedia/POPForums
2025, POP World Media, LLC
MIT
True
True
Resources.resx
PublicResXFileCodeGenerator
Resources.Designer.cs
PopForums
================================================
FILE: src/PopForums/Repositories/IAwardCalculationQueueRepository.cs
================================================
namespace PopForums.Repositories;
public interface IAwardCalculationQueueRepository
{
Task Enqueue(AwardCalculationPayload payload);
Task> Dequeue();
}
================================================
FILE: src/PopForums/Repositories/IAwardConditionRepository.cs
================================================
namespace PopForums.Repositories;
public interface IAwardConditionRepository
{
Task> GetConditions(string awardDefinitionID);
Task DeleteConditions(string awardDefinitionID);
Task SaveConditions(List conditions);
Task DeleteConditionsByEventDefinitionID(string eventDefinitionID);
Task DeleteCondition(string awardDefinitionID, string eventDefinitionID);
}
================================================
FILE: src/PopForums/Repositories/IAwardDefinitionRepository.cs
================================================
namespace PopForums.Repositories;
public interface IAwardDefinitionRepository
{
Task Get(string awardDefinitionID);
Task> GetAll();
Task> GetByEventDefinitionID(string eventDefinitionID);
Task Create(string awardDefinitionID, string title, string description, bool isSingleTimeAward);
Task Delete(string awardDefinitionID);
}
================================================
FILE: src/PopForums/Repositories/IBanRepository.cs
================================================
namespace PopForums.Repositories;
public interface IBanRepository
{
Task BanIP(string ip);
Task RemoveIPBan(string ip);
Task> GetIPBans();
Task IPIsBanned(string ip);
Task BanEmail(string email);
Task RemoveEmailBan(string email);
Task> GetEmailBans();
Task EmailIsBanned(string email);
}
================================================
FILE: src/PopForums/Repositories/ICategoryRepository.cs
================================================
namespace PopForums.Repositories;
public interface ICategoryRepository
{
Task Get(int categoryID);
Task> GetAll();
Task Create(string newTitle, int sortOrder);
Task Delete(int categoryID);
Task Update(Category category);
}
================================================
FILE: src/PopForums/Repositories/IEmailQueueRepository.cs
================================================
namespace PopForums.Repositories;
public interface IEmailQueueRepository
{
Task Enqueue(EmailQueuePayload payload);
Task Dequeue();
}
================================================
FILE: src/PopForums/Repositories/IErrorLogRepository.cs
================================================
namespace PopForums.Repositories;
public interface IErrorLogRepository
{
Task Create(DateTime timeStamp, string message, string stackTrace, string data, ErrorSeverity severity);
Task GetErrorCount();
Task> GetErrors(int startRow, int pageSize);
Task DeleteError(int errorID);
Task DeleteAllErrors();
}
================================================
FILE: src/PopForums/Repositories/IEventDefinitionRepository.cs
================================================
namespace PopForums.Repositories;
public interface IEventDefinitionRepository
{
Task Get(string eventDefinitionID);
Task> GetAll();
Task Create(EventDefinition eventDefinition);
void Delete(string eventDefinitionID);
}
================================================
FILE: src/PopForums/Repositories/IExternalUserAssociationRepository.cs
================================================
namespace PopForums.Repositories;
public interface IExternalUserAssociationRepository
{
Task Get(string issuer, string providerKey);
Task Get(int externalUserAssociationID);
Task> GetByUser(int userID);
Task Save(int userID, string issuer, string providerKey, string name);
Task Delete(int externalUserAssociationID);
}
================================================
FILE: src/PopForums/Repositories/IFavoriteTopicsRepository.cs
================================================
namespace PopForums.Repositories;
public interface IFavoriteTopicsRepository
{
Task> GetFavoriteTopics(int userID, int startRow, int pageSize);
Task GetFavoriteTopicCount(int userID);
Task IsTopicFavorite(int userID, int topicID);
Task AddFavoriteTopic(int userID, int topicID);
Task RemoveFavoriteTopic(int userID, int topicID);
}
================================================
FILE: src/PopForums/Repositories/IFeedRepository.cs
================================================
namespace PopForums.Repositories;
public interface IFeedRepository
{
Task> GetFeed(int userID, int itemCount);
Task PublishEvent(int userID, string message, int points, DateTime timeStamp);
Task GetOldestTime(int userID, int takeCount);
Task DeleteOlderThan(int userID, DateTime timeCutOff);
Task> GetFeed(int itemCount);
}
================================================
FILE: src/PopForums/Repositories/IForumRepository.cs
================================================
namespace PopForums.Repositories;
public interface IForumRepository
{
Task Get(int forumID);
Task Get(string urlName);
Task Create(int? categoryID, string title, string description, bool isVisible, bool isArchived, int sortOrder, string urlName, string forumAdapterName, bool isQAForum);
Task> GetForumsInCategory(int? categoryID);
Task> GetUrlNamesThatStartWith(string urlName);
Task Update(int forumID, int? categoryID, string title, string description, bool isVisible, bool isArchived, string urlName, string forumAdapterName, bool isQAForum);
Task UpdateSortOrder(int forumID, int newSortOrder);
Task UpdateCategoryAssociation(int forumID, int? categoryID);
Task UpdateLastTimeAndUser(int forumID, DateTime lastTime, string lastName);
Task UpdateTopicAndPostCounts(int forumID, int topicCount, int postCount);
Task IncrementPostCount(int forumID);
Task IncrementPostAndTopicCount(int forumID);
Task> GetAll();
Task> GetAllVisible();
///
/// Gets role requirements for the ability to post in this forum.
///
/// This should generally be cached, because the calling code runs this test on every forum when displayed.
/// ID of forum to fetch posting roles for.
/// A List of strings for roles required for posting.
Task> GetForumPostRoles(int forumID);
///
/// Gets role requirements for the ability to view this forum.
///
/// This should generally be cached, because the calling code runs this test on every forum when displayed.
/// ID of forum to fetch roles required for viewing.
/// A List of strings for roles required for viewing.
Task> GetForumViewRoles(int forumID);
///
/// Gets a graph of forums and associated post restrictions.
///
/// This should generally be cached, because the calling code runs this test on every forum when displayed.
/// A dictionary of key/value pairs of forums and post role restrictions.
Task>> GetForumPostRestrictionRoleGraph();
///
/// Gets a graph of forums and associated view restrictions.
///
/// This should generally be cached, because the calling code runs this test on every forum when displayed.
/// A dictionary of key/value pairs of forums and view role restrictions.
Task>> GetForumViewRestrictionRoleGraph();
Task AddPostRole(int forumID, string role);
Task RemovePostRole(int forumID, string role);
Task AddViewRole(int forumID, string role);
Task RemoveViewRole(int forumID, string role);
Task RemoveAllPostRoles(int forumID);
Task RemoveAllViewRoles(int forumID);
///
/// Gets all UrlName values for all forums.
///
/// This should generally be cached, as it's used as a lookup for URL routing on every request.
/// An enumerable object of strings.
Task> GetAllForumUrlNames();
Dictionary GetAllForumTitles();
Task GetAggregateTopicCount();
Task GetAggregatePostCount();
}
================================================
FILE: src/PopForums/Repositories/IIgnoreRepository.cs
================================================
namespace PopForums.Repositories;
public interface IIgnoreRepository
{
Task AddIgnore(int userID, int ignoreUserID);
Task DeleteIgnore(int userID, int ignoreUserID);
Task> GetIgnoreList(int userID);
Task> GetIgnoredUserIdsInList(int userID, List userIDs);
}
================================================
FILE: src/PopForums/Repositories/ILastReadRepository.cs
================================================
namespace PopForums.Repositories;
public interface ILastReadRepository
{
Task SetForumRead(int userID, int forumID, DateTime readTime);
Task DeleteTopicReadsInForum(int userID, int forumID);
Task SetAllForumsRead(int userID, DateTime readTime);
Task DeleteAllTopicReads(int userID);
Task SetTopicRead(int userID, int topicID, DateTime readTime);
Task> GetLastReadTimesForForums(int userID);
Task GetLastReadTimesForForum(int userID, int forumID);
Task> GetLastReadTimesForTopics(int userID, IEnumerable topicIDs);
Task GetLastReadTimeForTopic(int userID, int topicID);
}
================================================
FILE: src/PopForums/Repositories/IModerationLogRepository.cs
================================================
namespace PopForums.Repositories;
public interface IModerationLogRepository
{
Task Log(DateTime timeStamp, int userID, string userName, int moderationType, int? forumID, int topicID, int? postID, string comment, string oldText);
Task> GetLog(DateTime start, DateTime end);
Task> GetLog(int topicID, bool excludePostEntries);
Task> GetLog(int postID);
}
================================================
FILE: src/PopForums/Repositories/INotificationRepository.cs
================================================
namespace PopForums.Repositories;
public interface INotificationRepository
{
Task UpdateNotification(Notification notification);
Task CreateNotification(Notification notification);
Task MarkNotificationRead(int userID, NotificationType notificationType, long contextID);
Task> GetNotifications(int userID, DateTime afterDateTime, int pageSize);
Task GetPageCount(int userID, int pageSize);
Task GetUnreadNotificationCount(int userID);
Task MarkAllRead(int userID);
Task DeleteOlderThan(int userID, DateTime timeCutOff);
}
================================================
FILE: src/PopForums/Repositories/IPointLedgerRepository.cs
================================================
namespace PopForums.Repositories;
public interface IPointLedgerRepository
{
Task RecordEntry(PointLedgerEntry entry);
Task GetPointTotal(int userID);
Task GetEntryCount(int userID, string eventDefinitionID);
}
================================================
FILE: src/PopForums/Repositories/IPostImageRepository.cs
================================================
namespace PopForums.Repositories;
public interface IPostImageRepository
{
Task Persist(byte[] bytes, string contentType);
Task GetWithoutData(string id);
[Obsolete("Use the combination of GetWithoutData(int) and GetImageStream(int) instead.")]
Task Get(string id);
Task DeletePostImageData(string id, string tenantID);
Task GetImageStream(string id);
}
================================================
FILE: src/PopForums/Repositories/IPostImageTempRepository.cs
================================================
namespace PopForums.Repositories;
public interface IPostImageTempRepository
{
Task Save(Guid postImageTempID, DateTime timeStamp, string tenantID);
Task Delete(Guid id);
Task> GetOld(DateTime olderThan);
}
================================================
FILE: src/PopForums/Repositories/IPostRepository.cs
================================================
namespace PopForums.Repositories;
public interface IPostRepository
{
Task Create(int topicID, int parentPostID, string IP, bool isFirstInTopic, bool showSig, int userID, string name, string title, string fullText, DateTime postTime, bool isEdited, string lastEditName, DateTime? lastEditTime, bool isDeleted, int votes);
Task Update(Post post);
Task> Get(int topicID, bool includeDeleted, int startRow, int pageSize);
Task> Get(int topicID, bool includeDeleted);
Task GetReplyCount(int topicID, bool includeDeleted);
Task Get(int postID);
Task> GetPostIDsWithTimes(int topicID, bool includeDeleted);
Task GetPostCount(int userID);
Task> GetIPHistory(string ip, DateTime start, DateTime end);
Task GetLastPostID(int topicID);
Task GetVoteCount(int postID);
Task CalculateVoteCount(int postID);
Task SetVoteCount(int postID, int votes);
Task VotePost(int postID, int userID);
Task> GetVotes(int postID);
Task> GetVotedPostIDs(int userID, List postIDs);
Task GetLastInTopic(int topicID);
Task DeleteVote(int postID, int userID);
}
================================================
FILE: src/PopForums/Repositories/IPrivateMessageRepository.cs
================================================
namespace PopForums.Repositories;
public interface IPrivateMessageRepository
{
Task Get(int pmID, int userID);
Task> GetPosts(int pmID, DateTime afterDateTime);
Task> GetPosts(int pmID, DateTime beforeDateTime, int pageSize);
Task CreatePrivateMessage(PrivateMessage pm);
Task AddUsers(int pmID, List userIDs, DateTime viewDate, bool isArchived);
Task AddPost(PrivateMessagePost post);
Task> GetUsers(int pmID);
Task SetLastViewTime(int pmID, int userID, DateTime viewDate);
Task SetArchive(int pmID, int userID, bool isArchived);
Task> GetPrivateMessages(int userID, PrivateMessageBoxType boxType, int startRow, int pageSize);
Task GetUnreadCount(int userID);
Task GetBoxCount(int userID, PrivateMessageBoxType boxType);
Task UpdateLastPostTime(int pmID, DateTime lastPostTime);
Task GetExistingFromIDs(List ids);
Task GetFirstUnreadPostID(int pmID, DateTime lastReadTime);
}
================================================
FILE: src/PopForums/Repositories/IProfileRepository.cs
================================================
namespace PopForums.Repositories;
public interface IProfileRepository
{
Task GetProfile(int userID);
Task Create(Profile profile);
Task Update(Profile profile);
Task GetLastPostID(int userID);
Task SetLastPostID(int userID, int postID);
Task> GetSignatures(List userIDs);
Task> GetAvatars(List userIDs);
Task SetCurrentImageIDToNull(int userID);
Task UpdatePoints(int userID, int points);
}
================================================
FILE: src/PopForums/Repositories/IQueuedEmailMessageRepository.cs
================================================
namespace PopForums.Repositories;
public interface IQueuedEmailMessageRepository
{
Task CreateMessage(QueuedEmailMessage message);
Task DeleteMessage(int messageID);
Task GetMessage(int messageID);
}
================================================
FILE: src/PopForums/Repositories/IRoleRepository.cs
================================================
namespace PopForums.Repositories;
public interface IRoleRepository
{
Task CreateRole(string role);
Task DeleteRole(string role);
Task> GetAllRoles();
Task> GetUserRoles(int userID);
Task ReplaceUserRoles(int userID, string[] roles);
}
================================================
FILE: src/PopForums/Repositories/ISearchIndexQueueRepository.cs
================================================
namespace PopForums.Repositories;
public interface ISearchIndexQueueRepository
{
Task Enqueue(SearchIndexPayload payload);
Task Dequeue();
}
================================================
FILE: src/PopForums/Repositories/ISearchRepository.cs
================================================
namespace PopForums.Repositories;
public interface ISearchRepository
{
Task> GetJunkWords();
Task CreateJunkWord(string word);
Task DeleteJunkWord(string word);
Task DeleteAllIndexedWordsForTopic(int topicID);
Task SaveSearchWord(int topicID, string word, int rank);
Task>, int>> SearchTopics(string searchTerm, List hiddenForums, SearchType searchType, int startRow, int pageSize);
}
================================================
FILE: src/PopForums/Repositories/ISecurityLogRepository.cs
================================================
namespace PopForums.Repositories;
public interface ISecurityLogRepository
{
Task Create(SecurityLogEntry logEntry);
Task> GetByUserID(int userID, DateTime startDate, DateTime endDate);
Task> GetIPHistory(string ip, DateTime start, DateTime end);
}
================================================
FILE: src/PopForums/Repositories/IServiceHeartbeatRepository.cs
================================================
namespace PopForums.Repositories;
public interface IServiceHeartbeatRepository
{
Task RecordHeartbeat(string serviceName, string machineName, DateTime lastRun);
Task> GetAll();
Task ClearAll();
}
================================================
FILE: src/PopForums/Repositories/ISettingsRepository.cs
================================================
namespace PopForums.Repositories;
public interface ISettingsRepository
{
Dictionary Get();
void Save(Dictionary dictionary);
event Action OnSettingsInvalidated;
}
================================================
FILE: src/PopForums/Repositories/ISetupRepository.cs
================================================
namespace PopForums.Repositories;
public interface ISetupRepository
{
bool IsConnectionPossible();
bool IsDatabaseSetup();
void SetupDatabase();
}
================================================
FILE: src/PopForums/Repositories/ISubscribeNotificationRepository.cs
================================================
namespace PopForums.Repositories;
public interface ISubscribeNotificationRepository
{
Task Enqueue(SubscribeNotificationPayload payload);
Task Dequeue();
}
================================================
FILE: src/PopForums/Repositories/ISubscribedTopicsRepository.cs
================================================
namespace PopForums.Repositories;
public interface ISubscribedTopicsRepository
{
Task