Refactoring - split data, logic and model layers; custom network exception
This commit is contained in:
131
StudentHouseDashboard/Data/AnnouncementRepository.cs
Normal file
131
StudentHouseDashboard/Data/AnnouncementRepository.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
using Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Data
|
||||
{
|
||||
public class AnnouncementRepository
|
||||
{
|
||||
public AnnouncementRepository() { }
|
||||
public List<Announcement> GetAllAnnouncements()
|
||||
{
|
||||
List<Announcement> announcements = new List<Announcement>();
|
||||
UserRepository userRepository = new UserRepository();
|
||||
using (SqlConnection conn = SqlConnectionHelper.CreateConnection())
|
||||
{
|
||||
string sql = "SELECT * FROM Announcements;";
|
||||
SqlCommand cmd = new SqlCommand(sql, conn);
|
||||
var reader = cmd.ExecuteReader();
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
Announcement announcement = new Announcement(Convert.ToInt32(reader["ID"]),
|
||||
userRepository.GetUserById(Convert.ToInt32(reader["Author"])),
|
||||
reader["Description"].ToString(), reader["Title"].ToString(),
|
||||
(DateTime)reader["PublishDate"], (bool)reader["IsImportant"],
|
||||
(bool)reader["IsSticky"]);
|
||||
CommentRepository commentRepository = new CommentRepository();
|
||||
announcement.Comments = commentRepository.GetAllCommentsOnAnnouncement(announcement.ID);
|
||||
// ID, Name, Password, Role
|
||||
announcements.Add(announcement);
|
||||
}
|
||||
conn.Close();
|
||||
}
|
||||
return announcements;
|
||||
}
|
||||
public List<Announcement> GetAnnouncementsByPage(int? p, int? c)
|
||||
{
|
||||
List<Announcement> announcements = new List<Announcement>();
|
||||
UserRepository userRepository = new UserRepository();
|
||||
if (c == null || c < 0)
|
||||
{
|
||||
c = 10;
|
||||
}
|
||||
if (p == null || p < 0)
|
||||
{
|
||||
p = 0;
|
||||
}
|
||||
using (SqlConnection conn = SqlConnectionHelper.CreateConnection())
|
||||
{
|
||||
string sql = "SELECT * FROM Announcements ORDER BY ID OFFSET @start ROWS FETCH NEXT @count ROWS ONLY;";
|
||||
SqlCommand sqlCommand = new SqlCommand(sql, conn);
|
||||
sqlCommand.Parameters.AddWithValue("@start", p * c);
|
||||
sqlCommand.Parameters.AddWithValue("@count", c);
|
||||
var reader = sqlCommand.ExecuteReader();
|
||||
while (reader.Read())
|
||||
{
|
||||
announcements.Add(new Announcement(Convert.ToInt32(reader["ID"]),
|
||||
userRepository.GetUserById(Convert.ToInt32(reader["Author"])),
|
||||
reader["Description"].ToString(), reader["Title"].ToString(),
|
||||
(DateTime)reader["PublishDate"], (bool)reader["IsImportant"],
|
||||
(bool)reader["IsSticky"]));
|
||||
}
|
||||
|
||||
}
|
||||
return announcements;
|
||||
}
|
||||
public bool CreateAnnouncement(string title, string description, User author, DateTime publishDate, bool isImportant, bool isSticky)
|
||||
{
|
||||
using (SqlConnection conn = SqlConnectionHelper.CreateConnection())
|
||||
{
|
||||
string sql = "INSERT INTO Announcements (Author, Description, Title, PublishDate, IsImportant, IsSticky) VALUES (@author, @desc, @title, @date, @important, @sticky);";
|
||||
SqlCommand cmd = new SqlCommand(sql, conn);
|
||||
cmd.Parameters.AddWithValue("@author", author.ID);
|
||||
cmd.Parameters.AddWithValue("@desc", description);
|
||||
cmd.Parameters.AddWithValue("@title", title);
|
||||
cmd.Parameters.AddWithValue("@date", publishDate);
|
||||
cmd.Parameters.AddWithValue("@important", isImportant);
|
||||
cmd.Parameters.AddWithValue("@sticky", isSticky);
|
||||
int writer = cmd.ExecuteNonQuery();
|
||||
|
||||
if (writer == 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
}
|
||||
public bool UpdateAnnouncement(int id, string title, string description, bool isImportant, bool isSticky)
|
||||
{
|
||||
using (SqlConnection conn = SqlConnectionHelper.CreateConnection())
|
||||
{
|
||||
string sql = "UPDATE Announcements " +
|
||||
"SET Description = @desc, Title = @title, IsImportant = @important, IsSticky = @sticky " +
|
||||
"WHERE Id = @id;";
|
||||
SqlCommand cmd = new SqlCommand(sql, conn);
|
||||
cmd.Parameters.AddWithValue("@desc", description);
|
||||
cmd.Parameters.AddWithValue("@title", title);
|
||||
cmd.Parameters.AddWithValue("@important", isImportant);
|
||||
cmd.Parameters.AddWithValue("@sticky", isSticky);
|
||||
cmd.Parameters.AddWithValue("@id", id);
|
||||
int writer = cmd.ExecuteNonQuery();
|
||||
|
||||
if (writer == 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
}
|
||||
public bool DeleteAnnouncement(int id)
|
||||
{
|
||||
using (SqlConnection conn = SqlConnectionHelper.CreateConnection())
|
||||
{
|
||||
string sql = "DELETE FROM Announcements WHERE Id = @id;";
|
||||
SqlCommand cmd = new SqlCommand(sql, conn);
|
||||
cmd.Parameters.AddWithValue("@id", id);
|
||||
int writer = cmd.ExecuteNonQuery();
|
||||
|
||||
if (writer == 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
184
StudentHouseDashboard/Data/CommentRepository.cs
Normal file
184
StudentHouseDashboard/Data/CommentRepository.cs
Normal file
@@ -0,0 +1,184 @@
|
||||
using System.ComponentModel.Design;
|
||||
using System.Data.SqlClient;
|
||||
using Models;
|
||||
|
||||
namespace Data;
|
||||
|
||||
public class CommentRepository
|
||||
{
|
||||
public CommentRepository()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public List<Comment> GetAllCommentsOnAnnouncement(int announcementId)
|
||||
{
|
||||
List<Comment> comments = new List<Comment>();
|
||||
using (SqlConnection connection = SqlConnectionHelper.CreateConnection())
|
||||
{
|
||||
string sql = "SELECT c.ID, c.Author, c.Description, c.Title, c.PublishDate, " +
|
||||
"u.ID UserID, u.Name UserName, u.Password, u.Role FROM AnnouncementsComments ac " +
|
||||
"INNER JOIN Comments c ON c.ID = ac.CommentID " +
|
||||
"INNER JOIN Users u ON u.ID = c.Author " +
|
||||
"WHERE ac.AnnouncementID = @announcementID";
|
||||
SqlCommand sqlCommand = new SqlCommand(sql, connection);
|
||||
sqlCommand.Parameters.AddWithValue("@announcementID", announcementId);
|
||||
var reader = sqlCommand.ExecuteReader();
|
||||
while (reader.Read())
|
||||
{
|
||||
Comment newComment = new Comment((int)reader["ID"],
|
||||
new User((int)reader["UserID"], reader["UserName"].ToString(),
|
||||
reader["Password"].ToString(), (UserRole)reader["Role"]),
|
||||
reader["Description"].ToString(), reader["Title"].ToString(),
|
||||
(DateTime)reader["PublishDate"]);
|
||||
newComment.Responses = GetAllCommentResponses(newComment.ID);
|
||||
comments.Add(newComment);
|
||||
}
|
||||
}
|
||||
|
||||
return comments;
|
||||
}
|
||||
|
||||
public List<Comment> GetAllCommentResponses(int commentId)
|
||||
{
|
||||
List<Comment> responses = new List<Comment>();
|
||||
using (SqlConnection connection = SqlConnectionHelper.CreateConnection())
|
||||
{
|
||||
string sql = "SELECT c.ID, c.Author, c.Description, c.Title, c.PublishDate, " +
|
||||
"u.ID UserID, u.Name UserName, u.Password, u.Role FROM CommentsResponses cr " +
|
||||
"INNER JOIN Comments c ON c.ID = cr.ResponseID " +
|
||||
"INNER JOIN Users u ON u.ID = c.Author " +
|
||||
"WHERE cr.CommentID = @commentID";
|
||||
SqlCommand sqlCommand = new SqlCommand(sql, connection);
|
||||
sqlCommand.Parameters.AddWithValue("@commentID", commentId);
|
||||
var reader = sqlCommand.ExecuteReader();
|
||||
while (reader.Read())
|
||||
{
|
||||
Comment newComment = new Comment((int)reader["ID"],
|
||||
new User((int)reader["UserID"], reader["UserName"].ToString(),
|
||||
reader["Password"].ToString(), (UserRole)reader["Role"]),
|
||||
reader["Description"].ToString(), reader["Title"].ToString(),
|
||||
(DateTime)reader["PublishDate"]);
|
||||
newComment.Responses = GetAllCommentResponses(newComment.ID);
|
||||
responses.Add(newComment);
|
||||
}
|
||||
}
|
||||
|
||||
return responses;
|
||||
}
|
||||
|
||||
public Comment GetCommentById(int id)
|
||||
{
|
||||
using (SqlConnection connection = SqlConnectionHelper.CreateConnection())
|
||||
{
|
||||
string sql = "SELECT c.ID, c.Author, c.Description, c.Title, c.PublishDate, " +
|
||||
"u.ID UserID, u.Name UserName, u.Password, u.Role FROM Comments c " +
|
||||
"INNER JOIN Users u ON u.ID = c.Author " +
|
||||
"WHERE c.ID = @commentID;";
|
||||
SqlCommand sqlCommand = new SqlCommand(sql, connection);
|
||||
sqlCommand.Parameters.AddWithValue("@commentID", id);
|
||||
var reader = sqlCommand.ExecuteReader();
|
||||
reader.Read();
|
||||
Comment newComment = new Comment((int)reader["ID"],
|
||||
new User((int)reader["UserID"], reader["UserName"].ToString(),
|
||||
reader["Password"].ToString(), (UserRole)reader["Role"]),
|
||||
reader["Description"].ToString(), reader["Title"].ToString(),
|
||||
(DateTime)reader["PublishDate"]);
|
||||
newComment.Responses = GetAllCommentResponses(newComment.ID);
|
||||
return newComment;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateComment(int id, string description)
|
||||
{
|
||||
using (SqlConnection connection = SqlConnectionHelper.CreateConnection())
|
||||
{
|
||||
string sql = "UPDATE Comments " +
|
||||
"SET Description = @desc " +
|
||||
"WHERE Id = @id;";
|
||||
SqlCommand cmd = new SqlCommand(sql, connection);
|
||||
cmd.Parameters.AddWithValue("@desc", description);
|
||||
cmd.Parameters.AddWithValue("@id", id);
|
||||
int writer = cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
private Comment CreateComment(User author, string description, string title, DateTime publishDate)
|
||||
{
|
||||
using (SqlConnection connection = SqlConnectionHelper.CreateConnection())
|
||||
{
|
||||
string sql = "INSERT INTO Comments (Author, Description, Title, PublishDate) " +
|
||||
"VALUES (@author, @desc, @title, @date); " +
|
||||
"SELECT SCOPE_IDENTITY();";
|
||||
SqlCommand cmd = new SqlCommand(sql, connection);
|
||||
cmd.Parameters.AddWithValue("@author", author.ID);
|
||||
cmd.Parameters.AddWithValue("@desc", description);
|
||||
cmd.Parameters.AddWithValue("@title", title);
|
||||
cmd.Parameters.AddWithValue("@date", publishDate);
|
||||
return GetCommentById(Convert.ToInt32(cmd.ExecuteScalar()));
|
||||
}
|
||||
}
|
||||
public void CreateCommentOnAnnouncement(User author, string description, string title, DateTime publishDate, int announcementId)
|
||||
{
|
||||
Comment comment = CreateComment(author, description, title, publishDate);
|
||||
using (SqlConnection connection = SqlConnectionHelper.CreateConnection())
|
||||
{
|
||||
string sql = "INSERT INTO AnnouncementsComments (AnnouncementID, CommentID) VALUES (@announcementID, @commentID);";
|
||||
SqlCommand cmd = new SqlCommand(sql, connection);
|
||||
cmd.Parameters.AddWithValue("@announcementID", announcementId);
|
||||
cmd.Parameters.AddWithValue("@commentID", comment.ID);
|
||||
int writer = cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
public void CreateResponseOnComment(User author, string description, string title, DateTime publishDate, int commentId)
|
||||
{
|
||||
Comment response = CreateComment(author, description, title, publishDate);
|
||||
using (SqlConnection connection = SqlConnectionHelper.CreateConnection())
|
||||
{
|
||||
string sql = "INSERT INTO CommentsResponses (CommentID, ResponseID) VALUES (@commentID, @responseID);";
|
||||
SqlCommand cmd = new SqlCommand(sql, connection);
|
||||
cmd.Parameters.AddWithValue("@commentID", commentId);
|
||||
cmd.Parameters.AddWithValue("@responseID", response.ID);
|
||||
int writer = cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteComment(int id)
|
||||
{
|
||||
using (SqlConnection connection = SqlConnectionHelper.CreateConnection())
|
||||
{
|
||||
string sql = "DELETE FROM Comments " +
|
||||
"WHERE Id = @id;";
|
||||
SqlCommand cmd = new SqlCommand(sql, connection);
|
||||
cmd.Parameters.AddWithValue("@id", id);
|
||||
int writer = cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteCommentOnAnnouncement(int commentId, int announcementId)
|
||||
{
|
||||
using (SqlConnection connection = SqlConnectionHelper.CreateConnection())
|
||||
{
|
||||
string sql = "DELETE FROM AnnouncementsComments " +
|
||||
"WHERE CommentID = @commentId AND AnnouncementID = @announcementId";
|
||||
SqlCommand cmd = new SqlCommand(sql, connection);
|
||||
cmd.Parameters.AddWithValue("@commentId", commentId);
|
||||
cmd.Parameters.AddWithValue("@announcementId", announcementId);
|
||||
int writer = cmd.ExecuteNonQuery();
|
||||
}
|
||||
DeleteComment(commentId);
|
||||
}
|
||||
public void DeleteResponseOnComment(int responseId, int commentId)
|
||||
{
|
||||
using (SqlConnection connection = SqlConnectionHelper.CreateConnection())
|
||||
{
|
||||
string sql = "DELETE FROM AnnouncementsComments " +
|
||||
"WHERE CommentID = @commentId AND ResponseID = @responseId";
|
||||
SqlCommand cmd = new SqlCommand(sql, connection);
|
||||
cmd.Parameters.AddWithValue("@commentId", commentId);
|
||||
cmd.Parameters.AddWithValue("@responseId", responseId);
|
||||
int writer = cmd.ExecuteNonQuery();
|
||||
}
|
||||
DeleteComment(commentId);
|
||||
}
|
||||
}
|
17
StudentHouseDashboard/Data/Data.csproj
Normal file
17
StudentHouseDashboard/Data/Data.csproj
Normal file
@@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Data.SqlClient" Version="4.8.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Models\Models.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
20
StudentHouseDashboard/Data/Exceptions.cs
Normal file
20
StudentHouseDashboard/Data/Exceptions.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Data
|
||||
{
|
||||
public class DatabaseNetworkException : WebException
|
||||
{
|
||||
public DatabaseNetworkException(string? message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public DatabaseNetworkException(string? message, Exception? innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
29
StudentHouseDashboard/Data/SqlConnectionHelper.cs
Normal file
29
StudentHouseDashboard/Data/SqlConnectionHelper.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Data
|
||||
{
|
||||
public static class SqlConnectionHelper
|
||||
{
|
||||
private static string connectionString = "Server=mssqlstud.fhict.local;Database=dbi509645;User Id=dbi509645;Password=sNPNBm*BX!6z8RM;";
|
||||
public static SqlConnection CreateConnection()
|
||||
{
|
||||
SqlConnection connection = new SqlConnection(connectionString);
|
||||
try
|
||||
{
|
||||
connection.Open();
|
||||
}
|
||||
catch (SqlException e)
|
||||
{
|
||||
throw new DatabaseNetworkException("Unable to access FHICT VDI database", e);
|
||||
// Console.WriteLine("Database connection error. Are you connected to the VDI VPN?");
|
||||
}
|
||||
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
}
|
142
StudentHouseDashboard/Data/UserRepository.cs
Normal file
142
StudentHouseDashboard/Data/UserRepository.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Data.SqlClient;
|
||||
using Models;
|
||||
using System.Data;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Data
|
||||
{
|
||||
public class UserRepository
|
||||
{
|
||||
public UserRepository() { }
|
||||
public List<User> GetAllUsers()
|
||||
{
|
||||
var users = new List<User>();
|
||||
|
||||
using (SqlConnection conn = SqlConnectionHelper.CreateConnection())
|
||||
{
|
||||
string sql = "SELECT * FROM Users;";
|
||||
SqlCommand cmd = new SqlCommand(sql, conn);
|
||||
var reader = cmd.ExecuteReader();
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
// ID, Name, Password, Role
|
||||
users.Add(new User(Convert.ToInt32(reader["ID"]),
|
||||
reader["Name"].ToString(),
|
||||
reader["Password"].ToString(),
|
||||
(UserRole)reader["Role"])
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
return users;
|
||||
}
|
||||
public User GetUserById(int id)
|
||||
{
|
||||
using (SqlConnection conn = SqlConnectionHelper.CreateConnection())
|
||||
{
|
||||
string sql = "SELECT * FROM Users WHERE ID = @id;";
|
||||
SqlCommand cmd = new SqlCommand(sql, conn);
|
||||
cmd.Parameters.AddWithValue("@id", id);
|
||||
var reader = cmd.ExecuteReader();
|
||||
|
||||
reader.Read();
|
||||
|
||||
// ID, Name, Password, Role
|
||||
return new User(Convert.ToInt32(reader["ID"]),
|
||||
reader["Name"].ToString(),
|
||||
reader["Password"].ToString(),
|
||||
(UserRole)reader["Role"]);
|
||||
}
|
||||
}
|
||||
public List<User> GetUsersByPage(int? p, int? c)
|
||||
{
|
||||
List<User> users = new List<User>();
|
||||
if (c == null || c < 0)
|
||||
{
|
||||
c = 10;
|
||||
}
|
||||
if (p == null || p < 0)
|
||||
{
|
||||
p = 0;
|
||||
}
|
||||
using (SqlConnection conn = SqlConnectionHelper.CreateConnection())
|
||||
{
|
||||
string sql = "SELECT * FROM Users ORDER BY ID OFFSET @start ROWS FETCH NEXT @count ROWS ONLY;";
|
||||
SqlCommand sqlCommand = new SqlCommand(sql, conn);
|
||||
sqlCommand.Parameters.AddWithValue("@start", p * c);
|
||||
sqlCommand.Parameters.AddWithValue("@count", c);
|
||||
var reader = sqlCommand.ExecuteReader();
|
||||
while (reader.Read())
|
||||
{
|
||||
users.Add(new User(Convert.ToInt32(reader["ID"]), reader["Name"].ToString(),
|
||||
reader["Password"].ToString(), (UserRole)reader["Role"]));
|
||||
}
|
||||
|
||||
}
|
||||
return users;
|
||||
}
|
||||
public bool CreateUser(string name, string password, UserRole role)
|
||||
{
|
||||
using (SqlConnection conn = SqlConnectionHelper.CreateConnection())
|
||||
{
|
||||
string sql = "INSERT INTO Users (Name, Password, Role) VALUES (@name, @pass, @role);";
|
||||
SqlCommand cmd = new SqlCommand(sql, conn);
|
||||
cmd.Parameters.AddWithValue("@name", name);
|
||||
cmd.Parameters.AddWithValue("@pass", password);
|
||||
cmd.Parameters.AddWithValue("@role", (int)role);
|
||||
int writer = cmd.ExecuteNonQuery();
|
||||
|
||||
if (writer == 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
}
|
||||
public bool UpdateUser(int id, string name, string password, UserRole role)
|
||||
{
|
||||
using (SqlConnection conn = SqlConnectionHelper.CreateConnection())
|
||||
{
|
||||
string sql = "UPDATE Users " +
|
||||
"SET Name = @name, Password = @pass, Role = @role " +
|
||||
"WHERE ID = @id;";
|
||||
SqlCommand cmd = new SqlCommand(sql, conn);
|
||||
cmd.Parameters.AddWithValue("@name", name);
|
||||
cmd.Parameters.AddWithValue("@pass", password);
|
||||
cmd.Parameters.AddWithValue("@role", (int)role);
|
||||
cmd.Parameters.AddWithValue("@id", id);
|
||||
int writer = cmd.ExecuteNonQuery();
|
||||
|
||||
if (writer == 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
}
|
||||
public bool DisableUser(int id)
|
||||
{
|
||||
using (SqlConnection conn = SqlConnectionHelper.CreateConnection())
|
||||
{
|
||||
string sql = "UPDATE Users " +
|
||||
"SET Name = 'Deleted User @id', Password = '0'" +
|
||||
"WHERE ID = @id;";
|
||||
SqlCommand cmd = new SqlCommand(sql, conn);
|
||||
cmd.Parameters.AddWithValue("@id", id);
|
||||
int writer = cmd.ExecuteNonQuery();
|
||||
|
||||
if (writer == 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user