diff --git a/Library.Encyclopedia.API/Controllers/CommentController.cs b/Library.Encyclopedia.API/Controllers/CommentController.cs new file mode 100644 index 0000000..7152f3d --- /dev/null +++ b/Library.Encyclopedia.API/Controllers/CommentController.cs @@ -0,0 +1,197 @@ +using Library.Encyclopedia.DataAccess; +using Library.Encyclopedia.DataAccess.DataAccess; +using Library.Encyclopedia.Entity.Interfaces; +using Library.Encyclopedia.Entity.Models; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.DirectoryServices.AccountManagement; +using System.Linq; +using System.Threading.Tasks; + +namespace Library.Encyclopedia.API.Controllers +{ + [ApiController] + [Route("[controller]")] + public class CommentController : ControllerBase + { + private readonly ILogger _logger; + private readonly ICommentsDataAccess dataAccess; + private readonly ILikesDataAccess likesDataAccess; + + public CommentController(ILogger logger, IApplicationDbContext dbContext) + { + this._logger = logger; + dataAccess = new CommentsDataAccess(dbContext); + likesDataAccess = new LikesDataAccess(dbContext); + } + + [HttpGet] + [Authorize(Roles = @"EncyclopediaAdministrators")] + public async Task Get(int offset = 0, int limit = 10, bool asc = false) + { + try + { + var result = await dataAccess.Get(offset, limit, asc); + var likes = await likesDataAccess.GetLikes(result.Result.Select(s => s.Id).ToList()); + + if (likes != null) + { + foreach (var item in result.Result) + item.Likes = likes.ContainsKey(item.Id) ? likes[item.Id] : 0; + } + + if (result != null) + return Ok(result); + else + return StatusCode(500); + } + catch (Exception ex) + { + _logger.LogError(ex, $"an error has occured {ex.Message}"); + throw; + } + } + + [HttpPost("Resolve/{id}")] + [Authorize(Roles = @"EncyclopediaAdministrators")] + public async Task ResolveComment(Guid id, bool flag = true) + { + try + { + await dataAccess.Resolve(id, flag); + + return Ok(); + } + catch (Exception ex) + { + _logger.LogError(ex, $"an error has occured {ex.Message}"); + throw; + } + } + + [HttpGet("{id}")] + public async Task Get(Guid id) + { + try + { + var result = await dataAccess.Get(id); + var likes = await likesDataAccess.GetLikes(result.Select(s => s.Id).ToList()); + + if (likes != null) + { + foreach (var item in result) + item.Likes = likes.ContainsKey(item.Id) ? likes[item.Id] : 0; + } + + if (result != null) + return Ok(result); + else + return StatusCode(500); + } + catch (Exception ex) + { + _logger.LogError(ex, $"an error has occured {ex.Message}"); + throw; + } + } + + [HttpPost] + [Authorize] + public async Task Post([FromBody]Comments comments) + { + try + { + using (var context = new PrincipalContext(ContextType.Domain | ContextType.Machine)) + { + var usr = UserPrincipal.FindByIdentity(context, this.HttpContext.User.Identity.Name); + comments.UserName = usr.DisplayName; + comments.UserSid = usr.Sid.Value; + + var result = await dataAccess.CreateComment(comments); + + if (result != null) + return Ok(result); + else + return StatusCode(500); + } + } + catch (Exception ex) + { + _logger.LogError(ex, $"an error has occured {ex.Message}"); + throw; + } + } + + [HttpDelete("{id}")] + [Authorize] + public async Task Delete(Guid id) + { + try + { + using (var context = new PrincipalContext(ContextType.Domain | ContextType.Machine)) + { + var usr = UserPrincipal.FindByIdentity(context, this.HttpContext.User.Identity.Name); + + if (usr.GetAuthorizationGroups().Select(s => s.Name).Contains("EncyclopediaAdministrators")) + await dataAccess.DeleteComment(id, string.Empty, true); + else + await dataAccess.DeleteComment(id, usr.Sid.Value); + + return Ok(); + } + } + catch (Exception ex) + { + _logger.LogError(ex, $"an error has occured {ex.Message}"); + throw; + } + } + + [HttpPut("{id}")] + [Authorize] + public async Task Update(Guid id, string comment) + { + try + { + using (var context = new PrincipalContext(ContextType.Domain | ContextType.Machine)) + { + var usr = UserPrincipal.FindByIdentity(context, this.HttpContext.User.Identity.Name); + + await dataAccess.UpdateComment(id, comment, usr.Sid.Value); + + return Ok(); + } + } + catch (Exception ex) + { + _logger.LogError(ex, $"an error has occured {ex.Message}"); + throw; + } + } + + [HttpPost("Like/{id}")] + [Authorize] + public async Task LikeComment(Guid id, bool flag = true) + { + try + { + using (var context = new PrincipalContext(ContextType.Domain | ContextType.Machine)) + { + var usr = UserPrincipal.FindByIdentity(context, this.HttpContext.User.Identity.Name); + + await likesDataAccess.Like(id, usr.Sid.Value, flag); + + return Ok(); + } + } + catch (Exception ex) + { + _logger.LogError(ex, $"an error has occured {ex.Message}"); + throw; + } + } + } +} diff --git a/Library.Encyclopedia.DataAccess/ApplicationDbContext.cs b/Library.Encyclopedia.DataAccess/ApplicationDbContext.cs index 0c6069e..35eb7ec 100644 --- a/Library.Encyclopedia.DataAccess/ApplicationDbContext.cs +++ b/Library.Encyclopedia.DataAccess/ApplicationDbContext.cs @@ -26,15 +26,12 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) public DbSet Files { get; set; } public DbSet Links { get; set; } public DbSet QueryStats { get; set; } + public DbSet Comments { get; set; } + public DbSet Likes { get; set; } public new async Task SaveChanges() { return await base.SaveChangesAsync(); } - - public async Task BeginTransactionAsync() - { - return await base.Database.BeginTransactionAsync(); - } } } \ No newline at end of file diff --git a/Library.Encyclopedia.DataAccess/DataAccess/CommentsDataAccess.cs b/Library.Encyclopedia.DataAccess/DataAccess/CommentsDataAccess.cs new file mode 100644 index 0000000..73aca2c --- /dev/null +++ b/Library.Encyclopedia.DataAccess/DataAccess/CommentsDataAccess.cs @@ -0,0 +1,106 @@ +using Library.Encyclopedia.Entity.Interfaces; +using Library.Encyclopedia.Entity.Models; +using Library.Encyclopedia.Entity.Models.External; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Library.Encyclopedia.DataAccess.DataAccess +{ + public class CommentsDataAccess : ICommentsDataAccess + { + private readonly IApplicationDbContext dbContext; + + public CommentsDataAccess(IApplicationDbContext dbContext) + { + this.dbContext = dbContext; + } + + public async Task CreateComment(Comments comments) + { + comments.Id = Guid.NewGuid(); + comments.PostedTime = DateTime.Now; + + await dbContext.Comments.AddAsync(comments); + await dbContext.SaveChanges(); + + return comments; + } + + public async Task DeleteComment(Guid id, string userSid, bool isAdmin = false) + { + var item = await dbContext.Comments.FirstOrDefaultAsync(s => s.Id == id); + + if (item != null) + { + if (!isAdmin) + { + if (item.UserSid == userSid) + { + dbContext.Comments.Remove(item); + dbContext.Likes.RemoveRange(await dbContext.Likes.Where(s=>s.CommentId == item.Id).ToListAsync()); + await dbContext.SaveChanges(); + } + else + throw new Exception("This user did not post this comment!"); + } + else + { + dbContext.Comments.Remove(item); + dbContext.Likes.RemoveRange(await dbContext.Likes.Where(s => s.CommentId == item.Id).ToListAsync()); + await dbContext.SaveChanges(); + } + } + else + throw new Exception("Comment not found!"); + } + + public async Task Get(int offset = 0, int limit = 10, bool asc = false) + { + if(asc) + return new CommentsExternalCollection(await dbContext.Comments.OrderBy(s => s.PostedTime).Skip(offset).Take(limit).ToListAsync(), await dbContext.Comments.CountAsync()); + else + return new CommentsExternalCollection(await dbContext.Comments.OrderByDescending(s => s.PostedTime).Skip(offset).Take(limit).ToListAsync(), await dbContext.Comments.CountAsync()); + } + + public async Task> Get(Guid mainId) + { + return await dbContext.Comments.Where(s => s.MainId == mainId).OrderByDescending(s => s.PostedTime).ToListAsync(); + } + + public async Task Resolve(Guid id, bool flag = true) + { + var comment = await dbContext.Comments.FirstOrDefaultAsync(s => s.Id == id); + + if(comment != null) + { + comment.IsResolved = flag; + dbContext.Comments.Update(comment); + await dbContext.SaveChanges(); + } + } + + public async Task UpdateComment(Guid id, string comment, string userSid) + { + var commentObj = await dbContext.Comments.FirstOrDefaultAsync(s => s.Id == id); + + if (commentObj != null) + { + if (commentObj.UserSid == userSid) + { + commentObj.Comment = comment; + dbContext.Comments.Update(commentObj); + await dbContext.SaveChanges(); + } + else + throw new Exception("This user did not post this comment!"); + } + else + throw new Exception("Comment not found!"); + + } + } +} diff --git a/Library.Encyclopedia.DataAccess/DataAccess/LikesDataAccess.cs b/Library.Encyclopedia.DataAccess/DataAccess/LikesDataAccess.cs new file mode 100644 index 0000000..d5760b5 --- /dev/null +++ b/Library.Encyclopedia.DataAccess/DataAccess/LikesDataAccess.cs @@ -0,0 +1,54 @@ +using Library.Encyclopedia.Entity.Interfaces; +using Library.Encyclopedia.Entity.Models; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Library.Encyclopedia.DataAccess.DataAccess +{ + public class LikesDataAccess : ILikesDataAccess + { + private readonly IApplicationDbContext dbContext; + + public LikesDataAccess(IApplicationDbContext dbContext) + { + this.dbContext = dbContext; + } + + public Task DeleteLikes(List commentIds) + { + throw new NotImplementedException(); + } + + public async Task> GetLikes(List commentIds) + { + return (await dbContext.Likes.Where(s => commentIds.Contains(s.CommentId)).ToListAsync()).GroupBy(s => s.CommentId).ToDictionary(s => s.Key, s => s.LongCount()); + } + + public async Task Like(Guid commentId, string userSid, bool flag = true) + { + var result = await dbContext.Likes.FirstOrDefaultAsync(s => s.CommentId == commentId && s.UserSid == userSid); + if (result != null) + { + // if disliked + if (!flag) + { + dbContext.Likes.Remove(result); + await dbContext.SaveChanges(); + } + else throw new Exception("Already Liked"); + } + else + { + if (flag) + { + await dbContext.Likes.AddAsync(new Likes { CommentId = commentId, UserSid = userSid }); + await dbContext.SaveChanges(); + } + } + } + } +} diff --git a/Library.Encyclopedia.DataAccess/DataAccess/MainDataAccess.cs b/Library.Encyclopedia.DataAccess/DataAccess/MainDataAccess.cs index 95ccfe9..4eae680 100644 --- a/Library.Encyclopedia.DataAccess/DataAccess/MainDataAccess.cs +++ b/Library.Encyclopedia.DataAccess/DataAccess/MainDataAccess.cs @@ -60,7 +60,7 @@ async Task IMainDataAccess.GetAsync(string quer MainMinimizedExternalCollection result = new MainMinimizedExternalCollection(data.MinimizeWithQuery(query, previewSize), total); if (!string.IsNullOrEmpty(query) && !string.IsNullOrWhiteSpace(query) && total > 0 && query.Length >= 3) - this.queryStatsAdapter.AddQuery(query); + await this.queryStatsAdapter.AddQuery(query); return result; } @@ -430,6 +430,13 @@ public async Task DeleteAsync(Guid id) this.filesAdapter.DeleteFiles(entry.Id.ToString()); } _dbcontext.Main.Remove(entry); + + // delete all comments and likes + var allComments = await _dbcontext.Comments.Where(s => s.MainId == entry.Id).ToListAsync(); + _dbcontext.Comments.RemoveRange(allComments); + var allCommentIds = allComments.Select(s => s.Id); + _dbcontext.Likes.RemoveRange(await _dbcontext.Likes.Where(s => allCommentIds.Contains(s.CommentId)).ToListAsync()); + await _dbcontext.SaveChanges(); } else diff --git a/Library.Encyclopedia.DataAccess/IApplicationDbContext.cs b/Library.Encyclopedia.DataAccess/IApplicationDbContext.cs index fb924c7..562475d 100644 --- a/Library.Encyclopedia.DataAccess/IApplicationDbContext.cs +++ b/Library.Encyclopedia.DataAccess/IApplicationDbContext.cs @@ -11,7 +11,9 @@ public interface IApplicationDbContext DbSet Files { get; set; } DbSet Links { get; set; } DbSet QueryStats { get; set; } + DbSet Comments { get; set; } + DbSet Likes { get; set; } + Task SaveChanges(); - Task BeginTransactionAsync(); } } \ No newline at end of file diff --git a/Library.Encyclopedia.Entity/Interfaces/ICommentsDataAccess.cs b/Library.Encyclopedia.Entity/Interfaces/ICommentsDataAccess.cs new file mode 100644 index 0000000..6275b6f --- /dev/null +++ b/Library.Encyclopedia.Entity/Interfaces/ICommentsDataAccess.cs @@ -0,0 +1,19 @@ +using Library.Encyclopedia.Entity.Models; +using Library.Encyclopedia.Entity.Models.External; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace Library.Encyclopedia.Entity.Interfaces +{ + public interface ICommentsDataAccess + { + Task Get(int offset = 0, int limit = 10, bool asc = false); + Task> Get(Guid mainId); + Task CreateComment(Comments comments); + Task UpdateComment(Guid id, string comment, string userSid); + Task DeleteComment(Guid id, string userSid, bool isAdmin = false); + Task Resolve(Guid id, bool flag = true); + } +} diff --git a/Library.Encyclopedia.Entity/Interfaces/ILikesDataAccess.cs b/Library.Encyclopedia.Entity/Interfaces/ILikesDataAccess.cs new file mode 100644 index 0000000..ffe27c9 --- /dev/null +++ b/Library.Encyclopedia.Entity/Interfaces/ILikesDataAccess.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace Library.Encyclopedia.Entity.Interfaces +{ + public interface ILikesDataAccess + { + Task Like(Guid commentId, string userSid, bool flag = true); + Task> GetLikes(List commentIds); + Task DeleteLikes(List commentIds); + } +} diff --git a/Library.Encyclopedia.Entity/Models/Comments.cs b/Library.Encyclopedia.Entity/Models/Comments.cs new file mode 100644 index 0000000..a1f17c1 --- /dev/null +++ b/Library.Encyclopedia.Entity/Models/Comments.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Text; + +namespace Library.Encyclopedia.Entity.Models +{ + public class Comments + { + public Guid Id { get; set; } + public Guid MainId { get; set; } + public string Comment { get; set; } + public DateTime PostedTime { get; set; } + public string UserName { get; set; } + public string UserSid { get; set; } + public long Likes { get; set; } + public bool IsResolved { get; set; } + } +} diff --git a/Library.Encyclopedia.Entity/Models/External/CommentsExternalCollection.cs b/Library.Encyclopedia.Entity/Models/External/CommentsExternalCollection.cs new file mode 100644 index 0000000..3d2dbec --- /dev/null +++ b/Library.Encyclopedia.Entity/Models/External/CommentsExternalCollection.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Library.Encyclopedia.Entity.Models.External +{ + + public class CommentsExternalCollection : QueryExternalModel + { + public CommentsExternalCollection() + { + this.Count = 0; + this.Result = new List(); + this.Total = 0; + } + public CommentsExternalCollection(List collection, int total) + { + this.Result = collection; + this.Count = collection.Count(); + this.Total = total; + } + } +} diff --git a/Library.Encyclopedia.Entity/Models/Likes.cs b/Library.Encyclopedia.Entity/Models/Likes.cs new file mode 100644 index 0000000..eab4562 --- /dev/null +++ b/Library.Encyclopedia.Entity/Models/Likes.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Library.Encyclopedia.Entity.Models +{ + public class Likes + { + public long Id { get; set; } + public Guid CommentId { get; set; } + public string UserSid { get; set; } + } +}