diff --git a/BlogIT.DB/BL/EmailService.cs b/BlogIT.DB/BL/EmailService.cs new file mode 100644 index 0000000..2525448 --- /dev/null +++ b/BlogIT.DB/BL/EmailService.cs @@ -0,0 +1,31 @@ +using MimeKit; +using MailKit.Net.Smtp; +using System.Threading.Tasks; + +namespace BlogIT.DB.BL +{ + public class EmailService : IEmailService + { + public async Task SendEmailAsync(string email, string subject, string message) + { + var emailMessage = new MimeMessage(); + + emailMessage.From.Add(new MailboxAddress("Администрация сайта", "JIeHIH12345@mail.ru")); + emailMessage.To.Add(new MailboxAddress("", email)); + emailMessage.Subject = subject; + emailMessage.Body = new TextPart(MimeKit.Text.TextFormat.Html) + { + Text = message + }; + + using (var client = new SmtpClient()) + { + await client.ConnectAsync("smtp.mail.ru", 25, false); + await client.AuthenticateAsync("JIeHIH12345@mail.ru", "*********"); + await client.SendAsync(emailMessage); + + await client.DisconnectAsync(true); + } + } + } +} diff --git a/BlogIT.DB/BL/IEmailService.cs b/BlogIT.DB/BL/IEmailService.cs new file mode 100644 index 0000000..b5020bd --- /dev/null +++ b/BlogIT.DB/BL/IEmailService.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace BlogIT.DB.BL +{ + public interface IEmailService + { + Task SendEmailAsync(string email, string subject, string message); + } +} diff --git a/BlogIT.DB/BL/INewsService.cs b/BlogIT.DB/BL/INewsService.cs index c9329b3..857dea4 100644 --- a/BlogIT.DB/BL/INewsService.cs +++ b/BlogIT.DB/BL/INewsService.cs @@ -1,24 +1,27 @@ - +using BlogIT.DB.Interfaces; using BlogIT.DB.Models; -using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; namespace BlogIT.DB.BL { public interface INewsService { - IQueryable ListAll(); List GetCategories(); int AddNews(News news); News GetNewsById(int id); void DeleteNewsById(int id); void AddMessageChat(ChatMessage chatMessage); - IQueryable GetChatMessagesByPartyId(int partyId); - IQueryable GetLastNews(int count); - IQueryable GetTags(string tag); - IQueryable GetTopNews(int count); - IQueryable ListActualNews(bool includeChatMessage = false); + List GetChatMessagesByPartyId(int partyId); + List GetLastNews(int count); + List GetTags(string tag); + List GetTopNews(int count); void UpdateNews(News news); + void SetRating(Rating rating); + int GetCurrentUserRating(int newsId, string userId); + List GetTopTags(); + Task> ListNewsAsync(ISpecification spec); + Task CountNewsAsync(ISpecification spec); } } diff --git a/BlogIT.DB/BL/NewsService.cs b/BlogIT.DB/BL/NewsService.cs index 3428c5f..77a4ea4 100644 --- a/BlogIT.DB/BL/NewsService.cs +++ b/BlogIT.DB/BL/NewsService.cs @@ -1,10 +1,13 @@ using BlogIT.DB.DAL; +using BlogIT.DB.Interfaces; using BlogIT.DB.Models; +using BlogIT.DB.Specifications; using Microsoft.EntityFrameworkCore; using System; using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; namespace BlogIT.DB.BL { @@ -51,10 +54,6 @@ public News GetNewsById(int id) .SingleOrDefault(p => p.Id == id); } - public IQueryable ListAll() - { - return _context.News.Where(x => !x.Deleted); - } public void AddMessageChat(ChatMessage chatMessage) { @@ -62,45 +61,41 @@ public void AddMessageChat(ChatMessage chatMessage) _context.SaveChanges(); } - public IQueryable GetChatMessagesByPartyId(int newsId) + public List GetChatMessagesByPartyId(int newsId) { return _context.ChatMessages .Where(c => c.NewsId == newsId) .Include(i => i.User) .ThenInclude(i => i.Avatar) - .Include(i => i.Like); + .Include(i => i.Like) + .ToList(); } - public IQueryable GetLastNews(int count) + public List GetLastNews(int count) { return _context.News .Where(x => x.DateTime <= DateTime.Now && !x.Deleted) .OrderByDescending(s => s.DateTime) .Take(count) .Include(i => i.Category) - .Include(i => i.ChatMessages); + .Include(i => i.ChatMessages) + .ToList(); } - public IQueryable GetTopNews(int count) + public List GetTopNews(int count) { return _context.News + .OrderByDescending(p => p.RateAverage) + .ThenByDescending(p => p.RateCount) + .ThenByDescending(p => p.DateTime) .Take(count) - .Include(i => i.Category); - } - - public IQueryable ListActualNews(bool includeChatMessage = false) - { - IQueryable news = ListAll().Where(p => p.DateTime <= DateTime.Now); - if (includeChatMessage) - { - news = news.Include(i => i.ChatMessages); - } - return news; + .Include(i => i.Category) + .ToList(); } public void UpdateNews(News news) { - News updateNews = _context.News.Find(news.Id); + News updateNews = _context.News.Include(p => p.NewsTag).FirstOrDefault(p => p.Id == news.Id); if (updateNews != null) { @@ -110,7 +105,7 @@ public void UpdateNews(News news) updateNews.NewsText = news.NewsText; updateNews.CategoryId = news.CategoryId; - if(updateNews.Tags != news.Tags) + if (updateNews.Tags != news.Tags) { updateNews.Tags = news.Tags; UpdateNewsTags(updateNews); @@ -148,9 +143,81 @@ private void UpdateNewsTags(News news) }; } - public IQueryable GetTags(string tag) + public List GetTags(string tag) + { + return _context.Tags + .Where(p => p.Title.Contains(tag)) + .Take(10) + .ToList(); + } + + public void SetRating(Rating rating) + { + Rating updateRating = _context.Ratings.FirstOrDefault(p => p.NewsId == rating.NewsId && p.UserId == rating.UserId); + + if (updateRating != null) + { + updateRating.Rate = rating.Rate; + _context.Ratings.Update(updateRating); + } + else + { + _context.Ratings.Add(rating); + } + _context.SaveChanges(); + + UpdateNewsRating(rating.NewsId); + + } + + private void UpdateNewsRating(int newsId) + { + + News news = _context.News.Include(p => p.Ratings).SingleOrDefault(p => p.Id == newsId); + + if (news != null) + { + news.RateCount = news.Ratings.Count; + news.RateAverage = news.RateCount == 0 ? 0 : news.Ratings.Sum(m => m.Rate)/ news.RateCount; + _context.News.Update(news); + _context.SaveChanges(); + + } + } + + public int GetCurrentUserRating(int newsId, string userId) + { + if(!String.IsNullOrEmpty(userId)) + { + Rating rating = _context.Ratings.FirstOrDefault(p => p.NewsId == newsId && p.UserId == userId); + + if (rating != null) + { + return rating.Rate; + } + } + + return 0; + } + + public List GetTopTags() + { + return _context.Tags.Where(p => p.NewsTag.Count > 0).OrderByDescending(p => p.NewsTag.Count).Take(10).Select(p => p.Title).ToList(); + } + + public async Task> ListNewsAsync(ISpecification spec) + { + return await ApplySpecification(spec).ToListAsync(); + } + + public async Task CountNewsAsync(ISpecification spec) + { + return await ApplySpecification(spec).CountAsync(); + } + + private IQueryable ApplySpecification(ISpecification spec) { - return _context.Tags.Where(p => p.Title.Contains(tag)).Take(10); + return SpecificationEvaluator.GetQuery(_context.News.AsQueryable(), spec); } } } diff --git a/BlogIT.DB/BlogIT.DB.csproj b/BlogIT.DB/BlogIT.DB.csproj index af7e06e..3706415 100644 --- a/BlogIT.DB/BlogIT.DB.csproj +++ b/BlogIT.DB/BlogIT.DB.csproj @@ -20,6 +20,7 @@ + diff --git a/BlogIT.DB/DAL/BlogITContext.cs b/BlogIT.DB/DAL/BlogITContext.cs index cac690a..e1b7696 100644 --- a/BlogIT.DB/DAL/BlogITContext.cs +++ b/BlogIT.DB/DAL/BlogITContext.cs @@ -20,6 +20,7 @@ public BlogITContext(DbContextOptions options) public DbSet Tags { get; set; } public DbSet NewsTags { get; set; } public DbSet Likes { get; set; } + public DbSet Ratings { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -33,6 +34,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.ApplyConfiguration(new TagsConfiguration()); modelBuilder.ApplyConfiguration(new NewsTagsConfiguration()); modelBuilder.ApplyConfiguration(new LikeConfiguration()); + modelBuilder.ApplyConfiguration(new RatingConfiguration()); Initialization(modelBuilder); } @@ -94,6 +96,9 @@ public void Configure(EntityTypeBuilder builder) { builder.Property(p => p.Sex).IsRequired(); builder.Property(p => p.Birthday).IsRequired(); + builder.Property(p => p.Description).IsRequired(false).HasMaxLength(255); + builder.Property(p => p.ShortDescription).IsRequired(false).HasMaxLength(1024); + builder.Property(p => p.DateOfRegistration).IsRequired(); builder.HasOne(p => p.Avatar).WithOne().HasForeignKey(p => p.AvatarId); } } @@ -118,6 +123,8 @@ public void Configure(EntityTypeBuilder builder) builder.Property(p => p.Tags).IsRequired().HasMaxLength(1024); builder.Property(p => p.NewsText).IsRequired().HasMaxLength(10240); builder.Property(p => p.Deleted).IsRequired().HasDefaultValue(false); + builder.Property(p => p.RateCount).IsRequired().HasDefaultValue(0); + builder.Property(p => p.RateAverage).IsRequired().HasDefaultValue(0); builder.HasOne(p => p.Category) .WithMany(t => t.News) .HasForeignKey(p => p.CategoryId); @@ -186,6 +193,22 @@ public void Configure(EntityTypeBuilder builder) .WithMany(t => t.Like) .HasForeignKey(p => p.ChatMessageId); } + } -} + public class RatingConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(p => p.Id); + builder.Property(p => p.Rate).IsRequired().HasDefaultValue(0); + builder.HasOne(p => p.User) + .WithMany(t => t.Ratings) + .HasForeignKey(p => p.UserId); + builder.HasOne(p => p.News) + .WithMany(t => t.Ratings) + .HasForeignKey(p => p.NewsId); + } + } + +} \ No newline at end of file diff --git a/BlogIT.DB/Interfaces/ISpecification.cs b/BlogIT.DB/Interfaces/ISpecification.cs new file mode 100644 index 0000000..b3599e7 --- /dev/null +++ b/BlogIT.DB/Interfaces/ISpecification.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Text; + +namespace BlogIT.DB.Interfaces +{ + public interface ISpecification + { + Expression> Criteria { get; } + List>> Includes { get; } + List IncludeStrings { get; } + Expression> OrderBy { get; } + Expression> OrderByDescending { get; } + Expression> GroupBy { get; } + + int Take { get; } + int Skip { get; } + bool IsPagingEnabled { get; } + } +} diff --git a/BlogIT.DB/Migrations/20200120135058_AddRating.Designer.cs b/BlogIT.DB/Migrations/20200120135058_AddRating.Designer.cs new file mode 100644 index 0000000..669bf78 --- /dev/null +++ b/BlogIT.DB/Migrations/20200120135058_AddRating.Designer.cs @@ -0,0 +1,654 @@ +// +using System; +using BlogIT.DB.DAL; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace BlogIT.DB.Migrations +{ + [DbContext(typeof(BlogITContext))] + [Migration("20200120135058_AddRating")] + partial class AddRating + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.0") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("BlogIT.DB.Models.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.ToTable("Categories"); + + b.HasData( + new + { + Id = 1, + Title = "Java" + }, + new + { + Id = 2, + Title = "C#" + }, + new + { + Id = 3, + Title = "C++" + }, + new + { + Id = 4, + Title = "Algorithms" + }, + new + { + Id = 5, + Title = "Machine Learning" + }); + }); + + modelBuilder.Entity("BlogIT.DB.Models.ChatMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Date") + .HasColumnType("datetime2"); + + b.Property("Message") + .IsRequired() + .HasColumnType("nvarchar(1024)") + .HasMaxLength(1024); + + b.Property("NewsId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("NewsId"); + + b.HasIndex("UserId"); + + b.ToTable("ChatMessages"); + }); + + modelBuilder.Entity("BlogIT.DB.Models.FileModel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("Path") + .IsRequired() + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.ToTable("Files"); + }); + + modelBuilder.Entity("BlogIT.DB.Models.Like", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ChatMessageId") + .HasColumnType("int"); + + b.Property("LikeCount") + .HasColumnType("int"); + + b.Property("LikeDown") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("LikeUp") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("ChatMessageId"); + + b.HasIndex("UserId"); + + b.ToTable("Likes"); + }); + + modelBuilder.Entity("BlogIT.DB.Models.News", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("DateTime") + .HasColumnType("datetime2"); + + b.Property("Deleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(1024)") + .HasMaxLength(1024); + + b.Property("NewsText") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasMaxLength(10240); + + b.Property("RateAverage") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("RateCount") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("Tags") + .IsRequired() + .HasColumnType("nvarchar(1024)") + .HasMaxLength(1024); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(1024)") + .HasMaxLength(1024); + + b.Property("WriterId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.HasIndex("WriterId"); + + b.ToTable("News"); + }); + + modelBuilder.Entity("BlogIT.DB.Models.NewsTag", b => + { + b.Property("NewsId") + .HasColumnType("int"); + + b.Property("TagId") + .HasColumnType("int"); + + b.HasKey("NewsId", "TagId"); + + b.HasIndex("TagId"); + + b.ToTable("NewsTags"); + }); + + modelBuilder.Entity("BlogIT.DB.Models.Rating", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("NewsId") + .HasColumnType("int"); + + b.Property("Rate") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("NewsId"); + + b.HasIndex("UserId"); + + b.ToTable("Ratings"); + }); + + modelBuilder.Entity("BlogIT.DB.Models.Tag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.ToTable("Tags"); + }); + + modelBuilder.Entity("BlogIT.DB.Models.User", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("AvatarId") + .HasColumnType("int"); + + b.Property("Birthday") + .HasColumnType("datetime2"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("Sex") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("AvatarId") + .IsUnique() + .HasFilter("[AvatarId] IS NOT NULL"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers"); + + b.HasData( + new + { + Id = "2ea66a9a-1bf0-418a-a9f7-bb00b3a71955", + AccessFailedCount = 0, + Birthday = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + ConcurrencyStamp = "9a9e2880-e7d9-4b64-add4-795ac26a36bb", + Email = "Admin@admin.com", + EmailConfirmed = true, + LockoutEnabled = false, + NormalizedEmail = "ADMIN@ADMIN.COM", + NormalizedUserName = "ADMIN", + PasswordHash = "AQAAAAEAACcQAAAAEHuKKBDgB7OXg5nK+FMQpZGrnKhE+xrcAP3G6dMGSsh4Xt4zIfTa15arL/soZkLu2A==", + PhoneNumberConfirmed = false, + SecurityStamp = "", + Sex = "0", + TwoFactorEnabled = false, + UserName = "Admin" + }); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles"); + + b.HasData( + new + { + Id = "ccfabe84-124f-473b-81a0-5da1d8ab4857", + ConcurrencyStamp = "81b2fd77-9615-4927-98f6-0c8dce30a290", + Name = "admin", + NormalizedName = "ADMIN" + }, + new + { + Id = "53dda6a0-e534-4fac-b3d2-145a7c3e2752", + ConcurrencyStamp = "8eb0db2d-383a-4d73-96a2-c1e29cf58af5", + Name = "user", + NormalizedName = "USER" + }); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + + b.HasData( + new + { + UserId = "2ea66a9a-1bf0-418a-a9f7-bb00b3a71955", + RoleId = "ccfabe84-124f-473b-81a0-5da1d8ab4857" + }); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("BlogIT.DB.Models.ChatMessage", b => + { + b.HasOne("BlogIT.DB.Models.News", "News") + .WithMany("ChatMessages") + .HasForeignKey("NewsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BlogIT.DB.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("BlogIT.DB.Models.Like", b => + { + b.HasOne("BlogIT.DB.Models.ChatMessage", "ChatMessage") + .WithMany("Like") + .HasForeignKey("ChatMessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BlogIT.DB.Models.User", "User") + .WithMany("Like") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("BlogIT.DB.Models.News", b => + { + b.HasOne("BlogIT.DB.Models.Category", "Category") + .WithMany("News") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BlogIT.DB.Models.User", "Writer") + .WithMany("News") + .HasForeignKey("WriterId"); + }); + + modelBuilder.Entity("BlogIT.DB.Models.NewsTag", b => + { + b.HasOne("BlogIT.DB.Models.News", "News") + .WithMany("NewsTag") + .HasForeignKey("NewsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BlogIT.DB.Models.Tag", "Tag") + .WithMany("NewsTag") + .HasForeignKey("TagId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("BlogIT.DB.Models.Rating", b => + { + b.HasOne("BlogIT.DB.Models.News", "News") + .WithMany("Ratings") + .HasForeignKey("NewsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BlogIT.DB.Models.User", "User") + .WithMany("Ratings") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("BlogIT.DB.Models.User", b => + { + b.HasOne("BlogIT.DB.Models.FileModel", "Avatar") + .WithOne() + .HasForeignKey("BlogIT.DB.Models.User", "AvatarId"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("BlogIT.DB.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("BlogIT.DB.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BlogIT.DB.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("BlogIT.DB.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/BlogIT.DB/Migrations/20200120135058_AddRating.cs b/BlogIT.DB/Migrations/20200120135058_AddRating.cs new file mode 100644 index 0000000..810452d --- /dev/null +++ b/BlogIT.DB/Migrations/20200120135058_AddRating.cs @@ -0,0 +1,73 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace BlogIT.DB.Migrations +{ + public partial class AddRating : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "RateAverage", + table: "News", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "RateCount", + table: "News", + nullable: false, + defaultValue: 0); + + migrationBuilder.CreateTable( + name: "Ratings", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Rate = table.Column(nullable: false, defaultValue: 0), + UserId = table.Column(nullable: true), + NewsId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Ratings", x => x.Id); + table.ForeignKey( + name: "FK_Ratings_News_NewsId", + column: x => x.NewsId, + principalTable: "News", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Ratings_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_Ratings_NewsId", + table: "Ratings", + column: "NewsId"); + + migrationBuilder.CreateIndex( + name: "IX_Ratings_UserId", + table: "Ratings", + column: "UserId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Ratings"); + + migrationBuilder.DropColumn( + name: "RateAverage", + table: "News"); + + migrationBuilder.DropColumn( + name: "RateCount", + table: "News"); + } + } +} diff --git a/BlogIT.DB/Migrations/20200123094356_AddedUserFields.Designer.cs b/BlogIT.DB/Migrations/20200123094356_AddedUserFields.Designer.cs new file mode 100644 index 0000000..0bef655 --- /dev/null +++ b/BlogIT.DB/Migrations/20200123094356_AddedUserFields.Designer.cs @@ -0,0 +1,666 @@ +// +using System; +using BlogIT.DB.DAL; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace BlogIT.DB.Migrations +{ + [DbContext(typeof(BlogITContext))] + [Migration("20200123094356_AddedUserFields")] + partial class AddedUserFields + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.0") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("BlogIT.DB.Models.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.ToTable("Categories"); + + b.HasData( + new + { + Id = 1, + Title = "Java" + }, + new + { + Id = 2, + Title = "C#" + }, + new + { + Id = 3, + Title = "C++" + }, + new + { + Id = 4, + Title = "Algorithms" + }, + new + { + Id = 5, + Title = "Machine Learning" + }); + }); + + modelBuilder.Entity("BlogIT.DB.Models.ChatMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Date") + .HasColumnType("datetime2"); + + b.Property("Message") + .IsRequired() + .HasColumnType("nvarchar(1024)") + .HasMaxLength(1024); + + b.Property("NewsId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("NewsId"); + + b.HasIndex("UserId"); + + b.ToTable("ChatMessages"); + }); + + modelBuilder.Entity("BlogIT.DB.Models.FileModel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("Path") + .IsRequired() + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.ToTable("Files"); + }); + + modelBuilder.Entity("BlogIT.DB.Models.Like", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ChatMessageId") + .HasColumnType("int"); + + b.Property("LikeCount") + .HasColumnType("int"); + + b.Property("LikeDown") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("LikeUp") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("ChatMessageId"); + + b.HasIndex("UserId"); + + b.ToTable("Likes"); + }); + + modelBuilder.Entity("BlogIT.DB.Models.News", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("DateTime") + .HasColumnType("datetime2"); + + b.Property("Deleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(1024)") + .HasMaxLength(1024); + + b.Property("NewsText") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasMaxLength(10240); + + b.Property("RateAverage") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("RateCount") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("Tags") + .IsRequired() + .HasColumnType("nvarchar(1024)") + .HasMaxLength(1024); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(1024)") + .HasMaxLength(1024); + + b.Property("WriterId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.HasIndex("WriterId"); + + b.ToTable("News"); + }); + + modelBuilder.Entity("BlogIT.DB.Models.NewsTag", b => + { + b.Property("NewsId") + .HasColumnType("int"); + + b.Property("TagId") + .HasColumnType("int"); + + b.HasKey("NewsId", "TagId"); + + b.HasIndex("TagId"); + + b.ToTable("NewsTags"); + }); + + modelBuilder.Entity("BlogIT.DB.Models.Rating", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("NewsId") + .HasColumnType("int"); + + b.Property("Rate") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("NewsId"); + + b.HasIndex("UserId"); + + b.ToTable("Ratings"); + }); + + modelBuilder.Entity("BlogIT.DB.Models.Tag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.ToTable("Tags"); + }); + + modelBuilder.Entity("BlogIT.DB.Models.User", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("AvatarId") + .HasColumnType("int"); + + b.Property("Birthday") + .HasColumnType("datetime2"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("DateOfRegistration") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasColumnType("nvarchar(255)") + .HasMaxLength(255); + + b.Property("Email") + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("Sex") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ShortDescription") + .HasColumnType("nvarchar(1024)") + .HasMaxLength(1024); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("AvatarId") + .IsUnique() + .HasFilter("[AvatarId] IS NOT NULL"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers"); + + b.HasData( + new + { + Id = "2ea66a9a-1bf0-418a-a9f7-bb00b3a71955", + AccessFailedCount = 0, + Birthday = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + ConcurrencyStamp = "9a9e2880-e7d9-4b64-add4-795ac26a36bb", + DateOfRegistration = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + Email = "Admin@admin.com", + EmailConfirmed = true, + LockoutEnabled = false, + NormalizedEmail = "ADMIN@ADMIN.COM", + NormalizedUserName = "ADMIN", + PasswordHash = "AQAAAAEAACcQAAAAEHuKKBDgB7OXg5nK+FMQpZGrnKhE+xrcAP3G6dMGSsh4Xt4zIfTa15arL/soZkLu2A==", + PhoneNumberConfirmed = false, + SecurityStamp = "", + Sex = "0", + TwoFactorEnabled = false, + UserName = "Admin" + }); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles"); + + b.HasData( + new + { + Id = "ccfabe84-124f-473b-81a0-5da1d8ab4857", + ConcurrencyStamp = "81b2fd77-9615-4927-98f6-0c8dce30a290", + Name = "admin", + NormalizedName = "ADMIN" + }, + new + { + Id = "53dda6a0-e534-4fac-b3d2-145a7c3e2752", + ConcurrencyStamp = "8eb0db2d-383a-4d73-96a2-c1e29cf58af5", + Name = "user", + NormalizedName = "USER" + }); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + + b.HasData( + new + { + UserId = "2ea66a9a-1bf0-418a-a9f7-bb00b3a71955", + RoleId = "ccfabe84-124f-473b-81a0-5da1d8ab4857" + }); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("BlogIT.DB.Models.ChatMessage", b => + { + b.HasOne("BlogIT.DB.Models.News", "News") + .WithMany("ChatMessages") + .HasForeignKey("NewsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BlogIT.DB.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("BlogIT.DB.Models.Like", b => + { + b.HasOne("BlogIT.DB.Models.ChatMessage", "ChatMessage") + .WithMany("Like") + .HasForeignKey("ChatMessageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BlogIT.DB.Models.User", "User") + .WithMany("Like") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("BlogIT.DB.Models.News", b => + { + b.HasOne("BlogIT.DB.Models.Category", "Category") + .WithMany("News") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BlogIT.DB.Models.User", "Writer") + .WithMany("News") + .HasForeignKey("WriterId"); + }); + + modelBuilder.Entity("BlogIT.DB.Models.NewsTag", b => + { + b.HasOne("BlogIT.DB.Models.News", "News") + .WithMany("NewsTag") + .HasForeignKey("NewsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BlogIT.DB.Models.Tag", "Tag") + .WithMany("NewsTag") + .HasForeignKey("TagId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("BlogIT.DB.Models.Rating", b => + { + b.HasOne("BlogIT.DB.Models.News", "News") + .WithMany("Ratings") + .HasForeignKey("NewsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BlogIT.DB.Models.User", "User") + .WithMany("Ratings") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("BlogIT.DB.Models.User", b => + { + b.HasOne("BlogIT.DB.Models.FileModel", "Avatar") + .WithOne() + .HasForeignKey("BlogIT.DB.Models.User", "AvatarId"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("BlogIT.DB.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("BlogIT.DB.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BlogIT.DB.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("BlogIT.DB.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/BlogIT.DB/Migrations/20200123094356_AddedUserFields.cs b/BlogIT.DB/Migrations/20200123094356_AddedUserFields.cs new file mode 100644 index 0000000..44b2fed --- /dev/null +++ b/BlogIT.DB/Migrations/20200123094356_AddedUserFields.cs @@ -0,0 +1,44 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace BlogIT.DB.Migrations +{ + public partial class AddedUserFields : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "DateOfRegistration", + table: "AspNetUsers", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "Description", + table: "AspNetUsers", + maxLength: 255, + nullable: true); + + migrationBuilder.AddColumn( + name: "ShortDescription", + table: "AspNetUsers", + maxLength: 1024, + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "DateOfRegistration", + table: "AspNetUsers"); + + migrationBuilder.DropColumn( + name: "Description", + table: "AspNetUsers"); + + migrationBuilder.DropColumn( + name: "ShortDescription", + table: "AspNetUsers"); + } + } +} diff --git a/BlogIT.DB/Migrations/BlogITContextModelSnapshot.cs b/BlogIT.DB/Migrations/BlogITContextModelSnapshot.cs index a2b5c9d..90b0e42 100644 --- a/BlogIT.DB/Migrations/BlogITContextModelSnapshot.cs +++ b/BlogIT.DB/Migrations/BlogITContextModelSnapshot.cs @@ -178,6 +178,16 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(max)") .HasMaxLength(10240); + b.Property("RateAverage") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("RateCount") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + b.Property("Tags") .IsRequired() .HasColumnType("nvarchar(1024)") @@ -215,6 +225,33 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("NewsTags"); }); + modelBuilder.Entity("BlogIT.DB.Models.Rating", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("NewsId") + .HasColumnType("int"); + + b.Property("Rate") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("NewsId"); + + b.HasIndex("UserId"); + + b.ToTable("Ratings"); + }); + modelBuilder.Entity("BlogIT.DB.Models.Tag", b => { b.Property("Id") @@ -250,6 +287,13 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsConcurrencyToken() .HasColumnType("nvarchar(max)"); + b.Property("DateOfRegistration") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasColumnType("nvarchar(255)") + .HasMaxLength(255); + b.Property("Email") .HasColumnType("nvarchar(256)") .HasMaxLength(256); @@ -287,6 +331,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("nvarchar(max)"); + b.Property("ShortDescription") + .HasColumnType("nvarchar(1024)") + .HasMaxLength(1024); + b.Property("TwoFactorEnabled") .HasColumnType("bit"); @@ -317,6 +365,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) AccessFailedCount = 0, Birthday = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), ConcurrencyStamp = "9a9e2880-e7d9-4b64-add4-795ac26a36bb", + DateOfRegistration = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), Email = "Admin@admin.com", EmailConfirmed = true, LockoutEnabled = false, @@ -539,6 +588,19 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired(); }); + modelBuilder.Entity("BlogIT.DB.Models.Rating", b => + { + b.HasOne("BlogIT.DB.Models.News", "News") + .WithMany("Ratings") + .HasForeignKey("NewsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BlogIT.DB.Models.User", "User") + .WithMany("Ratings") + .HasForeignKey("UserId"); + }); + modelBuilder.Entity("BlogIT.DB.Models.User", b => { b.HasOne("BlogIT.DB.Models.FileModel", "Avatar") diff --git a/BlogIT.DB/Models/BaseEntity.cs b/BlogIT.DB/Models/BaseEntity.cs new file mode 100644 index 0000000..1fa6389 --- /dev/null +++ b/BlogIT.DB/Models/BaseEntity.cs @@ -0,0 +1,8 @@ + +namespace BlogIT.DB.Models +{ + public class BaseEntity + { + public int Id { get; set; } + } +} diff --git a/BlogIT.DB/Models/News.cs b/BlogIT.DB/Models/News.cs index d15995d..f7a8d80 100644 --- a/BlogIT.DB/Models/News.cs +++ b/BlogIT.DB/Models/News.cs @@ -3,9 +3,8 @@ namespace BlogIT.DB.Models { - public class News + public class News : BaseEntity { - public int Id { get; set; } public string Title { get; set; } public DateTime DateTime { get; set; } public string Description { get; set; } @@ -18,5 +17,9 @@ public class News public bool Deleted { get; set; } public List ChatMessages { get; set; } public List NewsTag { get; set; } + public List Ratings { get; set; } + public int RateCount { get; set; } + public int RateAverage { get; set; } + } } diff --git a/BlogIT.DB/Models/Rating.cs b/BlogIT.DB/Models/Rating.cs new file mode 100644 index 0000000..d5f4648 --- /dev/null +++ b/BlogIT.DB/Models/Rating.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BlogIT.DB.Models +{ + public class Rating + { + public int Id { get; set; } + public int Rate { get; set; } + public string UserId { get; set; } + public User User { get; set; } + public int NewsId { get; set; } + public News News { get; set; } + } +} diff --git a/BlogIT.DB/Models/User.cs b/BlogIT.DB/Models/User.cs index 137a210..22bf9a1 100644 --- a/BlogIT.DB/Models/User.cs +++ b/BlogIT.DB/Models/User.cs @@ -10,7 +10,11 @@ public class User : IdentityUser public string Sex { get; set; } public List News { get; set; } public List Like { get; set; } + public List Ratings { get; set; } public FileModel Avatar { get; set; } public int? AvatarId { get; set; } + public DateTime DateOfRegistration { get; set; } + public string ShortDescription { get; set; } + public string Description { get; set; } } } diff --git a/BlogIT.DB/Specifications/BaseSpecification.cs b/BlogIT.DB/Specifications/BaseSpecification.cs new file mode 100644 index 0000000..f8dee67 --- /dev/null +++ b/BlogIT.DB/Specifications/BaseSpecification.cs @@ -0,0 +1,49 @@ +using BlogIT.DB.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace BlogIT.DB.Specifications +{ + public abstract class BaseSpecification : ISpecification + { + protected BaseSpecification(Expression> criteria) + { + Criteria = criteria; + } + public Expression> Criteria { get; } + public List>> Includes { get; } = new List>>(); + public List IncludeStrings { get; } = new List(); + public Expression> OrderBy { get; private set; } + public Expression> OrderByDescending { get; private set; } + public Expression> GroupBy { get; private set; } + + public int Take { get; private set; } + public int Skip { get; private set; } + public bool IsPagingEnabled { get; private set; } = false; + + protected virtual void AddInclude(Expression> includeExpression) + { + Includes.Add(includeExpression); + } + + protected virtual void AddInclude(string includeString) + { + IncludeStrings.Add(includeString); + } + protected virtual void ApplyPaging(int skip, int take) + { + Skip = skip; + Take = take; + IsPagingEnabled = true; + } + protected virtual void ApplyOrderBy(Expression> orderByExpression) + { + OrderBy = orderByExpression; + } + protected virtual void ApplyOrderByDescending(Expression> orderByDescendingExpression) + { + OrderByDescending = orderByDescendingExpression; + } + } +} diff --git a/BlogIT.DB/Specifications/NewsFilterPaginatedSpecification.cs b/BlogIT.DB/Specifications/NewsFilterPaginatedSpecification.cs new file mode 100644 index 0000000..58cc0c8 --- /dev/null +++ b/BlogIT.DB/Specifications/NewsFilterPaginatedSpecification.cs @@ -0,0 +1,58 @@ +using BlogIT.DB.Models; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace BlogIT.DB.Specifications +{ + public class NewsFilterPaginatedSpecification : BaseSpecification + { + public NewsFilterPaginatedSpecification(int skip, int take, string searchString, List tags, DateTime dateCalendar, int categoryId = 0, bool findByComments = false) + : base(p => (!p.Deleted) && (dateCalendar == DateTime.MinValue || p.DateTime.Date == dateCalendar.Date) && + (categoryId == 0 || p.CategoryId == categoryId) && + (tags == null || tags.Count == 0 || p.NewsTag.Any(s => tags.Contains(s.Tag.Title.ToUpper()))) && + (String.IsNullOrEmpty(searchString) || p.Title.Contains(searchString) || p.Description.Contains(searchString) || p.NewsText.Contains(searchString) || (findByComments && p.ChatMessages.Any(p => p.Message.Contains(searchString)))) + ) + { + AddInclude(p => p.ChatMessages); + AddInclude(p => p.Category); + ApplyPaging(skip, take); + } + + public void ApplyOrder(SortState sortOrder) + { + switch (sortOrder) + { + case SortState.TitleDesc: + ApplyOrderByDescending(s => s.Title); + break; + case SortState.DateTimeAsc: + ApplyOrderBy(s => s.DateTime); + break; + case SortState.DateTimeDesc: + ApplyOrderByDescending(s => s.DateTime); + break; + case SortState.WriterAsc: + ApplyOrderBy(s => s.Writer.UserName); + break; + case SortState.WriterDesc: + ApplyOrderByDescending(s => s.Writer.UserName); + break; + default: + ApplyOrderBy(s => s.Title); + break; + } + } + + } + + public enum SortState + { + TitleAsc, + TitleDesc, + DateTimeAsc, + DateTimeDesc, + WriterAsc, + WriterDesc + } +} diff --git a/BlogIT.DB/Specifications/NewsFilterSpecification.cs b/BlogIT.DB/Specifications/NewsFilterSpecification.cs new file mode 100644 index 0000000..dee300b --- /dev/null +++ b/BlogIT.DB/Specifications/NewsFilterSpecification.cs @@ -0,0 +1,19 @@ +using BlogIT.DB.Models; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace BlogIT.DB.Specifications +{ + public class NewsFilterSpecification : BaseSpecification + { + public NewsFilterSpecification(string searchString, List tags, DateTime dateCalendar, int categoryId = 0, bool findByComments = false) + : base(p => (!p.Deleted) && (dateCalendar == DateTime.MinValue || p.DateTime.Date == dateCalendar.Date) && + (categoryId == 0 || p.CategoryId == categoryId) && + (tags == null || tags.Count == 0 || p.NewsTag.Any(s => tags.Contains(s.Tag.Title.ToUpper()))) && + (String.IsNullOrEmpty(searchString) || p.Title.Contains(searchString) || p.Description.Contains(searchString) || p.NewsText.Contains(searchString) || (findByComments && p.ChatMessages.Any(p => p.Message.Contains(searchString)))) + ) + { + } + } +} diff --git a/BlogIT.DB/Specifications/SpecificationEvaluator.cs b/BlogIT.DB/Specifications/SpecificationEvaluator.cs new file mode 100644 index 0000000..c585303 --- /dev/null +++ b/BlogIT.DB/Specifications/SpecificationEvaluator.cs @@ -0,0 +1,53 @@ +using BlogIT.DB.Interfaces; +using BlogIT.DB.Models; +using Microsoft.EntityFrameworkCore; +using System.Linq; + + +namespace BlogIT.DB.Specifications +{ + public class SpecificationEvaluator where T: BaseEntity + { + public static IQueryable GetQuery(IQueryable inputQuery, ISpecification specification) + { + var query = inputQuery; + + // modify the IQueryable using the specification's criteria expression + if (specification.Criteria != null) + { + query = query.Where(specification.Criteria); + } + + // Includes all expression-based includes + query = specification.Includes.Aggregate(query, + (current, include) => current.Include(include)); + + // Include any string-based include statements + query = specification.IncludeStrings.Aggregate(query, + (current, include) => current.Include(include)); + + // Apply ordering if expressions are set + if (specification.OrderBy != null) + { + query = query.OrderBy(specification.OrderBy); + } + else if (specification.OrderByDescending != null) + { + query = query.OrderByDescending(specification.OrderByDescending); + } + + if (specification.GroupBy != null) + { + query = query.GroupBy(specification.GroupBy).SelectMany(x => x); + } + + // Apply paging if enabled + if (specification.IsPagingEnabled) + { + query = query.Skip(specification.Skip) + .Take(specification.Take); + } + return query; + } + } +} diff --git a/BlogIT.MVC/BlogIT.MVC.csproj b/BlogIT.MVC/BlogIT.MVC.csproj index fd13a75..fa52083 100644 --- a/BlogIT.MVC/BlogIT.MVC.csproj +++ b/BlogIT.MVC/BlogIT.MVC.csproj @@ -4,11 +4,14 @@ netcoreapp3.1 BlogIT.MVC BlogIT.MVC + f81f46a7-b813-4205-927d-8da1505f8d89 + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/BlogIT.MVC/Controllers/AccountController.cs b/BlogIT.MVC/Controllers/AccountController.cs index 5dfefc1..1884045 100644 --- a/BlogIT.MVC/Controllers/AccountController.cs +++ b/BlogIT.MVC/Controllers/AccountController.cs @@ -10,6 +10,8 @@ using System.Security.Claims; using System.Threading.Tasks; using BlogIT.DB.BL; +using System.Linq; +using Microsoft.AspNetCore.Authentication; namespace BlogIT.MVC.Controllers { @@ -20,18 +22,21 @@ public class AccountController : Controller private readonly IMapper _mapper; private readonly IStringLocalizer _localizer; private readonly IPhotoService _photoService; + private readonly IEmailService _emailService; public AccountController(UserManager userManager, SignInManager signInManager, IMapper mapper, IStringLocalizer localizer, - IPhotoService photoService) + IPhotoService photoService, + IEmailService emailService) { _userManager = userManager; _signInManager = signInManager; _mapper = mapper; _localizer = localizer; _photoService = photoService; + _emailService = emailService; } [HttpGet] @@ -46,7 +51,7 @@ public async Task Register(RegisterViewModel registerViewModel) if (ModelState.IsValid) { User user = _mapper.Map(registerViewModel); - + var result = await _userManager.CreateAsync(user, registerViewModel.Password); if (result.Succeeded) { @@ -58,8 +63,18 @@ public async Task Register(RegisterViewModel registerViewModel) var gender = new Claim(ClaimTypes.Gender, registerViewModel.Sex.ToString(), typeof(String).ToString()); await _userManager.AddClaimAsync(user, gender); - await _signInManager.SignInAsync(user, false); - return RedirectToAction("Index", "Home"); + string code = await _userManager.GenerateEmailConfirmationTokenAsync(user); + var callbackUrl = Url.Action( + "ConfirmEmail", + "Account", + new { userId = user.Id, code = code }, + protocol: HttpContext.Request.Scheme); + + await _emailService.SendEmailAsync(user.Email, "Confirm your account", + $"Подтвердите регистрацию, перейдя по ссылке: link"); + + return RedirectToAction("Confirm", "Account"); + } else { @@ -72,6 +87,32 @@ public async Task Register(RegisterViewModel registerViewModel) return View(registerViewModel); } + [HttpGet] + public IActionResult Confirm() + { + return View(); + } + + [HttpGet] + [AllowAnonymous] + public async Task ConfirmEmail(string userId, string code) + { + if (userId == null || code == null) + { + return View("Error"); + } + var user = await _userManager.FindByIdAsync(userId); + if (user == null) + { + return View("Error"); + } + var result = await _userManager.ConfirmEmailAsync(user, code); + if (result.Succeeded) + return RedirectToAction("Index", "Home"); + else + return View("Error"); + } + [HttpGet] public IActionResult Login() { @@ -84,6 +125,17 @@ public async Task Login(LoginViewModel model) { if (ModelState.IsValid) { + + var user = await _userManager.FindByNameAsync(model.Name); + if (user != null) + { + if (!await _userManager.IsEmailConfirmedAsync(user)) + { + ModelState.AddModelError(string.Empty, "Вы не подтвердили свой email"); + return View(model); + } + } + var result = await _signInManager.PasswordSignInAsync(model.Name, model.Password, model.RememberMe, false); if (result.Succeeded) @@ -100,6 +152,46 @@ public async Task Login(LoginViewModel model) return View(model); } + + public IActionResult LoginWhithOAuth(string provider) + { + var authenticationProperties = _signInManager.ConfigureExternalAuthenticationProperties(provider, Url.Action(nameof(HandleExternalLogin))); + return Challenge(authenticationProperties, provider); + } + + public async Task HandleExternalLogin() + { + var info = await _signInManager.GetExternalLoginInfoAsync(); + + var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: true); + + if (!result.Succeeded) + { + var email = info.Principal.FindFirstValue(ClaimTypes.Email); + var newUser = new User + { + UserName = email, + Email = email, + EmailConfirmed = true, + DateOfRegistration = DateTime.Now, + Sex = "0" + }; + var createResult = await _userManager.CreateAsync(newUser); + if (!createResult.Succeeded) + throw new Exception(createResult.Errors.Select(e => e.Description).Aggregate((errors, error) => $"{errors}, {error}")); + + await _userManager.AddToRoleAsync(newUser, "user"); + + await _userManager.AddLoginAsync(newUser, info); + var newUserClaims = info.Principal.Claims.Append(new Claim("userId", newUser.Id)); + await _userManager.AddClaimsAsync(newUser, newUserClaims); + await _signInManager.SignInAsync(newUser, isPersistent: false); + await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); + } + + return RedirectToAction("Index", "Home"); + } + [HttpGet] [Authorize(Roles = "user, admin")] public async Task ProfileSettings(string id) @@ -126,6 +218,8 @@ public async Task ProfileSettings(ProfileSettingsViewModel model, user.Email = model.Email; user.UserName = model.UserName; user.Birthday = model.Birthday; + user.Description = model.Description; + user.ShortDescription = model.ShortDescription; user.Sex = model.Sex; if (file != null) { diff --git a/BlogIT.MVC/Controllers/HomeController.cs b/BlogIT.MVC/Controllers/HomeController.cs index ab6f645..7a94aee 100644 --- a/BlogIT.MVC/Controllers/HomeController.cs +++ b/BlogIT.MVC/Controllers/HomeController.cs @@ -1,13 +1,17 @@ using AutoMapper; using AutoMapper.QueryableExtensions; +using AutoMapper.Collection; using BlogIT.DB.BL; using BlogIT.DB.Models; using BlogIT.MVC.ViewModels; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.Extensions.Configuration; using System; using System.Collections.Generic; using System.Linq; +using BlogIT.DB.Specifications; +using System.Threading.Tasks; namespace BlogIT.MVC.Controllers { @@ -16,60 +20,57 @@ public class HomeController : Controller private readonly IMapper _mapper; private readonly INewsService _newsService; + private readonly IConfiguration _configuration; public HomeController(IMapper mapper, - INewsService newsService) + INewsService newsService, + IConfiguration configuration) { _mapper = mapper; _newsService = newsService; + _configuration = configuration; } public IActionResult Index() { + Int32.TryParse(_configuration["CountLastNews"], out int countLastNews); + Int32.TryParse(_configuration["CountTopNews"], out int countTopNews); HomePageViewModel homePageViewModel = new HomePageViewModel() { - LastNews = _newsService.GetLastNews(3).ProjectTo(_mapper.ConfigurationProvider).ToList(), - TopNews = _newsService.GetTopNews(3).ProjectTo(_mapper.ConfigurationProvider).ToList() + LastNews = _mapper.Map, List>(_newsService.GetLastNews(countLastNews)), + TopNews = _mapper.Map, List>(_newsService.GetTopNews(countTopNews)), + ListTags = _newsService.GetTopTags() }; return View(homePageViewModel); } - public IActionResult List(string searchString, string tags, DateTime dateCalendar, int categoryId = 0, bool findByComments = false, int page = 1) + public async Task List(string searchString, string tags, DateTime dateCalendar, int categoryId = 0, bool findByComments = false, int page = 1) { + + Int32.TryParse(_configuration["NumberOfItemsPerPage"], out int pageSize); - int pageSize = 3; + List tagsList = new List(); - IQueryable source = _newsService.ListActualNews(findByComments); - - - if(dateCalendar> DateTime.MinValue) - { - source = source.Where(p => p.DateTime.Date == dateCalendar.Date); - } - if (categoryId != 0) - { - source = source.Where(p => p.CategoryId == categoryId); - } if (!String.IsNullOrEmpty(tags)) { - string[] tagsArray = tags.Split(','); + tagsList = tags.ToUpper().Split(',').ToList(); - foreach (string tagTitle in tagsArray) - { - source = source.Where(p => p.NewsTag.Any(s => s.Tag.Title.ToUpper() == tagTitle.ToUpper())); - } - } - if (!String.IsNullOrEmpty(searchString)) - { - source = source.Where(p => p.Title.Contains(searchString) || p.Description.Contains(searchString) || p.NewsText.Contains(searchString) || (findByComments && p.ChatMessages.Any(p => p.Message.Contains(searchString)))); } - var count = source.Count(); - var items = source.Skip((page - 1) * pageSize).Take(pageSize).ProjectTo(_mapper.ConfigurationProvider).ToList(); + var filterSpecification = new NewsFilterSpecification(searchString, tagsList, dateCalendar, categoryId, findByComments); + var filterPaginatedSpecification = + new NewsFilterPaginatedSpecification((page - 1) * pageSize, pageSize, searchString, tagsList, dateCalendar, categoryId, findByComments); + + var itemsOnPage = await _newsService.ListNewsAsync(filterPaginatedSpecification); + var totalItems = await _newsService.CountNewsAsync(filterSpecification); + + var items = _mapper.Map, List>(itemsOnPage); + + - PageViewModel pageViewModel = new PageViewModel(count, page, pageSize); + PageViewModel pageViewModel = new PageViewModel(totalItems, page, pageSize); List listCategories = _newsService.GetCategories(); diff --git a/BlogIT.MVC/Controllers/NewsController.cs b/BlogIT.MVC/Controllers/NewsController.cs index cbcb022..709a97a 100644 --- a/BlogIT.MVC/Controllers/NewsController.cs +++ b/BlogIT.MVC/Controllers/NewsController.cs @@ -1,19 +1,22 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using AutoMapper; using AutoMapper.QueryableExtensions; using BlogIT.DB.BL; using BlogIT.DB.Models; +using BlogIT.DB.Specifications; using BlogIT.MVC.ViewModels; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Rendering; +using static BlogIT.DB.Specifications.NewsFilterPaginatedSpecification; namespace BlogIT.MVC.Controllers { - //[Authorize(Roles = "admin")] + [RequestFormLimits(ValueLengthLimit = int.MaxValue, MultipartBodyLengthLimit = int.MaxValue)] public class NewsController : Controller { @@ -31,46 +34,28 @@ public NewsController(IMapper mapper, _userManager = userManager; } - public IActionResult Index(string searchString, int page = 1, + [Authorize(Roles = "admin")] + public async Task Index(string searchString, int page = 1, SortState sortOrder = SortState.DateTimeDesc) { int pageSize = 3; + + + var filterSpecification = new NewsFilterSpecification(searchString, null, DateTime.MinValue, 0, false); - IQueryable source = _newsService.ListAll(); + var filterPaginatedSpecification = + new NewsFilterPaginatedSpecification((page - 1) * pageSize, pageSize, searchString, null, DateTime.MinValue, 0, false); + filterPaginatedSpecification.ApplyOrder(sortOrder); - if (!String.IsNullOrEmpty(searchString)) - { - source = source.Where(p => p.Title.Contains(searchString) || p.Tags.Contains(searchString) || p.Description.Contains(searchString) || p.NewsText.Contains(searchString) || p.ChatMessages.Any(p => p.Message.Contains(searchString))); - } + var itemsOnPage = await _newsService.ListNewsAsync(filterPaginatedSpecification); + var totalItems = await _newsService.CountNewsAsync(filterSpecification); - // сортировка - switch (sortOrder) - { - case SortState.TitleDesc: - source = source.OrderByDescending(s => s.Title); - break; - case SortState.DateTimeAsc: - source = source.OrderBy(s => s.DateTime); - break; - case SortState.DateTimeDesc: - source = source.OrderByDescending(s => s.DateTime); - break; - case SortState.WriterAsc: - source = source.OrderBy(s => s.Writer.UserName); - break; - case SortState.WriterDesc: - source = source.OrderByDescending(s => s.Writer.UserName); - break; - default: - source = source.OrderBy(s => s.Title); - break; - } + var items = _mapper.Map, List>(itemsOnPage); - var count = source.Count(); - var items = source.Skip((page - 1) * pageSize).Take(pageSize).ProjectTo(_mapper.ConfigurationProvider).ToList(); - PageViewModel pageViewModel = new PageViewModel(count, page, pageSize); + + PageViewModel pageViewModel = new PageViewModel(totalItems, page, pageSize); NewsListViewModel newsListViewModel = new NewsListViewModel(); newsListViewModel.ItemNewsListViewModel = items; @@ -83,6 +68,7 @@ public IActionResult Index(string searchString, int page = 1, } + [Authorize(Roles = "admin")] public IActionResult Create() { CreateNewsViewModel createNewsViewModel = new CreateNewsViewModel(); @@ -90,6 +76,7 @@ public IActionResult Create() return View(createNewsViewModel); } + [Authorize(Roles = "admin")] [HttpPost] public IActionResult Create(CreateNewsViewModel createNewsViewModel) { @@ -107,6 +94,7 @@ public IActionResult Create(CreateNewsViewModel createNewsViewModel) return View(createNewsViewModel); } + [Authorize(Roles = "admin")] public IActionResult Edit(int id) { News news = _newsService.GetNewsById(id); @@ -120,6 +108,7 @@ public IActionResult Edit(int id) return View(editNewsViewModel); } + [Authorize(Roles = "admin")] [HttpPost] public IActionResult Edit(EditNewsViewModel editNewsViewModel) { @@ -141,12 +130,14 @@ public IActionResult Index(int id) { News news = _newsService.GetNewsById(id); - NewsViewModel itemNewsListViewModel = _mapper.Map(news); - - return View(itemNewsListViewModel); + NewsViewModel newsViewModel = _mapper.Map(news); + newsViewModel.CurrentUserRating = _newsService.GetCurrentUserRating(news.Id, _userManager.GetUserId(User)); + + return View(newsViewModel); } + [Authorize(Roles = "admin")] [HttpPost] public ActionResult Delete(int id) { @@ -155,5 +146,27 @@ public ActionResult Delete(int id) return RedirectToAction("Index"); } + + [Authorize(Roles = "user, admin")] + [HttpPost] + public JsonResult PostRating(RatingViewModel ratingViewModel) + { + + string userId = _userManager.GetUserId(User); + + if(userId!=null) + { + Rating rating = _mapper.Map(ratingViewModel); + rating.UserId = userId; + + _newsService.SetRating(rating); + + return Json("You rated this " + ratingViewModel.Rate.ToString() + " star(s)"); + } + else + { + return Json("Need to register"); + } + } } } \ No newline at end of file diff --git a/BlogIT.MVC/Controllers/SearchController.cs b/BlogIT.MVC/Controllers/SearchController.cs index 915505a..f27dce9 100644 --- a/BlogIT.MVC/Controllers/SearchController.cs +++ b/BlogIT.MVC/Controllers/SearchController.cs @@ -3,6 +3,7 @@ using BlogIT.DB.Models; using Newtonsoft.Json; using BlogIT.DB.BL; +using System.Collections.Generic; namespace BlogIT.MVC.Controllers { @@ -16,7 +17,7 @@ public SearchController(INewsService newsService) } public IActionResult GetTags(string term) { - IQueryable tags = _newsService.GetTags(term); + List tags = _newsService.GetTags(term); var respons = tags.Select(a => new { value = a.Title, label = a.Title }).ToArray(); diff --git a/BlogIT.MVC/Helpers/NewsTagHelper.cs b/BlogIT.MVC/Helpers/NewsTagHelper.cs index 0c96626..884c0c5 100644 --- a/BlogIT.MVC/Helpers/NewsTagHelper.cs +++ b/BlogIT.MVC/Helpers/NewsTagHelper.cs @@ -37,7 +37,7 @@ public override void Process(TagHelperContext context, TagHelperOutput output) tagDivRow.AddCssClass("row"); TagBuilder tagDivCol = new TagBuilder("div"); - tagDivCol.AddCssClass("col-md-8 ml-auto mr-auto"); + tagDivCol.AddCssClass("col-md-10 ml-auto mr-auto"); TagBuilder tagDivCard = new TagBuilder("div"); tagDivCard.AddCssClass("row card card-blog card-plain text-center"); @@ -88,8 +88,8 @@ public override void Process(TagHelperContext context, TagHelperOutput output) tagDivCardCategory.InnerHtml.AppendHtml(tagDivDateTime); - TagBuilder tagDivCountComments = new TagBuilder("div"); - tagDivCountComments.AddCssClass("pull-right"); + TagBuilder tagDivPullRight = new TagBuilder("div"); + tagDivPullRight.AddCssClass("pull-right"); TagBuilder tagAComments = new TagBuilder("a"); tagAComments.Attributes["href"] = urlHelper.Action(ActionName, ControllerName, new { id = NewsView.Id }, null, null, "chatList"); @@ -101,9 +101,36 @@ public override void Process(TagHelperContext context, TagHelperOutput output) tagAComments.InnerHtml.AppendHtml(tagCountComments); - tagDivCountComments.InnerHtml.AppendHtml(tagAComments); + tagDivPullRight.InnerHtml.AppendHtml(tagAComments); + + + + + TagBuilder tagDivStars = new TagBuilder("div"); - tagDivCardCategory.InnerHtml.AppendHtml(tagDivCountComments); + for(int i = 1; i <= 5 ; i++) + { + TagBuilder tagDivStar = new TagBuilder("img"); + tagDivStar.AddCssClass("small-rating"); + + if(i <= NewsView.RateAverage) + { + tagDivStar.Attributes["src"] = "/Files/FilledStar.png"; + } + else + { + tagDivStar.Attributes["src"] = "/Files/EmptyStar.png"; + } + + tagDivStars.InnerHtml.AppendHtml(tagDivStar); + } + + + tagDivPullRight.InnerHtml.AppendHtml(tagDivStars); + + + + tagDivCardCategory.InnerHtml.AppendHtml(tagDivPullRight); diff --git a/BlogIT.MVC/Mappings/MappingProfile.cs b/BlogIT.MVC/Mappings/MappingProfile.cs index b774061..322227b 100644 --- a/BlogIT.MVC/Mappings/MappingProfile.cs +++ b/BlogIT.MVC/Mappings/MappingProfile.cs @@ -12,7 +12,9 @@ public class MappingProfile: Profile public MappingProfile() { - CreateMap().ReverseMap(); + CreateMap(); + CreateMap() + .ForMember(d => d.DateOfRegistration, o => o.MapFrom(s => DateTime.Now)); CreateMap().ReverseMap(); CreateMap().ReverseMap(); CreateMap() @@ -42,11 +44,14 @@ public MappingProfile() .ForMember(d => d.Date, o => o.MapFrom(s => s.Date.ToString())) .ForMember(d => d.AvatarPath, o => o.MapFrom(s => s.User.Avatar != null ? $"/{s.User.Avatar.Path}" : "/Files/placeholder.jpg")) .ForMember(d => d.LikeUpCount, o => o.MapFrom(s => s.Like.Where(p => p.LikeUp == true && p.ChatMessageId == s.Id).Count())) - .ForMember(d => d.LikeDownCount, o => o.MapFrom(s => s.Like.Where(p => p.LikeDown == true && p.ChatMessageId == s.Id).Count())); + .ForMember(d => d.LikeDownCount, o => o.MapFrom(s => s.Like.Where(p => p.LikeDown == true && p.ChatMessageId == s.Id).Count())) + .ForMember(d => d.UserId, o => o.MapFrom(s => s.User.Id)); CreateMap() .ForMember(d => d.Category, o => o.MapFrom(s => s.Category.Title)) .ForMember(d => d.CategoryId, o => o.MapFrom(s => s.Category.Id)) .ForMember(d => d.CountsOfComments, o => o.MapFrom(s => s.ChatMessages.Count)); + CreateMap(); + } } diff --git a/BlogIT.MVC/SignalR/ChatHub.cs b/BlogIT.MVC/SignalR/ChatHub.cs index 57b004d..4d8e36d 100644 --- a/BlogIT.MVC/SignalR/ChatHub.cs +++ b/BlogIT.MVC/SignalR/ChatHub.cs @@ -36,7 +36,7 @@ public async Task SendMessage(string message, int newsId) { string userName = Context.User.Identity.Name; User user = await _userManager.FindByNameAsync(userName); - user.Avatar = _photoService.GetFileByID((int)user.AvatarId); + user.Avatar = _photoService.GetFileByID(user.AvatarId??0); ChatMessage chatMessage = new ChatMessage() { @@ -69,7 +69,7 @@ public async Task ConnectUser(int newsId) { await Groups.AddToGroupAsync(Context.ConnectionId, newsId.ToString()); - List chatMessages = _newsService.GetChatMessagesByPartyId(newsId).ToList(); ; + List chatMessages = _newsService.GetChatMessagesByPartyId(newsId); string userName = Context.User.Identity.Name; User user = await _userManager.FindByNameAsync(userName); diff --git a/BlogIT.MVC/Startup.cs b/BlogIT.MVC/Startup.cs index 90726d0..2977c9b 100644 --- a/BlogIT.MVC/Startup.cs +++ b/BlogIT.MVC/Startup.cs @@ -36,12 +36,16 @@ public void ConfigureServices(IServiceCollection services) options.UseSqlServer(Configuration.GetConnectionString("BlogITDatabase"))); services.AddIdentity() - .AddEntityFrameworkStores(); + .AddEntityFrameworkStores() + .AddDefaultTokenProviders(); services.AddTransient(); services.AddTransient(); services.AddTransient(); - + services.AddTransient(); + + services.AddSingleton(Configuration); + services.AddAutoMapper(typeof(Mappings.MappingProfile)); services.AddAuthorization(); @@ -50,6 +54,11 @@ public void ConfigureServices(IServiceCollection services) .AddCookie(options => { options.LoginPath = new PathString("/Account/Login"); + }) + .AddFacebook(facebookOptions => + { + facebookOptions.AppId = Configuration["Authentication:Facebook:AppId"]; + facebookOptions.AppSecret = Configuration["Authentication:Facebook:AppSecret"]; }); services.AddLocalization(options => options.ResourcesPath = "Resources"); diff --git a/BlogIT.MVC/ViewModels/ChatMessageViewModel.cs b/BlogIT.MVC/ViewModels/ChatMessageViewModel.cs index 09687c6..9ac4f93 100644 --- a/BlogIT.MVC/ViewModels/ChatMessageViewModel.cs +++ b/BlogIT.MVC/ViewModels/ChatMessageViewModel.cs @@ -11,6 +11,7 @@ public class ChatMessageViewModel public string Date { get; set; } public string Message { get; set; } public string UserName { get; set; } + public string UserId { get; set; } public string AvatarPath { get; set; } public int LikeCount { get; set; } public int LikeUpCount { get; set; } diff --git a/BlogIT.MVC/ViewModels/HomePageViewModel.cs b/BlogIT.MVC/ViewModels/HomePageViewModel.cs index 6caf924..b1d677b 100644 --- a/BlogIT.MVC/ViewModels/HomePageViewModel.cs +++ b/BlogIT.MVC/ViewModels/HomePageViewModel.cs @@ -6,6 +6,7 @@ public class HomePageViewModel { public List LastNews { get; set; } public List TopNews { get; set; } + public List ListTags { get; set; } } } diff --git a/BlogIT.MVC/ViewModels/NewsAnnotationViewModel.cs b/BlogIT.MVC/ViewModels/NewsAnnotationViewModel.cs index ab18092..1618359 100644 --- a/BlogIT.MVC/ViewModels/NewsAnnotationViewModel.cs +++ b/BlogIT.MVC/ViewModels/NewsAnnotationViewModel.cs @@ -11,5 +11,7 @@ public class NewsAnnotationViewModel public string Category { get; set; } public int CategoryId { get; set; } public int CountsOfComments { get; set; } + public int RateAverage { get; set; } + } } diff --git a/BlogIT.MVC/ViewModels/NewsViewModel.cs b/BlogIT.MVC/ViewModels/NewsViewModel.cs index 4d0ac5c..6dc977b 100644 --- a/BlogIT.MVC/ViewModels/NewsViewModel.cs +++ b/BlogIT.MVC/ViewModels/NewsViewModel.cs @@ -22,5 +22,9 @@ public class NewsViewModel public string Category { get; set; } public int CategoryId { get; set; } public List ListTag { get; set; } + public int RateCount { get; set; } + public int RateAverage { get; set; } + public int CurrentUserRating { get; set; } + } } diff --git a/BlogIT.MVC/ViewModels/ProfileSettingsViewModel.cs b/BlogIT.MVC/ViewModels/ProfileSettingsViewModel.cs index ba455c6..9ff87df 100644 --- a/BlogIT.MVC/ViewModels/ProfileSettingsViewModel.cs +++ b/BlogIT.MVC/ViewModels/ProfileSettingsViewModel.cs @@ -17,6 +17,10 @@ public class ProfileSettingsViewModel public string Sex { get; set; } public int AvatarID { get; set; } public bool AvatarExist { get; set; } + [Display(Name = "ShortDescription")] + public string ShortDescription { get; set; } + [Display(Name = "Description")] + public string Description { get; set; } } } diff --git a/BlogIT.MVC/ViewModels/ProfileViewModel.cs b/BlogIT.MVC/ViewModels/ProfileViewModel.cs index 034339d..b44fc84 100644 --- a/BlogIT.MVC/ViewModels/ProfileViewModel.cs +++ b/BlogIT.MVC/ViewModels/ProfileViewModel.cs @@ -1,4 +1,5 @@ -using System.ComponentModel.DataAnnotations; +using System; +using System.ComponentModel.DataAnnotations; namespace BlogIT.MVC.ViewModels { @@ -11,6 +12,12 @@ public class ProfileViewModel public string Email { get; set; } public int AvatarID { get; set; } public bool AvatarExist { get; set; } + [Display(Name = "DateOfRegistration")] + public DateTime DateOfRegistration { get; set; } + [Display(Name = "ShortDescription")] + public string ShortDescription { get; set; } + [Display(Name = "Description")] + public string Description { get; set; } } } diff --git a/BlogIT.MVC/ViewModels/RatingViewModel.cs b/BlogIT.MVC/ViewModels/RatingViewModel.cs new file mode 100644 index 0000000..c542f02 --- /dev/null +++ b/BlogIT.MVC/ViewModels/RatingViewModel.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace BlogIT.MVC.ViewModels +{ + public class RatingViewModel + { + public int Rate { get; set; } + public int NewsId { get; set; } + } +} diff --git a/BlogIT.MVC/ViewModels/SortNewsViewModel.cs b/BlogIT.MVC/ViewModels/SortNewsViewModel.cs index 021f2bd..29fc837 100644 --- a/BlogIT.MVC/ViewModels/SortNewsViewModel.cs +++ b/BlogIT.MVC/ViewModels/SortNewsViewModel.cs @@ -1,7 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using BlogIT.DB.Specifications; + namespace BlogIT.MVC.ViewModels { @@ -20,14 +18,4 @@ public SortNewsViewModel(SortState sortOrder) Current = sortOrder; } } - - public enum SortState - { - TitleAsc, - TitleDesc, - DateTimeAsc, - DateTimeDesc, - WriterAsc, - WriterDesc - } } diff --git a/BlogIT.MVC/Views/Account/Confirm.cshtml b/BlogIT.MVC/Views/Account/Confirm.cshtml new file mode 100644 index 0000000..642af62 --- /dev/null +++ b/BlogIT.MVC/Views/Account/Confirm.cshtml @@ -0,0 +1,20 @@ +@{ + ViewData["Title"] = @Localizer["Title"]; +} + + + +
+
+
+ +
+
+

@Localizer["Для завершения регистрации проверьте электронную почту и перейдите по ссылке, указанной в письме"]

+
+
+
+
+
\ No newline at end of file diff --git a/BlogIT.MVC/Views/Account/Login.cshtml b/BlogIT.MVC/Views/Account/Login.cshtml index 0b4b761..edce07b 100644 --- a/BlogIT.MVC/Views/Account/Login.cshtml +++ b/BlogIT.MVC/Views/Account/Login.cshtml @@ -8,6 +8,23 @@

@Localizer["Title"]

+ + +
+
+ @Localizer["or"] +
+
@@ -34,9 +34,9 @@
-

Описание добавить пользователю

+

@Model.Description


- @Localizer["ListOfMyParties"] +

@Localizer["DateOfRegistration"] @Model.DateOfRegistration.ToString("D")

diff --git a/BlogIT.MVC/Views/Account/ProfileSettings.cshtml b/BlogIT.MVC/Views/Account/ProfileSettings.cshtml index 9821eca..fc43736 100644 --- a/BlogIT.MVC/Views/Account/ProfileSettings.cshtml +++ b/BlogIT.MVC/Views/Account/ProfileSettings.cshtml @@ -58,7 +58,7 @@
- +
@@ -68,6 +68,16 @@ +
+ + + +
+
+ + + +
diff --git a/BlogIT.MVC/Views/Account/Register.cshtml b/BlogIT.MVC/Views/Account/Register.cshtml index eb6c450..81d875f 100644 --- a/BlogIT.MVC/Views/Account/Register.cshtml +++ b/BlogIT.MVC/Views/Account/Register.cshtml @@ -15,12 +15,12 @@
-

Party!

-

Приходите на вечеринки друзей и организовывайте свои.

+

BlogIT

+

News blog.

-
+

@@ -30,9 +30,15 @@

diff --git a/BlogIT.MVC/Views/Home/Index.cshtml b/BlogIT.MVC/Views/Home/Index.cshtml index 318412a..a71d5a0 100644 --- a/BlogIT.MVC/Views/Home/Index.cshtml +++ b/BlogIT.MVC/Views/Home/Index.cshtml @@ -1,50 +1,65 @@ @model HomePageViewModel + @{ ViewBag.Title = @Localizer["Title"]; } - + + +
+
+
+ +
+
-
-
-
-
-
-

@Localizer["LastNews"]

+
+
+

@Localizer["LastNews"]

+
-
- @foreach (NewsAnnotationViewModel lastNews in Model.LastNews) - { - -
- } + @foreach (NewsAnnotationViewModel lastNews in Model.LastNews) + { + +
+ } - + -
-
-

@Localizer["TopNews"]

+
+
+

@Localizer["TopNews"]

+
-
- @foreach (NewsAnnotationViewModel topNews in Model.TopNews) - { - -
+ @foreach (NewsAnnotationViewModel topNews in Model.TopNews) + { + +
- } + } +
+
+
+
+ +
+
+ +
+
@section Scripts { @@ -53,5 +68,21 @@ $('body').removeClass('full-screen'); $('body').addClass('blog-posts'); }); + + let myTags = JSON.parse('@Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model.ListTags))'); + + let tagCloud = TagCloud('.content', myTags); + + let selectDate = new Date("@(DateTime.MinValue.ToString("s"))") + + let myCalendar = new VanillaCalendar({ + selector: "#myCalendar", + selectDate: selectDate, + onSelect: (data, elem) => { + $('#DateCalendar').val(data.date); + location.href = "Home/List?DateCalendar=" + data.date; + } + }); + } \ No newline at end of file diff --git a/BlogIT.MVC/Views/Home/List.cshtml b/BlogIT.MVC/Views/Home/List.cshtml index 8d158ba..16afa96 100644 --- a/BlogIT.MVC/Views/Home/List.cshtml +++ b/BlogIT.MVC/Views/Home/List.cshtml @@ -46,6 +46,7 @@ diff --git a/BlogIT.MVC/Views/News/Edit.cshtml b/BlogIT.MVC/Views/News/Edit.cshtml index 18a33f5..6c10c10 100644 --- a/BlogIT.MVC/Views/News/Edit.cshtml +++ b/BlogIT.MVC/Views/News/Edit.cshtml @@ -33,7 +33,7 @@
- +
diff --git a/BlogIT.MVC/Views/News/Index.cshtml b/BlogIT.MVC/Views/News/Index.cshtml index b8b885b..6e3db0b 100644 --- a/BlogIT.MVC/Views/News/Index.cshtml +++ b/BlogIT.MVC/Views/News/Index.cshtml @@ -8,6 +8,7 @@ @{ string userId = UserManager.GetUserId(User); string imgFilePath = await _photoService.GetPathAvatarByUserId(userId); + string CurrentUserRating = ""; } @{ @@ -57,6 +58,26 @@ }
+
+ Star Rating + Star Rating + Star Rating + Star Rating + Star Rating + (@Model.RateCount) + @Model.RateAverage + +
+
+ @if (Model.CurrentUserRating > 0) + { + CurrentUserRating = "You rated this " + @Model.CurrentUserRating + " star(s)"; + } + + @CurrentUserRating +
+ +
@@ -87,10 +108,13 @@
-
+

Комментарии

+
+ +
@@ -145,7 +169,8 @@ let aImage = document.createElement('a'); aImage.classList.add("pull-left"); - + aImage.setAttribute("href", "/Account/Profile/" + chatMessage.userId); + let profilePhotoSmall = document.createElement('div'); profilePhotoSmall.classList.add("profile-photo-small"); @@ -165,10 +190,18 @@ let mediaBody = document.createElement('div'); mediaBody.classList.add("media-body"); + let aMediaHeading = document.createElement('a'); + aMediaHeading.setAttribute("href", "/Account/Profile/" + chatMessage.userId); + + let mediaHeading = document.createElement('h5'); mediaHeading.classList.add("media-heading"); mediaHeading.textContent = chatMessage.userName; - mediaBody.append(mediaHeading); + + aMediaHeading.append(mediaHeading); + + + mediaBody.append(aMediaHeading); let mediaHeadingLike = document.createElement('h5'); @@ -287,7 +320,7 @@ mediaHeadingLike.textContent = likeCount; mediaHeadingLike.classList.add("like-down"); } else { - mediaHeadingLike.textContent = ""; + mediaHeadingLike.textContent = ""; } @@ -331,7 +364,7 @@ mediaHeadingLike.textContent = likeCount; mediaHeadingLike.classList.add("like-down"); } else { - mediaHeadingLike.textContent = ""; + mediaHeadingLike.textContent = ""; } @@ -348,8 +381,83 @@ media.append(mediaBody); - document.getElementById('chatList').append(media); + document.getElementById('chatList').prepend(media); }; + + + + //star rating + $(function () { + + refilRating(); + + $("img.rating").mouseover(function () { + + giveRating($(this), "FilledStar.png"); + + $(this).css("cursor", "pointer"); + + }); + + $("img.rating").mouseout(function () { + + giveRating($(this), "EmptyStar.png"); + + refilRating(); + + }); + + + + $("img.rating").click(function (e) { + + // $("img.rating").unbind("mouseout mouseover click"); + + $(this).css('color', 'red'); + + // alert(e.currentTarget + ' was clicked!'); + + var url = "/News/PostRating?Rate=" + parseInt($(this).attr("id").slice(6)) + "&NewsId=" + parseInt($(this).attr("NewsId")); + + $.ajax({ + url: url, + contentType: "application/json", + method: "POST", + data: {}, + success: function (data) { + $("#result-rate").text(data); + }, + fail: function (data) { + console.log(data); + } + }) + + }); + + }); + + + + function giveRating(img, image) { + + img.attr("src", "/Files/" + image) + + .prevAll("img.rating").attr("src", "/Files/" + image); + img.nextAll("img.rating").attr("src", "/Files/" + "EmptyStar.png"); + + } + + function refilRating() { + + var av = $("#RateAverage").text(); + + if (av != "" || av != null) + { + var img = $("#rating" + av); + img.attr("src", "/Files/FilledStar.png").prevAll("img.rating").attr("src", "/Files/FilledStar.png"); + } + } + } \ No newline at end of file diff --git a/BlogIT.MVC/Views/News/List.cshtml b/BlogIT.MVC/Views/News/List.cshtml index 90b59bf..d34e55c 100644 --- a/BlogIT.MVC/Views/News/List.cshtml +++ b/BlogIT.MVC/Views/News/List.cshtml @@ -1,12 +1,13 @@ -@model NewsListViewModel +@using BlogIT.DB.Specifications +@model NewsListViewModel @{ ViewBag.Title = @Localizer["Title"]; } - +
@@ -24,7 +25,7 @@ {
- +
diff --git a/BlogIT.MVC/Views/Shared/_Layout.cshtml b/BlogIT.MVC/Views/Shared/_Layout.cshtml index 20e8a8e..6702e74 100644 --- a/BlogIT.MVC/Views/Shared/_Layout.cshtml +++ b/BlogIT.MVC/Views/Shared/_Layout.cshtml @@ -153,6 +153,8 @@ + + diff --git a/BlogIT.MVC/Views/Shared/_LoginPartial.cshtml b/BlogIT.MVC/Views/Shared/_LoginPartial.cshtml index 19639d3..3038e15 100644 --- a/BlogIT.MVC/Views/Shared/_LoginPartial.cshtml +++ b/BlogIT.MVC/Views/Shared/_LoginPartial.cshtml @@ -19,13 +19,13 @@ -