Browse Source

Blog module post images

pull/441/head
Yunus Emre Kalkan 8 years ago
parent
commit
a412b899ec
  1. 2
      .gitignore
  2. 586
      modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20180911082243_added_CoverImage.Designer.cs
  3. 23
      modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20180911082243_added_CoverImage.cs
  4. 4
      modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/BloggingTestAppDbContextModelSnapshot.cs
  5. 83069
      modules/blogging/app/Volo.BloggingTestApp/Logs/logs.txt
  6. 3
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/CreatePostDto.cs
  7. 2
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/PostDto.cs
  8. 2
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/PostWithDetailsDto.cs
  9. 2
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/UpdatePostDto.cs
  10. 2
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Posts/PostAppService.cs
  11. 6
      modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Posts/Post.cs
  12. 1
      modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/EntityFrameworkCore/BloggingDbContextModelBuilderExtensions.cs
  13. 31
      modules/blogging/src/Volo.Blogging.Web/Areas/Blog/Controllers/FilesController.cs
  14. 23
      modules/blogging/src/Volo.Blogging.Web/Areas/Blog/Helpers/ImageFormatHelper.cs
  15. 12
      modules/blogging/src/Volo.Blogging.Web/Areas/Blog/Models/FileUploadResult.cs
  16. 30
      modules/blogging/src/Volo.Blogging.Web/BloggingWebConsts.cs
  17. 72
      modules/blogging/src/Volo.Blogging.Web/Hosting/FileService.cs
  18. 57
      modules/blogging/src/Volo.Blogging.Web/Hosting/FormFileExtensions.cs
  19. 16
      modules/blogging/src/Volo.Blogging.Web/Hosting/IFileService.cs
  20. 2
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Detail.cshtml
  21. 16
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Edit.cshtml
  22. 7
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Edit.cshtml.cs
  23. 46
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Index.cshtml
  24. 14
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/New.cshtml
  25. 5
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/New.cshtml.cs
  26. 33
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/edit.js
  27. 33
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/new.js
  28. 1
      modules/blogging/src/Volo.Blogging.Web/Volo.Blogging.Web.csproj
  29. 2
      modules/blogging/test/Volo.Blogging.Application.Tests/Volo/Blogging/CommentAppService_Tests.cs

2
.gitignore

@ -265,3 +265,5 @@ framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Logs/*
modules/blog/app/Volo.BlogTestApp/Logs/*
nupkg/push.ps1
templates/mvc/src/MyCompanyName.MyProjectName.Web/Logs/logs.txt
modules/blogging/app/Volo.BloggingTestApp/wwwroot/files/*
modules/blogging/app/Volo.BloggingTestApp/Logs/logs.txt

586
modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20180911082243_added_CoverImage.Designer.cs

@ -0,0 +1,586 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Volo.BloggingTestApp.EntityFrameworkCore;
namespace Volo.BloggingTestApp.EntityFrameworkCore.Migrations
{
[DbContext(typeof(BloggingTestAppDbContext))]
[Migration("20180911082243_added_CoverImage")]
partial class added_CoverImage
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.1.1-rtm-30846")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(256);
b.Property<string>("NormalizedName")
.IsRequired()
.HasMaxLength(256);
b.Property<Guid?>("TenantId");
b.HasKey("Id");
b.HasIndex("NormalizedName");
b.ToTable("AbpRoles");
});
modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClaimType")
.IsRequired()
.HasMaxLength(256);
b.Property<string>("ClaimValue")
.HasMaxLength(1024);
b.Property<Guid>("RoleId");
b.Property<Guid?>("TenantId");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AbpRoleClaims");
});
modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount")
.ValueGeneratedOnAdd()
.HasColumnName("AccessFailedCount")
.HasDefaultValue(0);
b.Property<string>("ConcurrencyStamp")
.IsRequired()
.HasColumnName("ConcurrencyStamp")
.HasMaxLength(256);
b.Property<string>("Email")
.HasColumnName("Email")
.HasMaxLength(256);
b.Property<bool>("EmailConfirmed")
.ValueGeneratedOnAdd()
.HasColumnName("EmailConfirmed")
.HasDefaultValue(false);
b.Property<string>("ExtraProperties")
.HasColumnName("ExtraProperties");
b.Property<bool>("LockoutEnabled")
.ValueGeneratedOnAdd()
.HasColumnName("LockoutEnabled")
.HasDefaultValue(false);
b.Property<DateTimeOffset?>("LockoutEnd");
b.Property<string>("NormalizedEmail")
.HasColumnName("NormalizedEmail")
.HasMaxLength(256);
b.Property<string>("NormalizedUserName")
.IsRequired()
.HasColumnName("NormalizedUserName")
.HasMaxLength(256);
b.Property<string>("PasswordHash")
.HasColumnName("PasswordHash")
.HasMaxLength(256);
b.Property<string>("PhoneNumber")
.HasColumnName("PhoneNumber")
.HasMaxLength(16);
b.Property<bool>("PhoneNumberConfirmed")
.ValueGeneratedOnAdd()
.HasColumnName("PhoneNumberConfirmed")
.HasDefaultValue(false);
b.Property<string>("SecurityStamp")
.IsRequired()
.HasColumnName("SecurityStamp")
.HasMaxLength(256);
b.Property<Guid?>("TenantId")
.HasColumnName("TenantId");
b.Property<bool>("TwoFactorEnabled")
.ValueGeneratedOnAdd()
.HasColumnName("TwoFactorEnabled")
.HasDefaultValue(false);
b.Property<string>("UserName")
.IsRequired()
.HasColumnName("UserName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("Email");
b.HasIndex("NormalizedEmail");
b.HasIndex("NormalizedUserName");
b.HasIndex("UserName");
b.ToTable("AbpUsers");
});
modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ClaimType")
.IsRequired()
.HasMaxLength(256);
b.Property<string>("ClaimValue")
.HasMaxLength(1024);
b.Property<Guid?>("TenantId");
b.Property<Guid>("UserId");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AbpUserClaims");
});
modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b =>
{
b.Property<Guid>("UserId");
b.Property<string>("LoginProvider")
.HasMaxLength(64);
b.Property<string>("ProviderDisplayName")
.HasMaxLength(128);
b.Property<string>("ProviderKey")
.IsRequired()
.HasMaxLength(196);
b.Property<Guid?>("TenantId");
b.HasKey("UserId", "LoginProvider");
b.HasIndex("LoginProvider", "ProviderKey");
b.ToTable("AbpUserLogins");
});
modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b =>
{
b.Property<Guid>("UserId");
b.Property<Guid>("RoleId");
b.Property<Guid?>("TenantId");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId", "UserId");
b.ToTable("AbpUserRoles");
});
modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b =>
{
b.Property<Guid>("UserId");
b.Property<string>("LoginProvider")
.HasMaxLength(128);
b.Property<string>("Name");
b.Property<Guid?>("TenantId");
b.Property<string>("Value");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AbpUserTokens");
});
modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128);
b.Property<string>("ProviderKey")
.IsRequired()
.HasMaxLength(64);
b.Property<string>("ProviderName")
.IsRequired()
.HasMaxLength(64);
b.Property<Guid?>("TenantId");
b.HasKey("Id");
b.HasIndex("Name", "ProviderName", "ProviderKey");
b.ToTable("AbpPermissionGrants");
});
modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128);
b.Property<string>("ProviderKey")
.HasMaxLength(64);
b.Property<string>("ProviderName")
.HasMaxLength(64);
b.Property<string>("Value")
.IsRequired()
.HasMaxLength(2048);
b.HasKey("Id");
b.HasIndex("Name", "ProviderName", "ProviderKey");
b.ToTable("AbpSettings");
});
modelBuilder.Entity("Volo.Blogging.Blogs.Blog", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("CreationTime")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnName("DeletionTime");
b.Property<string>("Description")
.HasColumnName("Description")
.HasMaxLength(1024);
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnName("IsDeleted")
.HasDefaultValue(false);
b.Property<DateTime?>("LastModificationTime")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnName("LastModifierId");
b.Property<string>("Name")
.IsRequired()
.HasColumnName("Name")
.HasMaxLength(256);
b.Property<string>("ShortName")
.IsRequired()
.HasColumnName("ShortName")
.HasMaxLength(32);
b.HasKey("Id");
b.ToTable("BlgBlogs");
});
modelBuilder.Entity("Volo.Blogging.Comments.Comment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("CreationTime")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnName("DeletionTime");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnName("IsDeleted")
.HasDefaultValue(false);
b.Property<DateTime?>("LastModificationTime")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnName("LastModifierId");
b.Property<Guid>("PostId")
.HasColumnName("PostId");
b.Property<Guid?>("RepliedCommentId")
.HasColumnName("RepliedCommentId");
b.Property<string>("Text")
.IsRequired()
.HasColumnName("Text")
.HasMaxLength(1024);
b.HasKey("Id");
b.HasIndex("PostId");
b.HasIndex("RepliedCommentId");
b.ToTable("BlgComments");
});
modelBuilder.Entity("Volo.Blogging.Posts.Post", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<Guid>("BlogId")
.HasColumnName("BlogId");
b.Property<string>("Content")
.HasColumnName("Content")
.HasMaxLength(1048576);
b.Property<string>("CoverImage")
.IsRequired()
.HasColumnName("CoverImage");
b.Property<DateTime>("CreationTime")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnName("DeletionTime");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnName("IsDeleted")
.HasDefaultValue(false);
b.Property<DateTime?>("LastModificationTime")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnName("LastModifierId");
b.Property<int>("ReadCount");
b.Property<string>("Title")
.IsRequired()
.HasColumnName("Title")
.HasMaxLength(512);
b.Property<string>("Url")
.IsRequired()
.HasColumnName("Url")
.HasMaxLength(64);
b.HasKey("Id");
b.HasIndex("BlogId");
b.ToTable("BlgPosts");
});
modelBuilder.Entity("Volo.Blogging.Posts.PostTag", b =>
{
b.Property<Guid>("PostId")
.HasColumnName("PostId");
b.Property<Guid>("TagId")
.HasColumnName("TagId");
b.Property<DateTime>("CreationTime");
b.Property<Guid?>("CreatorId");
b.HasKey("PostId", "TagId");
b.HasIndex("TagId");
b.ToTable("BlgPostTags");
});
modelBuilder.Entity("Volo.Blogging.Tagging.Tag", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime>("CreationTime")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnName("CreatorId");
b.Property<Guid?>("DeleterId")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnName("DeletionTime");
b.Property<string>("Description")
.HasColumnName("Description")
.HasMaxLength(512);
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnName("IsDeleted")
.HasDefaultValue(false);
b.Property<DateTime?>("LastModificationTime")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnName("LastModifierId");
b.Property<string>("Name")
.IsRequired()
.HasColumnName("Name")
.HasMaxLength(64);
b.Property<int>("UsageCount")
.HasColumnName("UsageCount");
b.HasKey("Id");
b.ToTable("BlgTags");
});
modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b =>
{
b.HasOne("Volo.Abp.Identity.IdentityRole")
.WithMany("Claims")
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b =>
{
b.HasOne("Volo.Abp.Identity.IdentityUser")
.WithMany("Claims")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b =>
{
b.HasOne("Volo.Abp.Identity.IdentityUser")
.WithMany("Logins")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b =>
{
b.HasOne("Volo.Abp.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Volo.Abp.Identity.IdentityUser")
.WithMany("Roles")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b =>
{
b.HasOne("Volo.Abp.Identity.IdentityUser")
.WithMany("Tokens")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Volo.Blogging.Comments.Comment", b =>
{
b.HasOne("Volo.Blogging.Posts.Post")
.WithMany()
.HasForeignKey("PostId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Volo.Blogging.Comments.Comment")
.WithMany()
.HasForeignKey("RepliedCommentId");
});
modelBuilder.Entity("Volo.Blogging.Posts.Post", b =>
{
b.HasOne("Volo.Blogging.Blogs.Blog")
.WithMany()
.HasForeignKey("BlogId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Volo.Blogging.Posts.PostTag", b =>
{
b.HasOne("Volo.Blogging.Posts.Post")
.WithMany("Tags")
.HasForeignKey("PostId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Volo.Blogging.Tagging.Tag")
.WithMany()
.HasForeignKey("TagId")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

23
modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20180911082243_added_CoverImage.cs

@ -0,0 +1,23 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace Volo.BloggingTestApp.EntityFrameworkCore.Migrations
{
public partial class added_CoverImage : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "CoverImage",
table: "BlgPosts",
nullable: false,
defaultValue: "");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "CoverImage",
table: "BlgPosts");
}
}
}

4
modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/BloggingTestAppDbContextModelSnapshot.cs

@ -391,6 +391,10 @@ namespace Volo.BloggingTestApp.EntityFrameworkCore.Migrations
.HasColumnName("Content")
.HasMaxLength(1048576);
b.Property<string>("CoverImage")
.IsRequired()
.HasColumnName("CoverImage");
b.Property<DateTime>("CreationTime")
.HasColumnName("CreationTime");

83069
modules/blogging/app/Volo.BloggingTestApp/Logs/logs.txt

File diff suppressed because one or more lines are too long

3
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/CreatePostDto.cs

@ -11,6 +11,9 @@ namespace Volo.Blogging.Posts
[StringLength(PostConsts.MaxTitleLength)]
public string Title { get; set; }
[Required]
public string CoverImage { get; set; }
[Required]
[StringLength(PostConsts.MaxUrlLength)]
public string Url { get; set; }

2
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/PostDto.cs

@ -9,6 +9,8 @@ namespace Volo.Blogging.Posts
public string Title { get; protected set; }
public string CoverImage { get; protected set; }
public string Url { get; set; }
public int ReadCount { get; set; }

2
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/PostWithDetailsDto.cs

@ -11,6 +11,8 @@ namespace Volo.Blogging.Posts
public string Title { get; set; }
public string CoverImage { get; set; }
public string Url { get; set; }
public string Content { get; set; }

2
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/UpdatePostDto.cs

@ -8,6 +8,8 @@ namespace Volo.Blogging.Posts
public string Title { get; set; }
public string CoverImage { get; set; }
public string Url { get; set; }
public string Content { get; set; }

2
modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Posts/PostAppService.cs

@ -87,6 +87,7 @@ namespace Volo.Blogging.Posts
post.SetTitle(input.Title);
post.SetUrl(input.Url);
post.Content = input.Content;
post.CoverImage = input.CoverImage;
post = await _postRepository.UpdateAsync(post);
@ -104,6 +105,7 @@ namespace Volo.Blogging.Posts
blogId: input.BlogId,
creatorId: CurrentUser.GetId(),
title: input.Title,
coverImage: input.CoverImage,
url: input.Url
) {Content = input.Content};

6
modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Posts/Post.cs

@ -13,6 +13,9 @@ namespace Volo.Blogging.Posts
[NotNull]
public virtual string Url { get; protected set; }
[NotNull]
public virtual string CoverImage { get; set; }
[NotNull]
public virtual string Title { get; protected set; }
@ -29,13 +32,14 @@ namespace Volo.Blogging.Posts
}
public Post(Guid id, Guid blogId, Guid creatorId, [NotNull] string title, [NotNull] string url)
public Post(Guid id, Guid blogId, Guid creatorId, [NotNull] string title, [NotNull] string coverImage, [NotNull] string url)
{
Id = id;
CreatorId = creatorId;
BlogId = blogId;
Title = Check.NotNullOrWhiteSpace(title, nameof(title));
Url = Check.NotNullOrWhiteSpace(url, nameof(url));
CoverImage = Check.NotNullOrWhiteSpace(coverImage, nameof(coverImage));
Tags = new Collection<PostTag>();
}

1
modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/EntityFrameworkCore/BloggingDbContextModelBuilderExtensions.cs

@ -40,6 +40,7 @@ namespace Volo.Blogging.EntityFrameworkCore
b.Property(x => x.BlogId).HasColumnName(nameof(Post.BlogId));
b.Property(x => x.Title).IsRequired().HasMaxLength(PostConsts.MaxTitleLength).HasColumnName(nameof(Post.Title));
b.Property(x => x.CoverImage).IsRequired().HasColumnName(nameof(Post.CoverImage));
b.Property(x => x.Url).IsRequired().HasMaxLength(PostConsts.MaxUrlLength).HasColumnName(nameof(Post.Url));
b.Property(x => x.Content).IsRequired(false).HasMaxLength(PostConsts.MaxContentLength).HasColumnName(nameof(Post.Content));

31
modules/blogging/src/Volo.Blogging.Web/Areas/Blog/Controllers/FilesController.cs

@ -0,0 +1,31 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Blogging.Areas.Blog.Models;
using Volo.Blogging.Hosting;
namespace Volo.Blogging.Areas.Blog.Controllers
{
[Area("Blog")]
[Route("Blog/[controller]/[action]")]
public class FilesController : AbpController
{
private readonly IFileService _fileService;
public FilesController(IFileService fileService)
{
_fileService = fileService;
}
[HttpPost]
public async Task<JsonResult> UploadImage(IFormFile file)
{
file.ValidateImage(out var fileBytes);
var fileUrl = await _fileService.SaveFileAsync(fileBytes, file.FileName);
return Json(new FileUploadResult(fileUrl));
}
}
}

23
modules/blogging/src/Volo.Blogging.Web/Areas/Blog/Helpers/ImageFormatHelper.cs

@ -0,0 +1,23 @@
using System.Collections.Generic;
using System.Drawing.Imaging;
using System.IO;
namespace Volo.Blogging.Areas.Blog.Helpers
{
public class ImageFormatHelper
{
public static ImageFormat GetImageRawFormat(byte[] fileBytes)
{
using (var memoryStream = new MemoryStream(fileBytes))
{
return System.Drawing.Image.FromStream(memoryStream).RawFormat;
}
}
public static bool IsValidImage(byte[] fileBytes, ICollection<ImageFormat> validFormats)
{
var imageFormat = GetImageRawFormat(fileBytes);
return validFormats.Contains(imageFormat);
}
}
}

12
modules/blogging/src/Volo.Blogging.Web/Areas/Blog/Models/FileUploadResult.cs

@ -0,0 +1,12 @@
namespace Volo.Blogging.Areas.Blog.Models
{
public class FileUploadResult
{
public string FileUrl { get; set; }
public FileUploadResult(string fileUrl)
{
FileUrl = fileUrl;
}
}
}

30
modules/blogging/src/Volo.Blogging.Web/BloggingWebConsts.cs

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing.Imaging;
using System.Linq;
namespace Volo.Blogging
{
public class BloggingWebConsts
{
public class FileUploading
{
public const string DefaultFileUploadFolderName = "files";
public static readonly ICollection<ImageFormat> AllowedImageUploadFormats = new Collection<ImageFormat>
{
ImageFormat.Jpeg,
ImageFormat.Png,
ImageFormat.Gif,
ImageFormat.Bmp
};
public static string AllowedImageFormatsJoint => string.Join(",", AllowedImageUploadFormats.Select(x => x.ToString()));
public const int MaxFileSize = 5242880; //5MB
public static int MaxFileSizeAsMegabytes => Convert.ToInt32((MaxFileSize / 1024f) / 1024f);
}
}
}

72
modules/blogging/src/Volo.Blogging.Web/Hosting/FileService.cs

@ -0,0 +1,72 @@
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Volo.Abp;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
namespace Volo.Blogging.Hosting
{
public class FileService : IFileService, ITransientDependency
{
private readonly IHostingEnvironment _hostingEnvironment;
private readonly IGuidGenerator _guidGenerator;
public FileService(IHostingEnvironment hostingEnvironment, IGuidGenerator guidGenerator)
{
_hostingEnvironment = hostingEnvironment;
_guidGenerator = guidGenerator;
}
public string FileUploadDirectory
{
get
{
var uploadDirectory = Path.Combine(_hostingEnvironment.WebRootPath, BloggingWebConsts.FileUploading.DefaultFileUploadFolderName);
if (!Directory.Exists(uploadDirectory))
{
Directory.CreateDirectory(uploadDirectory);
}
return uploadDirectory;
}
}
public string GenerateUniqueFileName(string extension, string prefix = null, string postfix = null)
{
return prefix + _guidGenerator.Create().ToString("N") + postfix + extension;
}
public async Task<string> SaveFormFileAndGetUrlAsync(IFormFile file)
{
var uniqueFileName = await SaveFileInternalAsync(file.FileName, file.AsBytes());
return GetFileUrl(uniqueFileName);
}
public async Task<string> SaveFileAsync(byte[] fileBytes, string originalFileName)
{
if (fileBytes == null || fileBytes.Length == 0)
{
throw new UserFriendlyException("File is empty!");
}
var uniqueFileName = await SaveFileInternalAsync(originalFileName, fileBytes);
return GetFileUrl(uniqueFileName);
}
private static string GetFileUrl(string uniqueFileName)
{
return "/" + BloggingWebConsts.FileUploading.DefaultFileUploadFolderName + "/" + uniqueFileName;
}
private async Task<string> SaveFileInternalAsync(string originalFileName, byte[] fileBytes)
{
var uniqueFileName = GenerateUniqueFileName(Path.GetExtension(originalFileName));
var filePath = Path.Combine(FileUploadDirectory, uniqueFileName);
File.WriteAllBytes(filePath, fileBytes); //TODO: Previously was using WriteAllBytesAsync, but it's only in .netcore.
return uniqueFileName;
}
}
}

57
modules/blogging/src/Volo.Blogging.Web/Hosting/FormFileExtensions.cs

@ -0,0 +1,57 @@
using System.IO;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Http;
using Volo.Abp;
using Volo.Blogging.Areas.Blog.Helpers;
namespace Volo.Blogging.Hosting
{
public static class FormFileExtensions
{
public static byte[] AsBytes(this IFormFile file)
{
byte[] fileBytes;
using (var stream = file.OpenReadStream())
{
fileBytes = stream.GetAllBytes();
}
return fileBytes;
}
public static void ValidateImage([CanBeNull] this IFormFile file, out byte[] fileBytes)
{
fileBytes = null;
if (file == null)
{
throw new UserFriendlyException("No file found!");
}
if (file.Length <= 0)
{
throw new UserFriendlyException("File is empty!");
}
if (!file.ContentType.Contains("image"))
{
throw new UserFriendlyException("Not a valid image!");
}
using (var stream = file.OpenReadStream())
{
fileBytes = stream.GetAllBytes();
}
if (!ImageFormatHelper.IsValidImage(fileBytes, BloggingWebConsts.FileUploading.AllowedImageUploadFormats))
{
throw new UserFriendlyException("Not a valid image format!");
}
if (file.Length > BloggingWebConsts.FileUploading.MaxFileSize)
{
throw new UserFriendlyException($"File exceeds the maximum upload size ({BloggingWebConsts.FileUploading.MaxFileSizeAsMegabytes} MB)!");
}
}
}
}

16
modules/blogging/src/Volo.Blogging.Web/Hosting/IFileService.cs

@ -0,0 +1,16 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace Volo.Blogging.Hosting
{
public interface IFileService
{
string FileUploadDirectory { get; }
string GenerateUniqueFileName(string extension, string prefix = null, string postfix = null);
Task<string> SaveFormFileAndGetUrlAsync(IFormFile file);
Task<string> SaveFileAsync(byte[] fileBytes, string originalFileName);
}
}

2
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Detail.cshtml

@ -24,7 +24,7 @@
<div class="col-md-12 mx-auto">
<section class="hero-section">
<div class="hero-articles">
<div class="img-container" style="min-height: 480px; background: url(https://placeimg.com/800/435/tech) center center no-repeat; background-size: cover; ">
<div class="img-container" style="min-height: 480px; background: url(@Model.Post.CoverImage) center center no-repeat; background-size: cover; ">
</div>
<div class="hero-content">
<p class="tags">

16
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Edit.cshtml

@ -23,6 +23,20 @@
<form method="post" id="edit-post-form">
<abp-input asp-for="Post.Title" auto-focus="true" />
<abp-input asp-for="Post.Url" />
<abp-input asp-for="Post.CoverImage" />
<abp-row>
<abp-column size-sm="_9">
<div class="form-group">
<label for="CoverImageFile">@L["CoverImage"]</label>
<input class="form-control" type="file" id="CoverImageFile" />
</div>
</abp-column>
<abp-column size-sm="_3">
<img id="CoverImage" src="@Model.Post.CoverImage" height="80" width="160" />
</abp-column>
</abp-row>
<abp-input asp-for="Post.Tags" />
<abp-input asp-for="Post.BlogId" />
<abp-input asp-for="Post.Id" />
@ -54,4 +68,4 @@
</div>
</form>
</div>
</div>

7
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Edit.cshtml.cs

@ -43,6 +43,7 @@ namespace Volo.Blogging.Pages.Blog.Posts
BlogId = Post.BlogId,
Title = Post.Title,
Url = Post.Url,
CoverImage = Post.CoverImage,
Content = Post.Content,
Tags = Post.Tags
};
@ -56,9 +57,11 @@ namespace Volo.Blogging.Pages.Blog.Posts
public class EditPostViewModel
{
[Required]
[HiddenInput]
public Guid Id { get; set; }
[Required]
[HiddenInput]
public Guid BlogId { get; set; }
@ -66,6 +69,10 @@ namespace Volo.Blogging.Pages.Blog.Posts
[StringLength(PostConsts.MaxTitleLength)]
public string Title { get; set; }
[Required]
[HiddenInput]
public string CoverImage { get; set; }
[Required]
[StringLength(PostConsts.MaxUrlLength)]
public string Url { get; set; }

46
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Index.cshtml

@ -39,7 +39,7 @@
<div class="hero-articles">
<div class="img-container">
<a asp-page="./Detail" asp-route-postUrl="@post.Url" asp-route-blogShortName="@Model.BlogShortName">
<img src="https://placeimg.com/680/680/tech" class="hero-article-img">
<img src="@post.CoverImage" class="hero-article-img">
</a>
</div>
<div class="hero-content">
@ -61,31 +61,23 @@
<div class="article-owner">
<div class="article-infos">
<div class="user-card">
<a href="#">
<a>
<img src="https://placeimg.com/120/120/people" class="article-avatar">
</a>
<a href="#">
<a>
<strong>Armağan Ünlü</strong>, @ConvertDatetimeToTimeAgo(post.CreationTime)
</a>
<span class="seperator">|</span>
<a href="#">
<a>
<i class="fa fa-eye"></i> @post.ReadCount @L["Read"]
</a>
<span class="seperator">|</span>
<a href="#">
<a>
<i class="fa fa-comment"></i> @post.CommentCount @L["Comment"]
</a>
</div>
</div>
</div>
<div class="user-link-icons">
<a class="add-bookmark" href="#">
<i class="fa fa-bookmark"></i>
</a>
<a class="share-article" href="#">
<i class="fa fa-share-alt"></i>
</a>
</div>
</div>
</div>
}
@ -112,16 +104,8 @@
<div class="col-4">
<div class="img-container" style="height: 80px;">
<a asp-page="./Detail" asp-route-postUrl="@post.Url" asp-route-blogShortName="@Model.BlogShortName">
<img src="https://placeimg.com/480/270/tech" class="box-article-img">
<img src="@post.CoverImage" class="box-article-img">
</a>
<div class="user-link-icons">
<a class="add-bookmark" href="#">
<i class="fa fa-bookmark"></i>
</a>
<a class="share-article" href="#">
<i class="fa fa-share-alt"></i>
</a>
</div>
</div>
</div>
<div class="col-8 pl-0">
@ -131,7 +115,7 @@
<div class="article-owner">
<div class="article-infos">
<div class="user-card">
<a href="#">
<a asp-page="./Detail" asp-route-postUrl="@post.Url" asp-route-blogShortName="@Model.BlogShortName">
<strong>Armağan Ünlü</strong>, @ConvertDatetimeToTimeAgo(post.CreationTime)
</a>
</div>
@ -172,16 +156,8 @@
<div class="col-md-4 order-md-@(oddPost?"last":"first")">
<div class="img-container">
<a asp-page="./Detail" asp-route-postUrl="@post.Url" asp-route-blogShortName="@Model.BlogShortName">
<img src="https://placeimg.com/480/270/tech" class="box-article-img">
<img src="@post.CoverImage" class="box-article-img">
</a>
<div class="user-link-icons">
<a class="add-bookmark" href="#">
<i class="fa fa-bookmark"></i>
</a>
<a class="share-article" href="#">
<i class="fa fa-share-alt"></i>
</a>
</div>
</div>
</div>
<div class="col-md-8 order-md-@(oddPost?"first":"last")">
@ -203,15 +179,15 @@
<a href="#">
<img src="https://placeimg.com/120/120/pople" class="article-avatar">
</a>
<a href="#">
<a>
<strong>Armağan Ünlü</strong>, @ConvertDatetimeToTimeAgo(post.CreationTime)
</a>
<span class="seperator">|</span>
<a href="#">
<a>
<i class="fa fa-eye"></i> @post.ReadCount
</a>
<span class="seperator">|</span>
<a href="#">
<a>
<i class="fa fa-comment"></i> @post.CommentCount
</a>
</div>

14
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/New.cshtml

@ -21,8 +21,20 @@
<form method="post" id="new-post-form">
<abp-input asp-for="Post.BlogId" />
<abp-input asp-for="Post.Title" auto-focus="true" />
<abp-input asp-for="Post.Url" />
<abp-input asp-for="Post.CoverImage" />
<abp-row>
<abp-column size-sm="_9">
<div class="form-group">
<label for="CoverImageFile">@L["CoverImage"]</label>
<input class="form-control" type="file" id="CoverImageFile" />
</div>
</abp-column>
<abp-column size-sm="_3">
<img id="CoverImage" src="@Model.Post.CoverImage" height="80" width="160" style="display:none" />
</abp-column>
</abp-row>
<abp-input asp-for="Post.Content" />
<abp-input asp-for="Post.Tags" />

5
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/New.cshtml.cs

@ -47,6 +47,7 @@ namespace Volo.Blogging.Pages.Blog.Posts
public class CreatePostViewModel
{
[Required]
[HiddenInput]
public Guid BlogId { get; set; }
@ -54,6 +55,10 @@ namespace Volo.Blogging.Pages.Blog.Posts
[StringLength(PostConsts.MaxTitleLength)]
public string Title { get; set; }
[Required]
[HiddenInput]
public string CoverImage { get; set; }
[Required]
[StringLength(PostConsts.MaxUrlLength)]
public string Url { get; set; }

33
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/edit.js

@ -6,13 +6,44 @@
var $form = $container.find("form#edit-post-form");
var editorDataKey = "tuiEditor";
var setCoverImage = function (file) {
console.log(file.fileUrl);
$('#Post_CoverImage').val(file.fileUrl);
$("#CoverImage").attr("src", file.fileUrl);
};
var uploadCoverImage = function (file) {
var formData = new FormData();
formData.append('file', file);
$.ajax({
type: "POST",
url: "/Blog/Files/UploadImage",
data: formData,
contentType: false,
processData: false,
success: function (response) {
setCoverImage(response);
}
});
};
$('#CoverImageFile').change(function () {
if (!$('#CoverImageFile').prop('files').length) {
return;
}
var file = $('#CoverImageFile').prop('files')[0];
uploadCoverImage(file);
});
var uploadImage = function (file, callbackFn) {
var formData = new FormData();
formData.append('file', file);
$.ajax({
type: "POST",
url: "",
url: "/Blog/Files/UploadImage",
data: formData,
contentType: false,
processData: false,

33
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/new.js

@ -7,13 +7,44 @@
var $form = $container.find("form#new-post-form");
var editorDataKey = "tuiEditor";
var setCoverImage = function (file) {
console.log(file.fileUrl);
$('#Post_CoverImage').val(file.fileUrl);
$("#CoverImage").attr("src", file.fileUrl);
$("#CoverImage").show();
};
var uploadCoverImage = function (file) {
var formData = new FormData();
formData.append('file', file);
$.ajax({
type: "POST",
url: "/Blog/Files/UploadImage",
data: formData,
contentType: false,
processData: false,
success: function (response) {
setCoverImage(response);
}
});
};
$('#CoverImageFile').change(function () {
if (!$('#CoverImageFile').prop('files').length) {
return;
}
var file = $('#CoverImageFile').prop('files')[0];
uploadCoverImage(file);
});
var uploadImage = function (file, callbackFn) {
var formData = new FormData();
formData.append('file', file);
$.ajax({
type: "POST",
url: "",
url: "/Blog/Files/UploadImage",
data: formData,
contentType: false,
processData: false,

1
modules/blogging/src/Volo.Blogging.Web/Volo.Blogging.Web.csproj

@ -19,6 +19,7 @@
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AutoMapper\Volo.Abp.AutoMapper.csproj" />
<ProjectReference Include="..\Volo.Blogging.HttpApi\Volo.Blogging.HttpApi.csproj" />
<PackageReference Include="CommonMark.NET" Version="0.15.1" />
<PackageReference Include="System.Drawing.Common" Version="4.5.0" />
</ItemGroup>
<ItemGroup>

2
modules/blogging/test/Volo.Blogging.Application.Tests/Volo/Blogging/CommentAppService_Tests.cs

@ -23,7 +23,7 @@ namespace Volo.Blogging
[Fact]
public async Task Should_Get_List_Of_Comments()
{
var post = await _postRepository.InsertAsync(new Post(Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid(), "asd", "asd"));
var post = await _postRepository.InsertAsync(new Post(Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid(), "asd", "asd", "asd"));
var comment1 = await _commentRepository.InsertAsync(new Comment(Guid.NewGuid(), post.Id, Guid.Empty, "qweasd"));
var comment2 = await _commentRepository.InsertAsync(new Comment(Guid.NewGuid(), post.Id, comment1.Id, "qweasd"));

Loading…
Cancel
Save