Browse Source

Merge branch 'master' of https://github.com/volosoft/abp

pull/441/head
Alper Ebicoglu 8 years ago
parent
commit
1cf6ef5e59
  1. 2
      README.md
  2. 30
      docs/docs-nav.json
  3. 4
      framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AutoMapperExpressionExtensions.cs
  4. 25
      framework/src/Volo.Abp.Core/Volo/Abp/Modularity/AbpModule.cs
  5. 625
      modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20180912113852_Added_BlogUsers.Designer.cs
  6. 35
      modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20180912113852_Added_BlogUsers.cs
  7. 39
      modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/BloggingTestAppDbContextModelSnapshot.cs
  8. 8575
      modules/blogging/app/Volo.BloggingTestApp/Logs/logs.txt
  9. 16
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/BloggingApplicationContractsModule.cs
  10. 1
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/BloggingPermissions.cs
  11. 4
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Blogs/Dtos/BlogDto.cs
  12. 5
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Blogs/Dtos/CreateBlogDto.cs
  13. 15
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Blogs/Dtos/UpdateBlogDto.cs
  14. 9
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Blogs/IBlogAppService.cs
  15. 5
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Comments/Dtos/CommentWithDetailsDto.cs
  16. 4
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Comments/Dtos/CommentWithRepliesDto.cs
  17. 4
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Comments/ICommentAppService.cs
  18. 24
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/BlogUserDto.cs
  19. 2
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/GetPostInput.cs
  20. 2
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/IPostAppService.cs
  21. 2
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/PostWithDetailsDto.cs
  22. 5
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/BloggingApplicationAutoMapperProfile.cs
  23. 18
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/BloggingApplicationModule.cs
  24. 33
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Blogs/BlogAppService.cs
  25. 79
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Comments/CommentAppService.cs
  26. 61
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Comments/CommentAuthorizationHandler.cs
  27. 13
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/CommonOperations.cs
  28. 93
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Posts/PostAppService.cs
  29. 61
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Posts/PostAuthorizationHandler.cs
  30. 1
      modules/blogging/src/Volo.Blogging.Domain/Volo.Blogging.Domain.csproj
  31. 6
      modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Blogs/Blog.cs
  32. 5
      modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Blogs/IBlogRepository.cs
  33. 2
      modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Comments/ICommentRepository.cs
  34. 7
      modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Posts/IPostTagRepository.cs
  35. 4
      modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Tagging/ITagRepository.cs
  36. 43
      modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Users/BlogUser.cs
  37. 22
      modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Users/BlogUserLookupService.cs
  38. 9
      modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Users/IBlogUserLookupService.cs
  39. 14
      modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Users/IBlogUserRepository.cs
  40. 1
      modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo.Blogging.EntityFrameworkCore.csproj
  41. 17
      modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Blogs/EfCoreBlogRepository.cs
  42. 6
      modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Comments/EfCoreCommentRepository.cs
  43. 3
      modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/EntityFrameworkCore/BloggingDbContext.cs
  44. 10
      modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/EntityFrameworkCore/BloggingDbContextModelBuilderExtensions.cs
  45. 3
      modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/EntityFrameworkCore/IBloggingDbContext.cs
  46. 17
      modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Posts/EfCorePostTagRepository.cs
  47. 15
      modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Tagging/EfCoreTagRepository.cs
  48. 27
      modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Users/EfCoreQaUserRepository.cs
  49. 1
      modules/blogging/src/Volo.Blogging.HttpApi.Client/Volo.Blogging.HttpApi.Client.csproj
  50. 10
      modules/blogging/src/Volo.Blogging.HttpApi.Client/Volo/Blogging/BloggingHttpApiClientModule.cs
  51. 1
      modules/blogging/src/Volo.Blogging.HttpApi/Volo.Blogging.HttpApi.csproj
  52. 5
      modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/BloggingHttpApiModule.cs
  53. 77
      modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/BlogsController.cs
  54. 6
      modules/blogging/src/Volo.Blogging.Web/AbpBloggingWebAutoMapperProfile.cs
  55. 7
      modules/blogging/src/Volo.Blogging.Web/BloggingMenuContributor.cs
  56. 8
      modules/blogging/src/Volo.Blogging.Web/BloggingWebModule.cs
  57. 24
      modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/en.json
  58. 24
      modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/tr.json
  59. 20
      modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/Create.cshtml
  60. 50
      modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/Create.cshtml.cs
  61. 20
      modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/Edit.cshtml
  62. 65
      modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/Edit.cshtml.cs
  63. 44
      modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/Index.cshtml
  64. 12
      modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/Index.cshtml.cs
  65. 14
      modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/create.js
  66. 14
      modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/edit.js
  67. 86
      modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/index.js
  68. 66
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/BloggingPage.cs
  69. 6
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Index.cshtml
  70. 32
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Index.cshtml.cs
  71. 233
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Detail.cshtml
  72. 29
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Detail.cshtml.cs
  73. 291
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Index.cshtml
  74. 7
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Index.cshtml.cs
  75. 1
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/New.cshtml.cs
  76. 9
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/detail.js
  77. 9
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/new.css.map
  78. 3
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/new.min.css
  79. 2
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Shared/Scripts/blog.js
  80. 169
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Shared/Styles/_bootstrap-overwrite.scss
  81. 40
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Shared/Styles/_header.scss
  82. 140
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Shared/Styles/_home.css
  83. 1
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Shared/Styles/_home.min.css
  84. 88
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Shared/Styles/_home.scss
  85. 30
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Shared/Styles/_post.scss
  86. 289
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Shared/Styles/blog.css
  87. 24
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Shared/Styles/blog.css.map
  88. 2
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Shared/Styles/blog.min.css
  89. 18
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Shared/Styles/blog.scss
  90. 2
      modules/blogging/src/Volo.Blogging.Web/Volo.Blogging.Web.csproj
  91. 4
      modules/blogging/src/Volo.Blogging.Web/compilerconfig.json
  92. 2
      modules/docs/src/Volo.Docs.Application/Volo/Docs/Documents/GithubDocumentStore.cs
  93. 1
      modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Projects/ProjectConsts.cs
  94. 1
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/DocsConsts.cs
  95. 3
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/DocsDomainModule.cs
  96. 22
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Project.cs
  97. 1
      templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj
  98. 2
      templates/mvc/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs

2
README.md

@ -1,6 +1,6 @@
# ABP # ABP
[![Build Status](https://travis-ci.org/volosoft/abp.svg?branch=master)](https://travis-ci.org/volosoft/abp) [![Build Status](http://vjenkins.dynu.net:5480/job/abp/badge/icon)](http://vjenkins.dynu.net:5480/blue/organizations/jenkins/abp/activity)
This project is the next generation of the [ASP.NET Boilerplate](https://aspnetboilerplate.com/) web application framework. This project is the next generation of the [ASP.NET Boilerplate](https://aspnetboilerplate.com/) web application framework.

30
docs/docs-nav.json

@ -0,0 +1,30 @@
{
"items": [{
"text": "Getting Started",
"items": [{
"text": "From Startup Templates",
"items": [{
"text": "ASP.NET Core MVC",
"path": "Getting-Started-AspNetCore-MVC-Template.md"
}]
},{
"text": "From Empty Projects",
"items": [{
"text": "With ASP.NET Core Web Application",
"path": "Getting-Started-AspNetCore-Application.md"
},{
"text": "With Console Application",
"path": "Getting-Started-Console-Application.md"
}]
}]
},{
"text": "Tutorials",
"items": [{
"text": "Application Development",
"items": [{
"text": "With ASP.NET Core MVC",
"path": "Tutorials/AspNetCore-Mvc/Part-I.md"
}]
}]
}]
}

4
framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AutoMapperExpressionExtensions.cs

@ -6,9 +6,9 @@ namespace Volo.Abp.AutoMapper
{ {
public static class AutoMapperExpressionExtensions public static class AutoMapperExpressionExtensions
{ {
public static void Ignore<TDestination, TMember, TResult>(this IMappingExpression<TDestination, TMember> mappingExpression, Expression<Func<TMember, TResult>> destinationMember) public static IMappingExpression<TDestination, TMember> Ignore<TDestination, TMember, TResult>(this IMappingExpression<TDestination, TMember> mappingExpression, Expression<Func<TMember, TResult>> destinationMember)
{ {
mappingExpression.ForMember(destinationMember, opts => opts.Ignore()); return mappingExpression.ForMember(destinationMember, opts => opts.Ignore());
} }
} }
} }

25
framework/src/Volo.Abp.Core/Volo/Abp/Modularity/AbpModule.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Reflection; using System.Reflection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace Volo.Abp.Modularity namespace Volo.Abp.Modularity
@ -91,6 +92,30 @@ namespace Volo.Abp.Modularity
ServiceConfigurationContext.Services.Configure(configureOptions); ServiceConfigurationContext.Services.Configure(configureOptions);
} }
protected void Configure<TOptions>(string name, Action<TOptions> configureOptions)
where TOptions : class
{
ServiceConfigurationContext.Services.Configure(name, configureOptions);
}
protected void Configure<TOptions>(IConfiguration configuration)
where TOptions : class
{
ServiceConfigurationContext.Services.Configure<TOptions>(configuration);
}
protected void Configure<TOptions>(IConfiguration configuration, Action<BinderOptions> configureBinder)
where TOptions : class
{
ServiceConfigurationContext.Services.Configure<TOptions>(configuration, configureBinder);
}
protected void Configure<TOptions>(string name, IConfiguration configuration)
where TOptions : class
{
ServiceConfigurationContext.Services.Configure<TOptions>(name, configuration);
}
protected void PreConfigure<TOptions>(Action<TOptions> configureOptions) protected void PreConfigure<TOptions>(Action<TOptions> configureOptions)
where TOptions : class where TOptions : class
{ {

625
modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20180912113852_Added_BlogUsers.Designer.cs

@ -0,0 +1,625 @@
// <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("20180912113852_Added_BlogUsers")]
partial class Added_BlogUsers
{
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.Blogging.Users.BlogUser", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
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<string>("PhoneNumber")
.HasColumnName("PhoneNumber")
.HasMaxLength(16);
b.Property<bool>("PhoneNumberConfirmed")
.ValueGeneratedOnAdd()
.HasColumnName("PhoneNumberConfirmed")
.HasDefaultValue(false);
b.Property<Guid?>("TenantId")
.HasColumnName("TenantId");
b.Property<string>("UserName")
.IsRequired()
.HasColumnName("UserName")
.HasMaxLength(256);
b.HasKey("Id");
b.ToTable("BlgUsers");
});
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
}
}
}

35
modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20180912113852_Added_BlogUsers.cs

@ -0,0 +1,35 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Volo.BloggingTestApp.EntityFrameworkCore.Migrations
{
public partial class Added_BlogUsers : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "BlgUsers",
columns: table => new
{
Id = table.Column<Guid>(nullable: false),
TenantId = table.Column<Guid>(nullable: true),
UserName = table.Column<string>(maxLength: 256, nullable: false),
Email = table.Column<string>(maxLength: 256, nullable: true),
EmailConfirmed = table.Column<bool>(nullable: false, defaultValue: false),
PhoneNumber = table.Column<string>(maxLength: 16, nullable: true),
PhoneNumberConfirmed = table.Column<bool>(nullable: false, defaultValue: false),
ExtraProperties = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_BlgUsers", x => x.Id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "BlgUsers");
}
}
}

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

@ -501,6 +501,45 @@ namespace Volo.BloggingTestApp.EntityFrameworkCore.Migrations
b.ToTable("BlgTags"); b.ToTable("BlgTags");
}); });
modelBuilder.Entity("Volo.Blogging.Users.BlogUser", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
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<string>("PhoneNumber")
.HasColumnName("PhoneNumber")
.HasMaxLength(16);
b.Property<bool>("PhoneNumberConfirmed")
.ValueGeneratedOnAdd()
.HasColumnName("PhoneNumberConfirmed")
.HasDefaultValue(false);
b.Property<Guid?>("TenantId")
.HasColumnName("TenantId");
b.Property<string>("UserName")
.IsRequired()
.HasColumnName("UserName")
.HasMaxLength(256);
b.HasKey("Id");
b.ToTable("BlgUsers");
});
modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b =>
{ {
b.HasOne("Volo.Abp.Identity.IdentityRole") b.HasOne("Volo.Abp.Identity.IdentityRole")

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

File diff suppressed because it is too large

16
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/BloggingApplicationContractsModule.cs

@ -1,8 +1,8 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Authorization.Permissions; using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Localization; using Volo.Abp.Localization;
using Volo.Abp.Localization.Resources.AbpValidation;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem;
using Volo.Blogging.Localization; using Volo.Blogging.Localization;
namespace Volo.Blogging namespace Volo.Blogging
@ -12,17 +12,21 @@ namespace Volo.Blogging
{ {
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
context.Services.Configure<PermissionOptions>(options =>
{
options.DefinitionProviders.Add<BloggingPermissionDefinitionProvider>();
});
context.Services.Configure<VirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<BloggingApplicationContractsModule>();
});
Configure<AbpLocalizationOptions>(options => Configure<AbpLocalizationOptions>(options =>
{ {
options.Resources options.Resources
.Get<BloggingResource>() .Get<BloggingResource>()
.AddVirtualJson("/Localization/Resources/Blogging/ApplicationContracts"); .AddVirtualJson("/Localization/Resources/Blogging/ApplicationContracts");
}); });
context.Services.Configure<PermissionOptions>(options =>
{
options.DefinitionProviders.Add<BloggingPermissionDefinitionProvider>();
});
} }
} }
} }

1
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/BloggingPermissions.cs

@ -10,6 +10,7 @@
public const string Delete = Default + ".Delete"; public const string Delete = Default + ".Delete";
public const string Update = Default + ".Update"; public const string Update = Default + ".Update";
public const string Create = Default + ".Create"; public const string Create = Default + ".Create";
} }
public static class Posts public static class Posts

4
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Blogs/BlogDto.cs → modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Blogs/Dtos/BlogDto.cs

@ -1,9 +1,9 @@
using System; using System;
using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Dtos;
namespace Volo.Blogging.Blogs namespace Volo.Blogging.Blogs.Dtos
{ {
public class BlogDto : EntityDto<Guid> public class BlogDto : FullAuditedEntityDto<Guid>
{ {
public string Name { get; set; } public string Name { get; set; }

5
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Blogs/CreateBlogDto.cs → modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Blogs/Dtos/CreateBlogDto.cs

@ -1,7 +1,4 @@
using System; namespace Volo.Blogging.Blogs.Dtos
using Volo.Abp.Application.Dtos;
namespace Volo.Blogging.Blogs
{ {
public class CreateBlogDto public class CreateBlogDto
{ {

15
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Blogs/Dtos/UpdateBlogDto.cs

@ -0,0 +1,15 @@
using System;
namespace Volo.Blogging.Blogs.Dtos
{
public class UpdateBlogDto
{
public Guid Id { get; set; }
public string Name { get; set; }
public string ShortName { get; set; }
public string Description { get; set; }
}
}

9
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Blogs/IBlogAppService.cs

@ -2,17 +2,24 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services; using Volo.Abp.Application.Services;
using Volo.Blogging.Blogs.Dtos;
namespace Volo.Blogging.Blogs namespace Volo.Blogging.Blogs
{ {
public interface IBlogAppService : IApplicationService public interface IBlogAppService : IApplicationService
{ {
Task<PagedResultDto<BlogDto>> GetListPagedAsync(PagedAndSortedResultRequestDto input);
Task<ListResultDto<BlogDto>> GetListAsync(); Task<ListResultDto<BlogDto>> GetListAsync();
Task<BlogDto> GetByShortNameAsync(string shortName); Task<BlogDto> GetByShortNameAsync(string shortName);
Task<BlogDto> GetAsync(Guid id); Task<BlogDto> GetAsync(Guid id);
Task<BlogDto> Create(CreateBlogDto input);
Task<BlogDto> Update(Guid id, UpdateBlogDto input);
Task Create(CreateBlogDto blog); Task Delete(Guid id);
} }
} }

5
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Comments/Dtos/CommentDto.cs → modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Comments/Dtos/CommentWithDetailsDto.cs

@ -1,12 +1,15 @@
using System; using System;
using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Dtos;
using Volo.Blogging.Posts;
namespace Volo.Blogging.Comments.Dtos namespace Volo.Blogging.Comments.Dtos
{ {
public class CommentDto : FullAuditedEntityDto<Guid> public class CommentWithDetailsDto : FullAuditedEntityDto<Guid>
{ {
public Guid? RepliedCommentId { get; set; } public Guid? RepliedCommentId { get; set; }
public string Text { get; set; } public string Text { get; set; }
public BlogUserDto Writer { get; set; }
} }
} }

4
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Comments/Dtos/CommentWithRepliesDto.cs

@ -4,8 +4,8 @@ namespace Volo.Blogging.Comments.Dtos
{ {
public class CommentWithRepliesDto public class CommentWithRepliesDto
{ {
public CommentDto Comment { get; set; } public CommentWithDetailsDto Comment { get; set; }
public List<CommentDto> Replies { get; set; } = new List<CommentDto>(); public List<CommentWithDetailsDto> Replies { get; set; } = new List<CommentWithDetailsDto>();
} }
} }

4
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Comments/ICommentAppService.cs

@ -10,9 +10,9 @@ namespace Volo.Blogging.Comments
{ {
Task<List<CommentWithRepliesDto>> GetHierarchicalListOfPostAsync(GetCommentListOfPostAsync input); Task<List<CommentWithRepliesDto>> GetHierarchicalListOfPostAsync(GetCommentListOfPostAsync input);
Task<CommentDto> CreateAsync(CreateCommentDto input); Task<CommentWithDetailsDto> CreateAsync(CreateCommentDto input);
Task<CommentDto> UpdateAsync(Guid id, UpdateCommentDto input); Task<CommentWithDetailsDto> UpdateAsync(Guid id, UpdateCommentDto input);
Task DeleteAsync(Guid id); Task DeleteAsync(Guid id);
} }

24
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/BlogUserDto.cs

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Text;
using Volo.Abp.Application.Dtos;
namespace Volo.Blogging.Posts
{
public class BlogUserDto : EntityDto<Guid>
{
public Guid? TenantId { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public bool EmailConfirmed { get; set; }
public string PhoneNumber { get; set; }
public bool PhoneNumberConfirmed { get; set; }
public Dictionary<string, object> ExtraProperties { get; set; }
}
}

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

@ -1,4 +1,4 @@
using System;  using System;
namespace Volo.Blogging.Posts namespace Volo.Blogging.Posts
{ {

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

@ -13,6 +13,8 @@ namespace Volo.Blogging.Posts
Task<PostWithDetailsDto> GetAsync(Guid id); Task<PostWithDetailsDto> GetAsync(Guid id);
Task DeleteAsync(Guid id);
Task<PostWithDetailsDto> CreateAsync(CreatePostDto input); Task<PostWithDetailsDto> CreateAsync(CreatePostDto input);
Task<PostWithDetailsDto> UpdateAsync(Guid id, UpdatePostDto input); Task<PostWithDetailsDto> UpdateAsync(Guid id, UpdatePostDto input);

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

@ -21,6 +21,8 @@ namespace Volo.Blogging.Posts
public int CommentCount { get; set; } public int CommentCount { get; set; }
public BlogUserDto Writer { get; set; }
public List<TagDto> Tags { get; set; } public List<TagDto> Tags { get; set; }
} }
} }

5
modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/BloggingApplicationAutoMapperProfile.cs

@ -1,6 +1,7 @@
using AutoMapper; using AutoMapper;
using Volo.Abp.AutoMapper; using Volo.Abp.AutoMapper;
using Volo.Blogging.Blogs; using Volo.Blogging.Blogs;
using Volo.Blogging.Blogs.Dtos;
using Volo.Blogging.Comments; using Volo.Blogging.Comments;
using Volo.Blogging.Comments.Dtos; using Volo.Blogging.Comments.Dtos;
using Volo.Blogging.Posts; using Volo.Blogging.Posts;
@ -15,8 +16,8 @@ namespace Volo.Blogging
{ {
CreateMap<Blog, BlogDto>(); CreateMap<Blog, BlogDto>();
CreateMap<Post, PostDto>(); CreateMap<Post, PostDto>();
CreateMap<Post, PostWithDetailsDto>().Ignore(x=>x.CommentCount); CreateMap<Post, PostWithDetailsDto>().Ignore(x=>x.Writer).Ignore(x=>x.CommentCount);
CreateMap<Comment, CommentDto>(); CreateMap<Comment, CommentWithDetailsDto>().Ignore(x => x.Writer);
CreateMap<Tag, TagDto>(); CreateMap<Tag, TagDto>();
} }
} }

18
modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/BloggingApplicationModule.cs

@ -1,7 +1,10 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AutoMapper; using Volo.Abp.AutoMapper;
using Volo.Abp.Caching; using Volo.Abp.Caching;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Blogging.Comments;
using Volo.Blogging.Posts;
namespace Volo.Blogging namespace Volo.Blogging
{ {
@ -18,6 +21,19 @@ namespace Volo.Blogging
{ {
options.AddProfile<BloggingApplicationAutoMapperProfile>(validate: true); options.AddProfile<BloggingApplicationAutoMapperProfile>(validate: true);
}); });
context.Services.Configure<AuthorizationOptions>(options =>
{
//TODO: Rename UpdatePolicy/DeletePolicy since it's candidate to conflicts with other modules!
options.AddPolicy("BloggingUpdatePolicy", policy => policy.Requirements.Add(CommonOperations.Update));
options.AddPolicy("BloggingDeletePolicy", policy => policy.Requirements.Add(CommonOperations.Delete));
});
context.Services.AddSingleton<IAuthorizationHandler, CommentAuthorizationHandler>();
context.Services.AddSingleton<IAuthorizationHandler, PostAuthorizationHandler>();
} }
} }
} }

33
modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Blogs/BlogAppService.cs

@ -1,9 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services; using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Entities;
using Volo.Blogging.Blogs.Dtos;
namespace Volo.Blogging.Blogs namespace Volo.Blogging.Blogs
{ {
@ -15,6 +17,18 @@ namespace Volo.Blogging.Blogs
{ {
_blogRepository = blogRepository; _blogRepository = blogRepository;
} }
public async Task<PagedResultDto<BlogDto>> GetListPagedAsync(PagedAndSortedResultRequestDto input)
{
var blogs = await _blogRepository.GetListAsync(input.Sorting, input.MaxResultCount, input.SkipCount );
var totalCount = await _blogRepository.GetTotalBlogCount();
var dtos = ObjectMapper.Map<List<Blog>, List<BlogDto>>(blogs);
return new PagedResultDto<BlogDto>(totalCount, dtos);
}
public async Task<ListResultDto<BlogDto>> GetListAsync() public async Task<ListResultDto<BlogDto>> GetListAsync()
{ {
var blogs = await _blogRepository.GetListAsync(); var blogs = await _blogRepository.GetListAsync();
@ -43,11 +57,30 @@ namespace Volo.Blogging.Blogs
return ObjectMapper.Map<Blog, BlogDto>(blog); return ObjectMapper.Map<Blog, BlogDto>(blog);
} }
[Authorize(BloggingPermissions.Blogs.Create)]
public async Task<BlogDto> Create(CreateBlogDto input) public async Task<BlogDto> Create(CreateBlogDto input)
{ {
var newBlog = await _blogRepository.InsertAsync(new Blog(GuidGenerator.Create(), input.Name, input.ShortName){Description = input.Description}); var newBlog = await _blogRepository.InsertAsync(new Blog(GuidGenerator.Create(), input.Name, input.ShortName){Description = input.Description});
return ObjectMapper.Map<Blog, BlogDto>(newBlog); return ObjectMapper.Map<Blog, BlogDto>(newBlog);
} }
[Authorize(BloggingPermissions.Blogs.Update)]
public async Task<BlogDto> Update(Guid id, UpdateBlogDto input)
{
var blog = await _blogRepository.GetAsync(id);
blog.SetName(input.Name);
blog.SetShortName(input.ShortName);
blog.Description = input.Description;
return ObjectMapper.Map<Blog, BlogDto>(blog);
}
[Authorize(BloggingPermissions.Blogs.Delete)]
public async Task Delete(Guid id)
{
await _blogRepository.DeleteAsync(id);
}
} }
} }

79
modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Comments/CommentAppService.cs

@ -5,25 +5,53 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Services; using Volo.Abp.Application.Services;
using Volo.Abp.Guids; using Volo.Abp.Guids;
using Volo.Abp.Users;
using Volo.Blogging.Comments.Dtos; using Volo.Blogging.Comments.Dtos;
using Volo.Blogging.Posts;
using Volo.Blogging.Users;
namespace Volo.Blogging.Comments namespace Volo.Blogging.Comments
{ {
// [Authorize(BloggingPermissions.Comments.Default)]
public class CommentAppService : ApplicationService, ICommentAppService public class CommentAppService : ApplicationService, ICommentAppService
{ {
protected IBlogUserLookupService UserLookupService;
private readonly ICommentRepository _commentRepository; private readonly ICommentRepository _commentRepository;
private readonly IGuidGenerator _guidGenerator; private readonly IGuidGenerator _guidGenerator;
public CommentAppService(ICommentRepository commentRepository, IGuidGenerator guidGenerator) public CommentAppService(ICommentRepository commentRepository, IGuidGenerator guidGenerator, IBlogUserLookupService userLookupService)
{ {
_commentRepository = commentRepository; _commentRepository = commentRepository;
_guidGenerator = guidGenerator; _guidGenerator = guidGenerator;
UserLookupService = userLookupService;
} }
public async Task<List<CommentWithRepliesDto>> GetHierarchicalListOfPostAsync(GetCommentListOfPostAsync input) public async Task<List<CommentWithRepliesDto>> GetHierarchicalListOfPostAsync(GetCommentListOfPostAsync input)
{ {
var comments = await GetListOfPostAsync(input); var comments = await GetListOfPostAsync(input);
var userDictionary = new Dictionary<Guid, BlogUserDto>();
foreach (var commentDto in comments)
{
if (commentDto.CreatorId.HasValue)
{
var creatorUser = await UserLookupService.FindByIdAsync(commentDto.CreatorId.Value);
if (creatorUser != null && !userDictionary.ContainsKey(creatorUser.Id))
{
userDictionary.Add(creatorUser.Id, ObjectMapper.Map<BlogUser, BlogUserDto>(creatorUser));
}
}
}
foreach (var commentDto in comments)
{
if (commentDto.CreatorId.HasValue && userDictionary.ContainsKey((Guid)commentDto.CreatorId))
{
commentDto.Writer = userDictionary[(Guid)commentDto.CreatorId];
}
}
var hierarchicalComments = new List<CommentWithRepliesDto>(); var hierarchicalComments = new List<CommentWithRepliesDto>();
foreach (var commentDto in comments) foreach (var commentDto in comments)
@ -45,72 +73,43 @@ namespace Volo.Blogging.Comments
return hierarchicalComments; return hierarchicalComments;
} }
private async Task<List<CommentDto>> GetListOfPostAsync(GetCommentListOfPostAsync input) private async Task<List<CommentWithDetailsDto>> GetListOfPostAsync(GetCommentListOfPostAsync input)
{ {
var comments = await _commentRepository.GetListOfPostAsync(input.PostId); var comments = await _commentRepository.GetListOfPostAsync(input.PostId);
return new List<CommentDto>( return new List<CommentWithDetailsDto>(
ObjectMapper.Map<List<Comment>, List<CommentDto>>(comments)); ObjectMapper.Map<List<Comment>, List<CommentWithDetailsDto>>(comments));
} }
[Authorize(BloggingPermissions.Comments.Create)] [Authorize(BloggingPermissions.Comments.Create)]
public async Task<CommentDto> CreateAsync(CreateCommentDto input) public async Task<CommentWithDetailsDto> CreateAsync(CreateCommentDto input)
{ {
var comment = new Comment(_guidGenerator.Create(), input.PostId, input.RepliedCommentId, input.Text); var comment = new Comment(_guidGenerator.Create(), input.PostId, input.RepliedCommentId, input.Text);
comment = await _commentRepository.InsertAsync(comment); comment = await _commentRepository.InsertAsync(comment);
return ObjectMapper.Map<Comment, CommentDto>(comment); return ObjectMapper.Map<Comment, CommentWithDetailsDto>(comment);
} }
public async Task<CommentDto> UpdateAsync(Guid id, UpdateCommentDto input) public async Task<CommentWithDetailsDto> UpdateAsync(Guid id, UpdateCommentDto input)
{ {
var comment = await _commentRepository.GetAsync(id); var comment = await _commentRepository.GetAsync(id);
if (CurrentUser.Id != comment.CreatorId) await AuthorizationService.CheckAsync(comment, CommonOperations.Update);
{
return await UpdateAsAdminAsync(id, comment, input);
}
return await UpdateCommentAsync(id, comment, input);
}
[Authorize(BloggingPermissions.Comments.Update)]
private async Task<CommentDto> UpdateAsAdminAsync(Guid id, Comment comment, UpdateCommentDto input)
{
return await UpdateCommentAsync(id, comment, input);
}
private async Task<CommentDto> UpdateCommentAsync(Guid id, Comment comment, UpdateCommentDto input)
{
comment.SetText(input.Text); comment.SetText(input.Text);
comment = await _commentRepository.UpdateAsync(comment); comment = await _commentRepository.UpdateAsync(comment);
return ObjectMapper.Map<Comment, CommentDto>(comment); return ObjectMapper.Map<Comment, CommentWithDetailsDto>(comment);
} }
public async Task DeleteAsync(Guid id) public async Task DeleteAsync(Guid id)
{ {
var comment = await _commentRepository.GetAsync(id); var comment = await _commentRepository.GetAsync(id);
if (CurrentUser.Id != comment.CreatorId) await AuthorizationService.CheckAsync(comment, CommonOperations.Delete);
{
await DeleteAsAdminAsync(id);
return;
}
await DeleteCommentAsync(id);
}
[Authorize(BloggingPermissions.Comments.Delete)]
private async Task DeleteAsAdminAsync(Guid id)
{
await DeleteCommentAsync(id);
}
private async Task DeleteCommentAsync(Guid id)
{
await _commentRepository.DeleteAsync(id); await _commentRepository.DeleteAsync(id);
var replies = await _commentRepository.GetRepliesOfComment(id); var replies = await _commentRepository.GetRepliesOfComment(id);

61
modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Comments/CommentAuthorizationHandler.cs

@ -0,0 +1,61 @@
using System.Security.Principal;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Infrastructure;
using Volo.Abp.Authorization.Permissions;
namespace Volo.Blogging.Comments
{
public class CommentAuthorizationHandler : AuthorizationHandler<OperationAuthorizationRequirement, Comment>
{
private readonly IPermissionChecker _permissionChecker;
public CommentAuthorizationHandler(IPermissionChecker permissionChecker)
{
_permissionChecker = permissionChecker;
}
protected override async Task HandleRequirementAsync(
AuthorizationHandlerContext context,
OperationAuthorizationRequirement requirement,
Comment resource)
{
if (requirement.Name == CommonOperations.Delete.Name && await HasDeletePermission(context, resource))
{
context.Succeed(requirement);
return;
}
if (requirement.Name == CommonOperations.Update.Name && await HasUpdatePermission(context, resource))
{
context.Succeed(requirement);
return;
}
}
private async Task<bool> HasDeletePermission(AuthorizationHandlerContext context, Comment resource)
{
if (await _permissionChecker.IsGrantedAsync(context.User, BloggingPermissions.Comments.Delete))
{
return true;
}
return false;
}
private async Task<bool> HasUpdatePermission(AuthorizationHandlerContext context, Comment resource)
{
if (resource.CreatorId != null && resource.CreatorId == context.User.FindUserId())
{
return true;
}
if (await _permissionChecker.IsGrantedAsync(context.User, BloggingPermissions.Comments.Update))
{
return true;
}
return false;
}
}
}

13
modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/CommonOperations.cs

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.AspNetCore.Authorization.Infrastructure;
namespace Volo.Blogging
{
public static class CommonOperations
{
public static OperationAuthorizationRequirement Update = new OperationAuthorizationRequirement { Name = nameof(Update) };
public static OperationAuthorizationRequirement Delete = new OperationAuthorizationRequirement { Name = nameof(Delete) };
}
}

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

@ -9,6 +9,7 @@ using Volo.Abp.Users;
using Volo.Blogging.Comments; using Volo.Blogging.Comments;
using Volo.Blogging.Tagging; using Volo.Blogging.Tagging;
using Volo.Blogging.Tagging.Dtos; using Volo.Blogging.Tagging.Dtos;
using Volo.Blogging.Users;
namespace Volo.Blogging.Posts namespace Volo.Blogging.Posts
{ {
@ -19,13 +20,16 @@ namespace Volo.Blogging.Posts
//[Authorize(BloggingPermissions.Posts.Default)] //[Authorize(BloggingPermissions.Posts.Default)]
public class PostAppService : ApplicationService, IPostAppService public class PostAppService : ApplicationService, IPostAppService
{ {
protected IBlogUserLookupService UserLookupService { get; }
private readonly IPostRepository _postRepository; private readonly IPostRepository _postRepository;
private readonly ITagRepository _tagRepository; private readonly ITagRepository _tagRepository;
private readonly IPostTagRepository _postTagRepository; private readonly IPostTagRepository _postTagRepository;
private readonly ICommentRepository _commentRepository; private readonly ICommentRepository _commentRepository;
public PostAppService(IPostRepository postRepository, ITagRepository tagRepository, IPostTagRepository postTagRepository, ICommentRepository commentRepository) public PostAppService(IPostRepository postRepository, ITagRepository tagRepository, IPostTagRepository postTagRepository, ICommentRepository commentRepository, IBlogUserLookupService userLookupService)
{ {
UserLookupService = userLookupService;
_postRepository = postRepository; _postRepository = postRepository;
_tagRepository = tagRepository; _tagRepository = tagRepository;
_postTagRepository = postTagRepository; _postTagRepository = postTagRepository;
@ -35,26 +39,61 @@ namespace Volo.Blogging.Posts
public async Task<ListResultDto<PostWithDetailsDto>> GetListByBlogIdAndTagName(Guid id, string tagName) public async Task<ListResultDto<PostWithDetailsDto>> GetListByBlogIdAndTagName(Guid id, string tagName)
{ {
var posts = _postRepository.GetPostsByBlogId(id); var posts = _postRepository.GetPostsByBlogId(id);
var tag = tagName.IsNullOrWhiteSpace()? null: await _tagRepository.GetByNameAsync(tagName);
var userDictionary = new Dictionary<Guid, BlogUserDto>();
var postDtos = new List<PostWithDetailsDto>(ObjectMapper.Map<List<Post>, List<PostWithDetailsDto>>(posts));
var postDtos = new List<PostWithDetailsDto>( if (tag != null)
ObjectMapper.Map<List<Post>, List<PostWithDetailsDto>>(posts)); {
postDtos = await FilterPostsByTag(postDtos, tag);
}
foreach (var postDto in postDtos) foreach (var postDto in postDtos)
{ {
postDto.Tags = await GetTagsOfPost(postDto);
postDto.CommentCount = await _commentRepository.GetCommentCountOfPostAsync(postDto.Id); postDto.CommentCount = await _commentRepository.GetCommentCountOfPostAsync(postDto.Id);
} }
if (!tagName.IsNullOrWhiteSpace()) foreach (var postDto in postDtos)
{
if (postDto.CreatorId.HasValue)
{
var creatorUser = await UserLookupService.FindByIdAsync(postDto.CreatorId.Value);
if (creatorUser != null && !userDictionary.ContainsKey(creatorUser.Id))
{
userDictionary.Add(creatorUser.Id, ObjectMapper.Map<BlogUser, BlogUserDto>(creatorUser));
}
}
}
foreach (var postDto in postDtos)
{ {
var tag = await _tagRepository.GetByNameAsync(tagName); if (postDto.CreatorId.HasValue && userDictionary.ContainsKey((Guid) postDto.CreatorId))
postDtos = postDtos.Where(p => p.Tags.Any(t => t.Id == tag.Id)).ToList(); {
postDto.Writer = userDictionary[(Guid)postDto.CreatorId];
}
} }
return new ListResultDto<PostWithDetailsDto>(postDtos); return new ListResultDto<PostWithDetailsDto>(postDtos);
} }
private async Task<List<PostWithDetailsDto>> FilterPostsByTag(List<PostWithDetailsDto> allPostDtos, Tag tag)
{
var filteredPostDtos = new List<PostWithDetailsDto>();
foreach (var postDto in allPostDtos)
{
if (await _postTagRepository.FindByTagIdAndPostIdAsync(postDto.Id, tag.Id) == null)
{
continue;
}
filteredPostDtos.Add(postDto);
}
return filteredPostDtos;
}
public async Task<PostWithDetailsDto> GetForReadingAsync(GetPostInput input) public async Task<PostWithDetailsDto> GetForReadingAsync(GetPostInput input)
{ {
var post = await _postRepository.GetPostByUrl(input.BlogId, input.Url); var post = await _postRepository.GetPostByUrl(input.BlogId, input.Url);
@ -63,7 +102,14 @@ namespace Volo.Blogging.Posts
var postDto = ObjectMapper.Map<Post, PostWithDetailsDto>(post); var postDto = ObjectMapper.Map<Post, PostWithDetailsDto>(post);
postDto.Tags = await GetTagsOfPost(postDto); postDto.Tags = await GetTagsOfPost(postDto.Id);
if (postDto.CreatorId.HasValue)
{
var creatorUser = await UserLookupService.FindByIdAsync(postDto.CreatorId.Value);
postDto.Writer = ObjectMapper.Map<BlogUser, BlogUserDto>(creatorUser);
}
return postDto; return postDto;
} }
@ -74,16 +120,39 @@ namespace Volo.Blogging.Posts
var postDto = ObjectMapper.Map<Post, PostWithDetailsDto>(post); var postDto = ObjectMapper.Map<Post, PostWithDetailsDto>(post);
postDto.Tags = await GetTagsOfPost(postDto); postDto.Tags = await GetTagsOfPost(postDto.Id);
if (postDto.CreatorId.HasValue)
{
var creatorUser = await UserLookupService.FindByIdAsync(postDto.CreatorId.Value);
postDto.Writer = ObjectMapper.Map<BlogUser, BlogUserDto>(creatorUser);
}
return postDto; return postDto;
} }
public async Task DeleteAsync(Guid id)
{
var post = await _postRepository.GetAsync(id);
await AuthorizationService.CheckAsync(post, CommonOperations.Delete);
var tags = await GetTagsOfPost(id);
_tagRepository.DecreaseUsageCountOfTags(tags.Select(t=>t.Id).ToList());
_postTagRepository.DeleteOfPost(id);
_commentRepository.DeleteOfPost(id);
await _postRepository.DeleteAsync(id);
}
[Authorize(BloggingPermissions.Posts.Update)] [Authorize(BloggingPermissions.Posts.Update)]
public async Task<PostWithDetailsDto> UpdateAsync(Guid id, UpdatePostDto input) public async Task<PostWithDetailsDto> UpdateAsync(Guid id, UpdatePostDto input)
{ {
var post = await _postRepository.GetAsync(id); var post = await _postRepository.GetAsync(id);
await AuthorizationService.CheckAsync(post, CommonOperations.Update);
post.SetTitle(input.Title); post.SetTitle(input.Title);
post.SetUrl(input.Url); post.SetUrl(input.Url);
post.Content = input.Content; post.Content = input.Content;
@ -171,9 +240,9 @@ namespace Volo.Blogging.Posts
} }
} }
private async Task<List<TagDto>> GetTagsOfPost(PostWithDetailsDto postDto) private async Task<List<TagDto>> GetTagsOfPost(Guid id)
{ {
var tagIds = (await _postTagRepository.GetListAsync()).Where(pt => pt.PostId == postDto.Id); var tagIds = (await _postTagRepository.GetListAsync()).Where(pt => pt.PostId == id);
var tags = await _tagRepository.GetListAsync(tagIds.Select(t => t.TagId)); var tags = await _tagRepository.GetListAsync(tagIds.Select(t => t.TagId));

61
modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Posts/PostAuthorizationHandler.cs

@ -0,0 +1,61 @@
using System.Security.Principal;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Infrastructure;
using Volo.Abp.Authorization.Permissions;
namespace Volo.Blogging.Posts
{
public class PostAuthorizationHandler : AuthorizationHandler<OperationAuthorizationRequirement, Post>
{
private readonly IPermissionChecker _permissionChecker;
public PostAuthorizationHandler(IPermissionChecker permissionChecker)
{
_permissionChecker = permissionChecker;
}
protected override async Task HandleRequirementAsync(
AuthorizationHandlerContext context,
OperationAuthorizationRequirement requirement,
Post resource)
{
if (requirement.Name == CommonOperations.Delete.Name && await HasDeletePermission(context, resource))
{
context.Succeed(requirement);
return;
}
if (requirement.Name == CommonOperations.Update.Name && await HasUpdatePermission(context, resource))
{
context.Succeed(requirement);
return;
}
}
private async Task<bool> HasDeletePermission(AuthorizationHandlerContext context, Post resource)
{
if (await _permissionChecker.IsGrantedAsync(context.User, BloggingPermissions.Comments.Delete))
{
return true;
}
return false;
}
private async Task<bool> HasUpdatePermission(AuthorizationHandlerContext context, Post resource)
{
if (resource.CreatorId != null && resource.CreatorId == context.User.FindUserId())
{
return true;
}
if (await _permissionChecker.IsGrantedAsync(context.User, BloggingPermissions.Comments.Update))
{
return true;
}
return false;
}
}
}

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

@ -11,6 +11,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Volo.Blogging.Domain.Shared\Volo.Blogging.Domain.Shared.csproj" /> <ProjectReference Include="..\Volo.Blogging.Domain.Shared\Volo.Blogging.Domain.Shared.csproj" />
<ProjectReference Include="..\..\..\users\src\Volo.Abp.Users.Domain\Volo.Abp.Users.Domain.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Ddd.Domain\Volo.Abp.Ddd.Domain.csproj" /> <ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Ddd.Domain\Volo.Abp.Ddd.Domain.csproj" />
</ItemGroup> </ItemGroup>

6
modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Blogs/Blog.cs

@ -33,5 +33,11 @@ namespace Volo.Blogging.Blogs
Name = Check.NotNullOrWhiteSpace(name, nameof(name)); Name = Check.NotNullOrWhiteSpace(name, nameof(name));
return this; return this;
} }
public virtual Blog SetShortName(string shortName)
{
ShortName = Check.NotNullOrWhiteSpace(shortName, nameof(shortName));
return this;
}
} }
} }

5
modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Blogs/IBlogRepository.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Repositories;
@ -7,5 +8,9 @@ namespace Volo.Blogging.Blogs
public interface IBlogRepository : IBasicRepository<Blog, Guid> public interface IBlogRepository : IBasicRepository<Blog, Guid>
{ {
Task<Blog> FindByShortNameAsync(string shortName); Task<Blog> FindByShortNameAsync(string shortName);
Task<List<Blog>> GetListAsync(string sorting, int maxResultCount, int skipCount);
Task<int> GetTotalBlogCount();
} }
} }

2
modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Comments/ICommentRepository.cs

@ -14,5 +14,7 @@ namespace Volo.Blogging.Comments
Task<int> GetCommentCountOfPostAsync(Guid postId); Task<int> GetCommentCountOfPostAsync(Guid postId);
Task<List<Comment>> GetRepliesOfComment(Guid id); Task<List<Comment>> GetRepliesOfComment(Guid id);
void DeleteOfPost(Guid id);
} }
} }

7
modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Posts/IPostTagRepository.cs

@ -1,8 +1,13 @@
using Volo.Abp.Domain.Repositories; using System;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
namespace Volo.Blogging.Posts namespace Volo.Blogging.Posts
{ {
public interface IPostTagRepository : IBasicRepository<PostTag> public interface IPostTagRepository : IBasicRepository<PostTag>
{ {
void DeleteOfPost(Guid id);
Task<PostTag> FindByTagIdAndPostIdAsync(Guid postId, Guid tagId);
} }
} }

4
modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Tagging/ITagRepository.cs

@ -11,6 +11,10 @@ namespace Volo.Blogging.Tagging
Task<Tag> GetByNameAsync(string name); Task<Tag> GetByNameAsync(string name);
Task<Tag> FindByNameAsync(string name);
Task<List<Tag>> GetListAsync(IEnumerable<Guid> ids); Task<List<Tag>> GetListAsync(IEnumerable<Guid> ids);
void DecreaseUsageCountOfTags(List<Guid> id);
} }
} }

43
modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Users/BlogUser.cs

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Users;
namespace Volo.Blogging.Users
{
public class BlogUser : AggregateRoot<Guid>, IUser, IHasExtraProperties
{
public virtual Guid? TenantId { get; protected set; }
public virtual string UserName { get; protected set; }
public virtual string Email { get; protected set; }
public virtual bool EmailConfirmed { get; protected set; }
public virtual string PhoneNumber { get; protected set; }
public virtual bool PhoneNumberConfirmed { get; protected set; }
public virtual Dictionary<string, object> ExtraProperties { get; protected set; }
protected BlogUser()
{
ExtraProperties = new Dictionary<string, object>();
}
public BlogUser(IUserData user)
{
Id = user.Id;
Email = user.Email;
EmailConfirmed = user.EmailConfirmed;
PhoneNumber = user.PhoneNumber;
PhoneNumberConfirmed = user.PhoneNumberConfirmed;
UserName = user.UserName;
TenantId = user.TenantId;
ExtraProperties = new Dictionary<string, object>();
}
}
}

22
modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Users/BlogUserLookupService.cs

@ -0,0 +1,22 @@
using Volo.Abp.Uow;
using Volo.Abp.Users;
namespace Volo.Blogging.Users
{
public class BlogUserLookupService : UserLookupService<BlogUser, IBlogUserRepository>, IBlogUserLookupService
{
public BlogUserLookupService(
IBlogUserRepository userRepository,
IUnitOfWorkManager unitOfWorkManager)
: base(
userRepository,
unitOfWorkManager)
{
}
protected override BlogUser CreateUser(IUserData externalUser)
{
return new BlogUser(externalUser);
}
}
}

9
modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Users/IBlogUserLookupService.cs

@ -0,0 +1,9 @@
using Volo.Abp.Users;
namespace Volo.Blogging.Users
{
public interface IBlogUserLookupService : IUserLookupService<BlogUser>
{
}
}

14
modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Users/IBlogUserRepository.cs

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Users;
namespace Volo.Blogging.Users
{
public interface IBlogUserRepository : IBasicRepository<BlogUser, Guid>, IUserRepository<BlogUser>
{
Task<List<BlogUser>> GetUsersAsync(int maxCount, string filter, CancellationToken cancellationToken = default);
}
}

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

@ -12,6 +12,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Volo.Blogging.Domain\Volo.Blogging.Domain.csproj" /> <ProjectReference Include="..\Volo.Blogging.Domain\Volo.Blogging.Domain.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.EntityFrameworkCore\Volo.Abp.EntityFrameworkCore.csproj" /> <ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.EntityFrameworkCore\Volo.Abp.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\..\users\src\Volo.Abp.Users.EntityFrameworkCore\Volo.Abp.Users.EntityFrameworkCore.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

17
modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Blogs/EfCoreBlogRepository.cs

@ -1,4 +1,7 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
@ -19,5 +22,19 @@ namespace Volo.Blogging.Blogs
{ {
return await DbSet.FirstOrDefaultAsync(p => p.ShortName == shortName); return await DbSet.FirstOrDefaultAsync(p => p.ShortName == shortName);
} }
public async Task<List<Blog>> GetListAsync(string sorting, int maxResultCount, int skipCount)
{
var auditLogs = await DbSet.OrderBy(sorting ?? "creationTime desc")
.PageBy(skipCount, maxResultCount)
.ToListAsync();
return auditLogs;
}
public async Task<int> GetTotalBlogCount()
{
return await DbSet.CountAsync();
}
} }
} }

6
modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Comments/EfCoreCommentRepository.cs

@ -35,5 +35,11 @@ namespace Volo.Blogging.Comments
return await DbSet return await DbSet
.Where(a => a.RepliedCommentId == id).ToListAsync(); .Where(a => a.RepliedCommentId == id).ToListAsync();
} }
public void DeleteOfPost(Guid id)
{
var recordsToDelete = DbSet.Where(pt => pt.PostId == id);
DbSet.RemoveRange(recordsToDelete);
}
} }
} }

3
modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/EntityFrameworkCore/BloggingDbContext.cs

@ -5,6 +5,7 @@ using Volo.Blogging.Blogs;
using Volo.Blogging.Comments; using Volo.Blogging.Comments;
using Volo.Blogging.Posts; using Volo.Blogging.Posts;
using Volo.Blogging.Tagging; using Volo.Blogging.Tagging;
using Volo.Blogging.Users;
namespace Volo.Blogging.EntityFrameworkCore namespace Volo.Blogging.EntityFrameworkCore
{ {
@ -14,6 +15,8 @@ namespace Volo.Blogging.EntityFrameworkCore
public static string TablePrefix { get; set; } = BloggingConsts.DefaultDbTablePrefix; public static string TablePrefix { get; set; } = BloggingConsts.DefaultDbTablePrefix;
public static string Schema { get; set; } = BloggingConsts.DefaultDbSchema; public static string Schema { get; set; } = BloggingConsts.DefaultDbSchema;
public DbSet<BlogUser> Users { get; set; }
public DbSet<Blog> Blogs { get; set; } public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; } public DbSet<Post> Posts { get; set; }

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

@ -3,10 +3,12 @@ using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Volo.Abp; using Volo.Abp;
using Volo.Abp.EntityFrameworkCore.Modeling; using Volo.Abp.EntityFrameworkCore.Modeling;
using Volo.Abp.Users.EntityFrameworkCore;
using Volo.Blogging.Blogs; using Volo.Blogging.Blogs;
using Volo.Blogging.Comments; using Volo.Blogging.Comments;
using Volo.Blogging.Posts; using Volo.Blogging.Posts;
using Volo.Blogging.Tagging; using Volo.Blogging.Tagging;
using Volo.Blogging.Users;
namespace Volo.Blogging.EntityFrameworkCore namespace Volo.Blogging.EntityFrameworkCore
{ {
@ -21,6 +23,14 @@ namespace Volo.Blogging.EntityFrameworkCore
var options = new BloggingModelBuilderConfigurationOptions(); var options = new BloggingModelBuilderConfigurationOptions();
optionsAction?.Invoke(options); optionsAction?.Invoke(options);
builder.Entity<BlogUser>(b =>
{
b.ToTable(options.TablePrefix + "Users", options.Schema);
b.ConfigureAbpUser(options);
b.ConfigureExtraProperties();
});
builder.Entity<Blog>(b => builder.Entity<Blog>(b =>
{ {
b.ToTable(options.TablePrefix + "Blogs", options.Schema); b.ToTable(options.TablePrefix + "Blogs", options.Schema);

3
modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/EntityFrameworkCore/IBloggingDbContext.cs

@ -5,12 +5,15 @@ using Volo.Blogging.Blogs;
using Volo.Blogging.Comments; using Volo.Blogging.Comments;
using Volo.Blogging.Posts; using Volo.Blogging.Posts;
using Volo.Blogging.Tagging; using Volo.Blogging.Tagging;
using Volo.Blogging.Users;
namespace Volo.Blogging.EntityFrameworkCore namespace Volo.Blogging.EntityFrameworkCore
{ {
[ConnectionStringName("Blogging")] [ConnectionStringName("Blogging")]
public interface IBloggingDbContext : IEfCoreDbContext public interface IBloggingDbContext : IEfCoreDbContext
{ {
DbSet<BlogUser> Users { get; }
DbSet<Blog> Blogs { get; set; } DbSet<Blog> Blogs { get; set; }
DbSet<Post> Posts { get; set; } DbSet<Post> Posts { get; set; }

17
modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Posts/EfCorePostTagRepository.cs

@ -1,4 +1,8 @@
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore;
using Volo.Blogging.EntityFrameworkCore; using Volo.Blogging.EntityFrameworkCore;
@ -9,5 +13,16 @@ namespace Volo.Blogging.Posts
public EfCorePostTagRepository(IDbContextProvider<IBloggingDbContext> dbContextProvider) : base(dbContextProvider) public EfCorePostTagRepository(IDbContextProvider<IBloggingDbContext> dbContextProvider) : base(dbContextProvider)
{ {
} }
public void DeleteOfPost(Guid id)
{
var recordsToDelete = DbSet.Where(pt=>pt.PostId == id);
DbSet.RemoveRange(recordsToDelete);
}
public async Task<PostTag> FindByTagIdAndPostIdAsync(Guid postId, Guid tagId)
{
return await DbSet.FirstOrDefaultAsync(pt=> pt.PostId == postId && pt.TagId == tagId);
}
} }
} }

15
modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Tagging/EfCoreTagRepository.cs

@ -22,6 +22,11 @@ namespace Volo.Blogging.Tagging
} }
public async Task<Tag> GetByNameAsync(string name) public async Task<Tag> GetByNameAsync(string name)
{
return await DbSet.FirstAsync(t=>t.Name == name);
}
public async Task<Tag> FindByNameAsync(string name)
{ {
return await DbSet.FirstOrDefaultAsync(t=>t.Name == name); return await DbSet.FirstOrDefaultAsync(t=>t.Name == name);
} }
@ -30,5 +35,15 @@ namespace Volo.Blogging.Tagging
{ {
return await DbSet.Where(c => ids.Contains(c.Id)).ToListAsync(); return await DbSet.Where(c => ids.Contains(c.Id)).ToListAsync();
} }
public void DecreaseUsageCountOfTags(List<Guid> ids)
{
var tags = DbSet.Where(t => ids.Any(id => id == t.Id));
foreach (var tag in tags)
{
tag.DecreaseUsageCount();
}
}
} }
} }

27
modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Users/EfCoreQaUserRepository.cs

@ -0,0 +1,27 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Users.EntityFrameworkCore;
using Volo.Blogging.EntityFrameworkCore;
namespace Volo.Blogging.Users
{
public class EfCoreBlogUserRepository : EfCoreUserRepositoryBase<IBloggingDbContext, BlogUser>, IBlogUserRepository
{
public EfCoreBlogUserRepository(IDbContextProvider<IBloggingDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public async Task<List<BlogUser>> GetUsersAsync(int maxCount, string filter, CancellationToken cancellationToken = default)
{
return await DbSet
.WhereIf( !string.IsNullOrWhiteSpace( filter), x=>x.UserName.Contains(filter))
.Take(maxCount).ToListAsync(cancellationToken);
}
}
}

1
modules/blogging/src/Volo.Blogging.HttpApi.Client/Volo.Blogging.HttpApi.Client.csproj

@ -11,6 +11,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Volo.Blogging.Application.Contracts\Volo.Blogging.Application.Contracts.csproj" /> <ProjectReference Include="..\Volo.Blogging.Application.Contracts\Volo.Blogging.Application.Contracts.csproj" />
<ProjectReference Include="..\..\..\..\..\abp\framework\src\Volo.Abp.Http.Client\Volo.Abp.Http.Client.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

10
modules/blogging/src/Volo.Blogging.HttpApi.Client/Volo/Blogging/BloggingHttpApiClientModule.cs

@ -1,12 +1,20 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Http.Client;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
namespace Volo.Blogging namespace Volo.Blogging
{ {
[DependsOn( [DependsOn(
typeof(BloggingApplicationContractsModule))] typeof(BloggingApplicationContractsModule),
typeof(AbpHttpClientModule))]
public class BloggingHttpApiClientModule : AbpModule public class BloggingHttpApiClientModule : AbpModule
{ {
public const string RemoteServiceName = "Blogging";
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddHttpClientProxies(typeof(BloggingApplicationContractsModule).Assembly, RemoteServiceName);
}
} }
} }

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

@ -11,6 +11,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Volo.Blogging.Application.Contracts\Volo.Blogging.Application.Contracts.csproj" /> <ProjectReference Include="..\Volo.Blogging.Application.Contracts\Volo.Blogging.Application.Contracts.csproj" />
<ProjectReference Include="..\..\..\..\..\abp\framework\src\Volo.Abp.AspNetCore.Mvc\Volo.Abp.AspNetCore.Mvc.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

5
modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/BloggingHttpApiModule.cs

@ -1,10 +1,11 @@
using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
namespace Volo.Blogging namespace Volo.Blogging
{ {
[DependsOn( [DependsOn(
typeof(BloggingApplicationContractsModule))] typeof(BloggingApplicationContractsModule),
typeof(AbpAspNetCoreMvcModule))]
public class BloggingHttpApiModule : AbpModule public class BloggingHttpApiModule : AbpModule
{ {

77
modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/BlogsController.cs

@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Auditing;
using Volo.Blogging.Blogs;
using Volo.Blogging.Blogs.Dtos;
namespace Volo.Blogging
{
[RemoteService]
[Area("blogging")]
[Controller]
[ControllerName("Blogs")]
[Route("api/blogging/blogs")]
[DisableAuditing]
public class BlogsController : AbpController, IBlogAppService
{
private readonly IBlogAppService _blogAppService;
public BlogsController(IBlogAppService blogAppService)
{
_blogAppService = blogAppService;
}
[HttpGet]
[Route("")]
public async Task<PagedResultDto<BlogDto>> GetListPagedAsync(PagedAndSortedResultRequestDto input)
{
return await _blogAppService.GetListPagedAsync(input);
}
[HttpGet]
[Route("/all")]
public async Task<ListResultDto<BlogDto>> GetListAsync()
{
return await _blogAppService.GetListAsync();
}
[HttpGet]
[Route("{shortName}")]
public async Task<BlogDto> GetByShortNameAsync(string shortName)
{
return await _blogAppService.GetByShortNameAsync(shortName);
}
[HttpGet]
[Route("{id}")]
public async Task<BlogDto> GetAsync(Guid id)
{
return await _blogAppService.GetAsync(id);
}
[HttpPost]
public async Task<BlogDto> Create(CreateBlogDto input)
{
return await _blogAppService.Create(input);
}
[HttpPut]
[Route("{id}")]
public async Task<BlogDto> Update(Guid id, UpdateBlogDto input)
{
throw new NotImplementedException();
}
[HttpDelete]
public async Task Delete(Guid id)
{
await _blogAppService.Delete(id);
}
}
}

6
modules/blogging/src/Volo.Blogging.Web/AbpBloggingWebAutoMapperProfile.cs

@ -1,8 +1,11 @@
using AutoMapper; using AutoMapper;
using Volo.Abp.AutoMapper; using Volo.Abp.AutoMapper;
using Volo.Blogging.Blogs; using Volo.Blogging.Blogs;
using Volo.Blogging.Blogs.Dtos;
using Volo.Blogging.Pages.Admin.Blogs;
using Volo.Blogging.Pages.Blog.Posts; using Volo.Blogging.Pages.Blog.Posts;
using Volo.Blogging.Posts; using Volo.Blogging.Posts;
using EditModel = Volo.Blogging.Pages.Admin.Blogs.EditModel;
using IndexModel = Volo.Blogging.Pages.Blog.IndexModel; using IndexModel = Volo.Blogging.Pages.Blog.IndexModel;
namespace Volo.Blogging namespace Volo.Blogging
@ -13,7 +16,8 @@ namespace Volo.Blogging
{ {
CreateMap<PostWithDetailsDto, EditPostViewModel>().Ignore(x=>x.Tags); CreateMap<PostWithDetailsDto, EditPostViewModel>().Ignore(x=>x.Tags);
CreateMap<NewModel.CreatePostViewModel, CreatePostDto>(); CreateMap<NewModel.CreatePostViewModel, CreatePostDto>();
CreateMap<IndexModel.BlogIndexViewModel, CreateBlogDto>(); CreateMap<CreateModel.BlogCreateModalView, CreateBlogDto>();
CreateMap<BlogDto, EditModel.BlogEditViewModel>();
} }
} }
} }

7
modules/blogging/src/Volo.Blogging.Web/BloggingMenuContributor.cs

@ -27,7 +27,14 @@ namespace Volo.Blogging
var rootMenuItem = new ApplicationMenuItem("Blogs", l["Menu:Blogs"], "/Blog"); var rootMenuItem = new ApplicationMenuItem("Blogs", l["Menu:Blogs"], "/Blog");
context.Menu.AddItem(rootMenuItem); context.Menu.AddItem(rootMenuItem);
var managementRootMenuItem = new ApplicationMenuItem("BlogManagement", l["Menu:BlogManagement"]);
managementRootMenuItem.AddItem(new ApplicationMenuItem("Blogs", l["Menu:Blogs"], "/Admin/Blogs"));
context.Menu.AddItem(managementRootMenuItem);
} }
} }
} }
} }

8
modules/blogging/src/Volo.Blogging.Web/BloggingWebModule.cs

@ -2,6 +2,7 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc.Localization; using Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AutoMapper; using Volo.Abp.AutoMapper;
using Volo.Abp.Localization; using Volo.Abp.Localization;
using Volo.Abp.Localization.Resources.AbpValidation; using Volo.Abp.Localization.Resources.AbpValidation;
@ -15,8 +16,9 @@ namespace Volo.Blogging
{ {
[DependsOn( [DependsOn(
typeof(BloggingHttpApiModule), typeof(BloggingHttpApiModule),
typeof(AbpAutoMapperModule), typeof(AbpAspNetCoreMvcUiBootstrapModule),
typeof(AbpAspNetCoreMvcUiBootstrapModule) typeof(AbpAspNetCoreMvcUiBundlingModule),
typeof(AbpAutoMapperModule)
)] )]
public class BloggingWebModule : AbpModule public class BloggingWebModule : AbpModule
{ {
@ -37,7 +39,7 @@ namespace Volo.Blogging
Configure<VirtualFileSystemOptions>(options => Configure<VirtualFileSystemOptions>(options =>
{ {
options.FileSets.AddEmbedded<BloggingWebModule>("Volo.Blogging"); options.FileSets.AddEmbedded<BloggingWebModule>("Volo.Blogging.Web");
}); });
Configure<AbpLocalizationOptions>(options => Configure<AbpLocalizationOptions>(options =>

24
modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/en.json

@ -2,28 +2,34 @@
"culture": "en", "culture": "en",
"texts": { "texts": {
"Menu:Blogs": "Blog", "Menu:Blogs": "Blog",
"Menu:BlogManagement": "Blog Management",
"Title": "Title", "Title": "Title",
"ContinueReading": "Continue Reading", "ContinueReading": "Continue Reading",
"DaysAgo": "days ago", "DaysAgo": "{0} days ago",
"YearsAgo": "years ago", "YearsAgo": "{0} years ago",
"MonthsAgo": "months ago", "MonthsAgo": "{0} months ago",
"WeeksAgo": "weeks ago", "WeeksAgo": "{0} weeks ago",
"MinutesAgo": "minutes ago", "MinutesAgo": "{0} minutes ago",
"SecondsAgo": "seconds ago", "SecondsAgo": "{0} seconds ago",
"HoursAgo": "hours ago", "HoursAgo": "{0} hours ago",
"Now": "now", "Now": "now",
"Content": "Content", "Content": "Content",
"SeeAll": "See All", "SeeAll": "See All",
"PopularTags": "Popular Tags", "PopularTags": "Popular Tags",
"Read": "Read", "Read": "{0} Read",
"LastPosts": "Last Posts", "LastPosts": "Last Posts",
"LeaveComment": "Leave Comment", "LeaveComment": "Leave Comment",
"TagsInThisArticle": "Tags in this article", "TagsInThisArticle": "Tags in this article",
"Posts": "Posts", "Posts": "Posts",
"Edit": "Edit", "Edit": "Edit",
"Comment": "Comment", "CommentDeletionWarningMessage": "Comment will be deleted.",
"BlogDeletionWarningMessage": "Blog will be deleted.",
"AreYouSure": "Are you sure?",
"Comment": "{0} Comment",
"ShareOnTwitter": "Share On Twitter",
"CoverImage": "Cover Image", "CoverImage": "Cover Image",
"CreateANewPost": "Create A New Post", "CreateANewPost": "Create A New Post",
"CreateANewBlog": "Create A New Blog",
"WhatIsNew": "What is new?" "WhatIsNew": "What is new?"
} }
} }

24
modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/tr.json

@ -3,27 +3,33 @@
"texts": { "texts": {
"Menu:Blogs": "Blog", "Menu:Blogs": "Blog",
"Title": "Başlık", "Title": "Başlık",
"Menu:BlogManagement": "Blog Yönetimi",
"ContinueReading": "Devamı...", "ContinueReading": "Devamı...",
"DaysAgo": "Gün Önce", "DaysAgo": "{0} Gün Önce",
"YearsAgo": "yıl önce", "YearsAgo": "{0} yıl önce",
"MonthsAgo": "ay önce", "MonthsAgo": "{0} ay önce",
"WeeksAgo": "hafta önce", "WeeksAgo": "{0} hafta önce",
"MinutesAgo": "dakika önce", "MinutesAgo": "{0} dakika önce",
"SecondsAgo": "saniye önce", "SecondsAgo": "{0} saniye önce",
"HoursAgo": "saat önce", "HoursAgo": "{0} saat önce",
"Now": "şimdi", "Now": "şimdi",
"Content": "İçerik", "Content": "İçerik",
"SeeAll": "Hepsini Gör", "SeeAll": "Hepsini Gör",
"PopularTags": "Popüler Etiketler", "PopularTags": "Popüler Etiketler",
"Read": "Okunma", "ReadWithCount": "{0} Okunma",
"ShareOnTwitter": "Twitter'da paylaş",
"LastPosts": "Son Yazılar", "LastPosts": "Son Yazılar",
"LeaveComment": "Yorum Bırak", "LeaveComment": "Yorum Bırak",
"TagsInThisArticle": "Makalenin Etiketleri", "TagsInThisArticle": "Makalenin Etiketleri",
"Posts": "Yazılar", "Posts": "Yazılar",
"Edit": "Düzenle", "Edit": "Düzenle",
"Comment": "Yorum", "CommentDeletionWarningMessage": "Yorum silinecek.",
"BlogDeletionWarningMessage": "Blog silinecek.",
"AreYouSure": "Emin misiniz?",
"CommentWithCount": "{0} Yorum",
"CoverImage": "Kapak resmi", "CoverImage": "Kapak resmi",
"CreateANewPost": "Yeni Yazı oluştur", "CreateANewPost": "Yeni Yazı oluştur",
"CreateANewBlog": "Yeni Blog Ekle",
"WhatIsNew": "Yeniler" "WhatIsNew": "Yeniler"
} }
} }

20
modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/Create.cshtml

@ -0,0 +1,20 @@
@page
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
@using Volo.Blogging.Pages.Blog
@inherits BloggingPage
@model Volo.Blogging.Pages.Admin.Blogs.CreateModel
@{
Layout = null;
}
<abp-dynamic-form submit-button="false" abp-model="Blog" asp-page="/Admin/Blogs/Create">
<abp-modal size="@(AbpModalSize.Large)">
<abp-modal-header title="@L["Create"].Value"></abp-modal-header>
<abp-modal-body>
<abp-form-content/>
</abp-modal-body>
<abp-modal-footer buttons="@(AbpModalButtons.Cancel|AbpModalButtons.Save)">
</abp-modal-footer>
</abp-modal>
</abp-dynamic-form>

50
modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/Create.cshtml.cs

@ -0,0 +1,50 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
using Volo.Blogging.Blogs;
using Volo.Blogging.Blogs.Dtos;
namespace Volo.Blogging.Pages.Admin.Blogs
{
public class CreateModel : AbpPageModel
{
private readonly IBlogAppService _blogAppService;
[BindProperty]
public BlogCreateModalView Blog { get; set; } = new BlogCreateModalView();
public CreateModel(IBlogAppService blogAppService)
{
_blogAppService = blogAppService;
}
public void OnGet()
{
}
public async void OnPostAsync()
{
var language = ObjectMapper.Map<BlogCreateModalView, CreateBlogDto>(Blog);
await _blogAppService.Create(language);
}
public class BlogCreateModalView
{
[Required]
[StringLength(BlogConsts.MaxNameLength)]
public string Name { get; set; }
[Required]
[StringLength(BlogConsts.MaxShortNameLength)]
public string ShortName { get; set; }
[StringLength(BlogConsts.MaxDescriptionLength)]
public string Description { get; set; }
}
}
}

20
modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/Edit.cshtml

@ -0,0 +1,20 @@
@page
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
@using Volo.Blogging.Pages.Blog
@inherits BloggingPage
@model Volo.Blogging.Pages.Admin.Blogs.EditModel
@{
Layout = null;
}
<abp-dynamic-form submit-button="false" abp-model="Blog" asp-page="/Admin/Blogs/Edit">
<abp-modal size="@(AbpModalSize.Large)">
<abp-modal-header title="@L["Create"].Value"></abp-modal-header>
<abp-modal-body>
<abp-form-content />
</abp-modal-body>
<abp-modal-footer buttons="@(AbpModalButtons.Cancel|AbpModalButtons.Save)">
</abp-modal-footer>
</abp-modal>
</abp-dynamic-form>

65
modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/Edit.cshtml.cs

@ -0,0 +1,65 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
using Volo.Blogging.Blogs;
using Volo.Blogging.Blogs.Dtos;
using Volo.Blogging.Posts;
namespace Volo.Blogging.Pages.Admin.Blogs
{
public class EditModel : AbpPageModel
{
private readonly IBlogAppService _blogAppService;
[BindProperty(SupportsGet = true)]
public Guid BlogId { get; set; }
[BindProperty]
public BlogEditViewModel Blog { get; set; } = new BlogEditViewModel();
public EditModel(IBlogAppService blogAppService)
{
_blogAppService = blogAppService;
}
public async Task OnGet()
{
var blog = await _blogAppService.GetAsync(BlogId);
Blog = ObjectMapper.Map<BlogDto, BlogEditViewModel>(blog);
}
public async Task OnPost()
{
await _blogAppService.Update(Blog.Id, new UpdateBlogDto()
{
Name = Blog.Name,
ShortName = Blog.ShortName,
Description = Blog.Description
});
}
public class BlogEditViewModel
{
[HiddenInput]
[Required]
public Guid Id { get; set; }
[Required]
[StringLength(BlogConsts.MaxNameLength)]
public string Name { get; set; }
[Required]
[StringLength(BlogConsts.MaxShortNameLength)]
public string ShortName { get; set; }
[StringLength(BlogConsts.MaxDescriptionLength)]
public string Description { get; set; }
}
}
}

44
modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/Index.cshtml

@ -0,0 +1,44 @@
@page
@using Microsoft.AspNetCore.Authorization
@using Volo.Blogging
@using Volo.Blogging.Pages.Blog
@inherits BloggingPage
@model IndexModel
@inject IAuthorizationService Authorization
@{
ViewBag.PageTitle = "Blogs";
}
@section scripts {
<abp-script src="/Pages/Admin/Blogs/index.js" />
<abp-script src="/Pages/Admin/Blogs/create.js" />
}
<abp-card>
<abp-card-header>
<abp-row>
<abp-column size-md="_6">
<h2>@L["Blogs"]</h2>
</abp-column>
<abp-column size-md="_6" class="text-right">
@if (await Authorization.IsGrantedAsync(BloggingPermissions.Blogs.Create))
{
<abp-button icon="plus" text="@L["CreateANewBlog"].Value" button-type="Primary" id="CreateNewBlogButtonId"></abp-button>
}
</abp-column>
</abp-row>
</abp-card-header>
<abp-card-body>
<abp-table striped-rows="true" id="BlogsTable" class="nowrap">
<thead>
<tr>
<th>@L["Actions"]</th>
<th>@L["Name"]</th>
<th>@L["ShortName"]</th>
<th>@L["CreationTime"]</th>
<th>@L["Description"]</th>
</tr>
</thead>
</abp-table>
</abp-card-body>
</abp-card>

12
modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/Index.cshtml.cs

@ -0,0 +1,12 @@
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
namespace Volo.Blogging.Pages.Admin.Blogs
{
public class IndexModel : AbpPageModel
{
public void OnGet()
{
}
}
}

14
modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/create.js

@ -0,0 +1,14 @@
var abp = abp || {};
$(function () {
abp.modals.blogCreate = function () {
var initModal = function (publicApi, args) {
var $form = publicApi.getForm();
};
return {
initModal: initModal
}
};
});

14
modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/edit.js

@ -0,0 +1,14 @@
var abp = abp || {};
$(function () {
abp.modals.blogEdit = function () {
var initModal = function (publicApi, args) {
var $form = publicApi.getForm();
};
return {
initModal: initModal
}
};
});

86
modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/index.js

@ -0,0 +1,86 @@
$(function () {
var l = abp.localization.getResource('Blogging');
var _createModal = new abp.ModalManager(abp.appPath + 'Admin/Blogs/Create');
var _editModal = new abp.ModalManager(abp.appPath + 'Admin/Blogs/Edit');
var _dataTable = $('#BlogsTable').DataTable(abp.libs.datatables.normalizeConfiguration({
processing: true,
serverSide: true,
paging: true,
searching: false,
autoWidth: false,
scrollX: true,
scrollCollapse: true,
order: [[3, "desc"]],
ajax: abp.libs.datatables.createAjax(volo.blogging.blogs.getListPaged),
columnDefs: [
{
targets: 0, //optional
rowAction: {
items:
[
{
text: l('Edit'),
visible: function () {
return true; //TODO: Check permission
},
action: function (data) {
_editModal.open({
blogId: data.record.id
});
}
},
{
text: l('Delete'),
visible: function () {
return true; //TODO: Check permission
},
confirmMessage: function (data) { return l('BlogDeletionWarningMessage') },
action: function (data) {
volo.blogging.blogs
.delete(data.record.id)
.then(function () {
_dataTable.ajax.reload();
});
}
}
]
}
},
{
target: 1,
data: "name"
},
{
target: 2,
data: "shortName"
},
{
target: 3,
data: "creationTime",
render: function (date) {
return date;
}
},
{
target: 4,
data: "description"
}
]
}));
$("#CreateNewBlogButtonId").click(function () {
_createModal.open();
});
_createModal.onClose(function () {
_dataTable.ajax.reload();
});
_editModal.onResult(function () {
_dataTable.ajax.reload();
});
});

66
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/BloggingPage.cs

@ -1,4 +1,7 @@
using System; using System;
using System.Text;
using CommonMark;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Localization; using Microsoft.AspNetCore.Mvc.Localization;
using Microsoft.AspNetCore.Mvc.Razor.Internal; using Microsoft.AspNetCore.Mvc.Razor.Internal;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
@ -13,6 +16,8 @@ namespace Volo.Blogging.Pages.Blog
public const string DefaultTitle = "Blog"; public const string DefaultTitle = "Blog";
public const int MaxShortContentLength = 128;
public string GetTitle(string title = null) public string GetTitle(string title = null)
{ {
if (string.IsNullOrWhiteSpace(title)) if (string.IsNullOrWhiteSpace(title))
@ -23,7 +28,50 @@ namespace Volo.Blogging.Pages.Blog
return title; return title;
} }
public string ConvertDatetimeToTimeAgo(DateTime dt) public string GetShortContent(string content)
{
var openingTag = "<p>";
var closingTag = "</p>";
var html = RenderMarkdownToString(content);
var splittedHtml = html.Split(closingTag);
if (splittedHtml.Length < 1)
{
return "";
}
var firstHtmlPart = splittedHtml[0];
var paragraphStartIndex = firstHtmlPart.IndexOf(openingTag, StringComparison.Ordinal) + openingTag.Length;
if (firstHtmlPart.Length - paragraphStartIndex <= MaxShortContentLength)
{
return firstHtmlPart.Substring(paragraphStartIndex);
}
return firstHtmlPart.Substring(paragraphStartIndex, MaxShortContentLength) + "...";
}
public IHtmlContent RenderMarkdownToHtml(string content)
{
byte[] bytes = Encoding.Default.GetBytes(content);
var utf8Content = Encoding.UTF8.GetString(bytes);
var html = CommonMarkConverter.Convert(utf8Content);
return new HtmlString(html);
}
public string RenderMarkdownToString(string content)
{
byte[] bytes = Encoding.Default.GetBytes(content);
var utf8Content = Encoding.UTF8.GetString(bytes);
return CommonMarkConverter.Convert(utf8Content);
}
public LocalizedHtmlString ConvertDatetimeToTimeAgo(DateTime dt)
{ {
var timeDiff = DateTime.Now - dt; var timeDiff = DateTime.Now - dt;
@ -31,37 +79,37 @@ namespace Volo.Blogging.Pages.Blog
if (diffInDays >= 365) if (diffInDays >= 365)
{ {
return diffInDays / 365 + L["YearsAgo"].Value; return L["YearsAgo", diffInDays / 365];
} }
if (diffInDays >= 30) if (diffInDays >= 30)
{ {
return diffInDays / 30 + L["MonthsAgo"].Value; return L["MonthsAgo", diffInDays / 30];
} }
if (diffInDays >= 7) if (diffInDays >= 7)
{ {
return diffInDays / 7 + L["WeeksAgo"].Value; return L["WeeksAgo", diffInDays / 7];
} }
if (diffInDays >= 1) if (diffInDays >= 1)
{ {
return diffInDays + L["DaysAgo"].Value; return L["DaysAgo", diffInDays];
} }
var diffInSeconds = (int) timeDiff.TotalSeconds; var diffInSeconds = (int) timeDiff.TotalSeconds;
if (diffInSeconds >= 3600) if (diffInSeconds >= 3600)
{ {
return diffInSeconds / 3600 + L["HoursAgo"].Value; return L["HoursAgo", diffInSeconds / 3600];
} }
if (diffInSeconds >= 60) if (diffInSeconds >= 60)
{ {
return diffInSeconds / 60 + L["MinutesAgo"].Value; return L["MinutesAgo", diffInSeconds];
} }
if (diffInSeconds >= 1) if (diffInSeconds >= 1)
{ {
return diffInSeconds + L["SecondsAgo"].Value; return L["SecondsAgo", diffInSeconds];
} }
return L["Now"].Value; return L["Now"];
} }
} }
} }

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

@ -15,9 +15,3 @@
<li><a asp-page="./Posts/Index" asp-route-blogShortName="@blog.ShortName">@blog.Name</a></li> <li><a asp-page="./Posts/Index" asp-route-blogShortName="@blog.ShortName">@blog.Name</a></li>
} }
</ul> </ul>
<br/>
<br/>
<br/>
<abp-dynamic-form abp-model="Blog" submit-button="true"/>

32
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Index.cshtml.cs

@ -4,6 +4,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
using Volo.Blogging.Blogs; using Volo.Blogging.Blogs;
using Volo.Blogging.Blogs.Dtos;
namespace Volo.Blogging.Pages.Blog namespace Volo.Blogging.Pages.Blog
{ {
@ -13,9 +14,6 @@ namespace Volo.Blogging.Pages.Blog
public IReadOnlyList<BlogDto> Blogs { get; private set; } public IReadOnlyList<BlogDto> Blogs { get; private set; }
[BindProperty]
public BlogIndexViewModel Blog { get; set; }
public IndexModel(IBlogAppService blogAppService) public IndexModel(IBlogAppService blogAppService)
{ {
_blogAppService = blogAppService; _blogAppService = blogAppService;
@ -35,33 +33,5 @@ namespace Volo.Blogging.Pages.Blog
return Page(); return Page();
} }
public async Task<IActionResult> OnPostAsync()
{
await _blogAppService.Create(ObjectMapper.Map<BlogIndexViewModel, CreateBlogDto>(Blog));
var result = await _blogAppService.GetListAsync();
if (result.Items.Count == 1)
{
var blog = result.Items[0];
return RedirectToPage("./Posts/Index", new { blogShortName = blog.ShortName});
}
Blogs = result.Items;
return Page();
}
public class BlogIndexViewModel
{
[Required]
public string Name { get; set; }
[Required]
public string ShortName { get; set; }
public string Description { get; set; }
}
} }
} }

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

@ -1,6 +1,7 @@
@page @page
@inherits Volo.Blogging.Pages.Blog.BloggingPage @inherits Volo.Blogging.Pages.Blog.BloggingPage
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Http.Extensions
@using Volo.Blogging @using Volo.Blogging
@using Volo.Blogging.Pages.Blog.Posts @using Volo.Blogging.Pages.Blog.Posts
@inject IAuthorizationService Authorization @inject IAuthorizationService Authorization
@ -20,7 +21,68 @@
</abp-style-bundle> </abp-style-bundle>
} }
<div class="vs-blog"> <div class="vs-blog">
<div class="row mb-5"> <div class="vs-blog-header">
<div class="vs-blog-title">
<div class="row">
<div class="col">
<h1 class="my-0 display-inline-block">
@L["Volosoft"]
<small class="text-muted">
@L["Blog"]
</small>
</h1>
</div>
<div class="article-owner">
<div class="article-infos">
<div class="user-card">
<a href="#">
<img src="https://placeimg.com/60/60/people" class="article-avatar">
</a>
<a href="#">
<strong> @(Model.Post.Writer == null ? "" : Model.Post.Writer.UserName)</strong>, @ConvertDatetimeToTimeAgo(Model.Post.CreationTime)
</a>
<span class="seperator">|</span>
<a href="#">
<i class="fa fa-eye"></i> @L["ReadWithCount", @Model.Post.ReadCount]
</a>
<span class="seperator">|</span>
<a href="#">
<i class="fa fa-comment"></i> @L["CommentWithCount", @Model.CommentCount]
</a>
@if (await Authorization.IsGrantedAsync(BloggingPermissions.Posts.Update) || (CurrentUser.Id == Model.Post.CreatorId))
{
<span class="seperator">|</span>
<a asp-page="./Edit" asp-route-postId="@Model.Post.Id" asp-route-blogShortName="@Model.BlogShortName">
<i class="fa fa-pencil"></i> @L["Edit"]
</a>
}
</div>
</div>
</div>
<div class="col-sm-4 text-right">
<br />
<a asp-page="/Blog/Posts/Index" asp-route-blogShortName="@Model.BlogShortName">
<i class="fa fa fa-angle-left"></i> @L["Blog"]
</a>
<span class="vs-seperator">|</span>
<a href="#">
<i class="fa fa-twitter"></i>
</a>
<span class="vs-seperator">|</span>
<a href="#">
<i class="fa fa-github"></i>
</a>
<span class="vs-seperator">|</span>
<a href="#">
<i class="fa fa-stack-overflow"></i>
</a>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12 mx-auto"> <div class="col-md-12 mx-auto">
<section class="hero-section"> <section class="hero-section">
<div class="hero-articles"> <div class="hero-articles">
@ -44,15 +106,15 @@
<img src="https://placeimg.com/60/60/people" class="article-avatar"> <img src="https://placeimg.com/60/60/people" class="article-avatar">
</a> </a>
<a href="#"> <a href="#">
<strong> Halil İbrahim Kalkan</strong>, @ConvertDatetimeToTimeAgo(Model.Post.CreationTime) <strong> @(Model.Post.Writer == null ? "" : Model.Post.Writer.UserName)</strong>, @ConvertDatetimeToTimeAgo(Model.Post.CreationTime)
</a> </a>
<span class="seperator">|</span> <span class="vs-seperator">|</span>
<a href="#"> <a href="#">
<i class="fa fa-eye"></i> @Model.Post.ReadCount @L["Read"] <i class="fa fa-eye"></i> @L["ReadWithCount", @Model.Post.ReadCount]
</a> </a>
<span class="seperator">|</span> <span class="vs-seperator">|</span>
<a href="#"> <a href="#">
<i class="fa fa-comment"></i> @Model.CommentCount @L["Comment"] <i class="fa fa-comment"></i> @L["CommentWithCount", @Model.CommentCount]
</a> </a>
@ -66,14 +128,6 @@
</div> </div>
</div> </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>
</section> </section>
</div> </div>
@ -82,7 +136,10 @@
<div class="row"> <div class="row">
<div class="col-md-8 mx-auto"> <div class="col-md-8 mx-auto">
<section class="post-content"> <section class="post-content">
<p>@Html.Raw(Model.FormattedContent)</p> <p>
@Html.Raw(RenderMarkdownToHtml(Model.Post.Content))
</p>
</section> </section>
</div> </div>
@ -97,41 +154,56 @@
<a asp-page="/Blog/Posts/Index" asp-route-blogShortName="@Model.BlogShortName" asp-route-tagName="@tag.Name" class="tag">@tag.Name</a> <a asp-page="/Blog/Posts/Index" asp-route-blogShortName="@Model.BlogShortName" asp-route-tagName="@tag.Name" class="tag">@tag.Name</a>
} }
</div> </div>
<div class="comment-form mt-4">
<div class="clearfix bg-light p-4"> <a class="btn-rounded float-right btn btn-primary"
<h3 class="mt-0">@L["LeaveComment"]</h3> href="@Model.GetTwitterShareUrl(Model.Post.Title, HttpContext.Request.GetEncodedUrl())" target="_blank">
<div> <i class="fa fa-twitter"></i>
<form method="post"> @L["ShareOnTwitter"]
<input name="postId" value="@Model.Post.Id" hidden /> </a>
<input name="repliedCommentId" id="repliedCommentId" hidden />
<div class="form-group"> @if (await Authorization.IsGrantedAsync(BloggingPermissions.Comments.Create))
<textarea class="form-control no-border" name="text" id="textBoxId" rows="4"></textarea> {
</div> <div class="comment-form mt-4">
<abp-button button-type="Primary" class="btn-rounded float-right" type="submit" text="@L["Submit"].Value" /> <div class="vs-blog-title mb-0">
</form> <h3>@L["LeaveComment"]</h3>
</div>
<div class="clearfix bg-light p-4">
<div>
<form method="post">
<input name="postId" value="@Model.Post.Id" hidden />
<input name="repliedCommentId" id="repliedCommentId" hidden />
<div class="form-group">
<textarea class="form-control no-border" name="text" id="textBoxId" rows="4"></textarea>
</div>
<abp-button button-type="Primary" class="btn-rounded float-right" type="submit" text="@L["Submit"].Value" />
</form>
</div>
</div> </div>
</div> </div>
</div> }
<div class="comment-area">
<div class="comment-area">
@foreach (var commentWithRepliesDto in Model.CommentsWithReplies) @foreach (var commentWithRepliesDto in Model.CommentsWithReplies)
{ {
<div class="media"> <div class="media">
<img class="d-flex mr-3 rounded-circle comment-avatar" src="https://placeimg.com/120/120/people" alt=""> <img class="d-flex mr-3 rounded-circle comment-avatar" src="https://placeimg.com/120/120/people" alt="">
<div class="media-body"> <div class="media-body">
<h5 class="comment-owner"> <h5 class="comment-owner">
Armağan Ünlü @(commentWithRepliesDto.Comment.Writer == null ? "" : commentWithRepliesDto.Comment.Writer.UserName)
<span class="float-right">@ConvertDatetimeToTimeAgo(commentWithRepliesDto.Comment.CreationTime)</span> <span class="float-right">@ConvertDatetimeToTimeAgo(commentWithRepliesDto.Comment.CreationTime)</span>
</h5> </h5>
<p id="@commentWithRepliesDto.Comment.Id"> <p id="@commentWithRepliesDto.Comment.Id">
@commentWithRepliesDto.Comment.Text @commentWithRepliesDto.Comment.Text
</p> </p>
<div class="comment-buttons"> <div class="comment-buttons font-75 bg-light">
@if (await Authorization.IsGrantedAsync(BloggingPermissions.Comments.Create))
{
<a href="#" class="tag replyLink" data-relpyid="@commentWithRepliesDto.Comment.Id">
<i class="fa fa-reply" aria-hidden="true"></i> @L["Reply"]
</a>
}
<a href="#" class="tag replyLink" data-relpyid="@commentWithRepliesDto.Comment.Id">
<i class="fa fa-reply" aria-hidden="true"></i> @L["Reply"]
</a>
@if (await Authorization.IsGrantedAsync(BloggingPermissions.Comments.Delete)) @if (await Authorization.IsGrantedAsync(BloggingPermissions.Comments.Delete))
{ {
<span class="seperator">|</span> <span class="seperator">|</span>
@ -149,34 +221,39 @@
} }
</div> </div>
<div class="comment-form mt-4 replyForm"> @if (await Authorization.IsGrantedAsync(BloggingPermissions.Comments.Create))
<div class="clearfix bg-light p-4"> {
<h3 class="mt-0">@L["ReplyTo"] Armağan Ünlü</h3> <div class="comment-form mt-4 replyForm">
<div> <div class="clearfix bg-light p-4">
<form method="post"> <h3 class="mt-0">
<input name="postId" value="@Model.Post.Id" hidden /> @L["ReplyTo"]
<input name="repliedCommentId" value="@commentWithRepliesDto.Comment.Id" hidden /> @(commentWithRepliesDto.Comment.Writer == null ? "" : commentWithRepliesDto.Comment.Writer.UserName)
</h3>
<div>
<form method="post">
<input name="postId" value="@Model.Post.Id" hidden />
<input name="repliedCommentId" value="@commentWithRepliesDto.Comment.Id" hidden />
<div class="form-group"> <div class="form-group">
<textarea class="form-control no-border" name="text" id="textBoxId" rows="4"></textarea> <textarea class="form-control no-border" name="text" id="textBoxId" rows="4"></textarea>
</div> </div>
<abp-button button-type="Primary" class="btn-rounded float-right" type="submit" text="@L["Comment"].Value" /> <abp-button button-type="Primary" class="btn-rounded float-right" type="submit" text="@L["Comment"].Value" />
</form> </form>
</div>
</div> </div>
</div> </div>
</div> }
@if (await Authorization.IsGrantedAsync(BloggingPermissions.Comments.Update) || (CurrentUser.Id == commentWithRepliesDto.Comment.CreatorId)) @if (await Authorization.IsGrantedAsync(BloggingPermissions.Comments.Update) || (CurrentUser.Id == commentWithRepliesDto.Comment.CreatorId))
{ {
<div class="comment-form mt-4 editForm"> <div class="comment-form mt-4 editForm">
<div class="clearfix bg-light p-4"> <div class="clearfix bg-light p-4">
<div> <div>
<form class="editFormClass"> <form class="editFormClass">
<input name="commentId" value="@commentWithRepliesDto.Comment.Id" hidden/> <input name="commentId" value="@commentWithRepliesDto.Comment.Id" hidden />
<div class="form-group"> <div class="form-group">
<textarea class="form-control no-border" name="text" id="textBoxId" rows="4">@commentWithRepliesDto.Comment.Text</textarea> <textarea class="form-control no-border" name="text" id="textBoxId" rows="4">@commentWithRepliesDto.Comment.Text</textarea>
</div> </div>
<abp-button button-type="Primary" class="btn-rounded float-right" type="submit" text="@L["Submit"].Value"/> <abp-button button-type="Primary" class="btn-rounded float-right" type="submit" text="@L["Submit"].Value" />
</form> </form>
</div> </div>
</div> </div>
@ -189,21 +266,24 @@
<img class="d-flex mr-3 rounded-circle answer-avatar" src="https://placeimg.com/120/120/people?t=1535457179534" alt=""> <img class="d-flex mr-3 rounded-circle answer-avatar" src="https://placeimg.com/120/120/people?t=1535457179534" alt="">
<div class="media-body"> <div class="media-body">
<h5 class="comment-owner"> <h5 class="comment-owner">
Zlatan Ibrahimovic @(reply.Writer == null ? "" : reply.Writer.UserName)
<span class="float-right">@ConvertDatetimeToTimeAgo(reply.CreationTime)</span> <span class="float-right">@ConvertDatetimeToTimeAgo(reply.CreationTime)</span>
</h5> </h5>
<p id="@reply.Id"> <p id="@reply.Id">
@reply.Text @reply.Text
</p> </p>
<div class="comment-buttons"> <div class="comment-buttons font-75 bg-light">
<a href="#" class="tag" data-relpyid="@commentWithRepliesDto.Comment.Id">
<i class="fa fa-reply" aria-hidden="true"></i> @L["Reply"]
</a>
@if (await Authorization.IsGrantedAsync(BloggingPermissions.Comments.Create))
{
<a href="#" class="tag replyLink" data-relpyid="@commentWithRepliesDto.Comment.Id">
<i class="fa fa-reply" aria-hidden="true"></i> @L["Reply"]
</a>
}
@if (await Authorization.IsGrantedAsync(BloggingPermissions.Comments.Delete) || (CurrentUser.Id == commentWithRepliesDto.Comment.CreatorId)) @if (await Authorization.IsGrantedAsync(BloggingPermissions.Comments.Delete) || (CurrentUser.Id == commentWithRepliesDto.Comment.CreatorId))
{ {
<span class="seperator">|</span> <span class="seperator">|</span>
<a href="#" class="tag" data-deleteid="@reply.Id"> <a href="#" class="tag deleteLink" data-deleteid="@reply.Id">
<i class="fa fa-trash" aria-hidden="true"></i> @L["Delete"] <i class="fa fa-trash" aria-hidden="true"></i> @L["Delete"]
</a> </a>
} }
@ -211,39 +291,44 @@
@if (await Authorization.IsGrantedAsync(BloggingPermissions.Comments.Update) || (CurrentUser.Id == commentWithRepliesDto.Comment.CreatorId)) @if (await Authorization.IsGrantedAsync(BloggingPermissions.Comments.Update) || (CurrentUser.Id == commentWithRepliesDto.Comment.CreatorId))
{ {
<span class="seperator">|</span> <span class="seperator">|</span>
<a href="#" class="tag" data-updateid="@reply.Id"> <a href="#" class="tag updateLink" data-updateid="@reply.Id">
<i class="fa fa-pencil" aria-hidden="true"></i> @L["Edit"] <i class="fa fa-pencil" aria-hidden="true"></i> @L["Edit"]
</a> </a>
} }
</div> </div>
<div class="comment-form mt-4 replyForm"> @if (await Authorization.IsGrantedAsync(BloggingPermissions.Comments.Create))
<div class="clearfix bg-light p-4"> {
<h3 class="mt-0">@L["ReplyTo"] Armağan Ünlü</h3> <div class="comment-form mt-4 replyForm">
<div> <div class="clearfix bg-light p-4">
<form method="post"> <h3 class="mt-0">
<input name="postId" value="@Model.Post.Id" hidden /> @L["ReplyTo"]
<input name="repliedCommentId" value="@commentWithRepliesDto.Comment.Id" hidden /> @(commentWithRepliesDto.Comment.Writer == null ? "" : commentWithRepliesDto.Comment.Writer.UserName)
<div class="form-group"> </h3>
<textarea class="form-control no-border" name="text" id="textBoxId" rows="4"></textarea> <div>
</div> <form method="post">
<abp-button button-type="Primary" class="btn-rounded float-right" type="submit" text="@L["Submit"].Value" /> <input name="postId" value="@Model.Post.Id" hidden />
</form> <input name="repliedCommentId" value="@commentWithRepliesDto.Comment.Id" hidden />
<div class="form-group">
<textarea class="form-control no-border" name="text" id="textBoxId" rows="4"></textarea>
</div>
<abp-button button-type="Primary" class="btn-rounded float-right" type="submit" text="@L["Submit"].Value" />
</form>
</div>
</div> </div>
</div> </div>
</div> }
@if (await Authorization.IsGrantedAsync(BloggingPermissions.Comments.Update) || (CurrentUser.Id == commentWithRepliesDto.Comment.CreatorId)) @if (await Authorization.IsGrantedAsync(BloggingPermissions.Comments.Update) || (CurrentUser.Id == commentWithRepliesDto.Comment.CreatorId))
{ {
<div class="comment-form mt-4 editForm"> <div class="comment-form mt-4 editForm">
<div class="clearfix bg-light p-4"> <div class="clearfix bg-light p-4">
<div> <div>
<form class="editFormClass"> <form class="editFormClass">
<input name="commentId" value="@reply.Id" hidden/> <input name="commentId" value="@reply.Id" hidden />
<div class="form-group"> <div class="form-group">
<textarea class="form-control no-border" name="text" id="textBoxId" rows="4">@reply.Text</textarea> <textarea class="form-control no-border" name="text" id="textBoxId" rows="4">@reply.Text</textarea>
</div> </div>
<abp-button button-type="Primary" class="btn-rounded float-right" type="submit" text="@L["Submit"].Value"/> <abp-button button-type="Primary" class="btn-rounded float-right" type="submit" text="@L["Submit"].Value" />
</form> </form>
</div> </div>
</div> </div>

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

@ -1,12 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using CommonMark; using System.Web;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.RazorPages;
using Volo.Blogging.Blogs; using Volo.Blogging.Blogs;
using Volo.Blogging.Blogs.Dtos;
using Volo.Blogging.Comments; using Volo.Blogging.Comments;
using Volo.Blogging.Comments.Dtos; using Volo.Blogging.Comments.Dtos;
using Volo.Blogging.Posts; using Volo.Blogging.Posts;
@ -15,6 +14,7 @@ namespace Volo.Blogging.Pages.Blog.Posts
{ {
public class DetailModel : PageModel public class DetailModel : PageModel
{ {
private const int TwitterLinkLength = 23;
private readonly IPostAppService _postAppService; private readonly IPostAppService _postAppService;
private readonly IBlogAppService _blogAppService; private readonly IBlogAppService _blogAppService;
private readonly ICommentAppService _commentAppService; private readonly ICommentAppService _commentAppService;
@ -27,13 +27,11 @@ namespace Volo.Blogging.Pages.Blog.Posts
[BindProperty] [BindProperty]
public PostDetailsViewModel NewComment { get; set; } public PostDetailsViewModel NewComment { get; set; }
public int CommentCount { get; set; } public int CommentCount { get; set; }
public PostWithDetailsDto Post { get; set; } public PostWithDetailsDto Post { get; set; }
public IHtmlContent FormattedContent { get; set; }
public IReadOnlyList<CommentWithRepliesDto> CommentsWithReplies { get; set; } public IReadOnlyList<CommentWithRepliesDto> CommentsWithReplies { get; set; }
public BlogDto Blog { get; set; } public BlogDto Blog { get; set; }
@ -73,7 +71,6 @@ namespace Volo.Blogging.Pages.Blog.Posts
{ {
Blog = await _blogAppService.GetByShortNameAsync(BlogShortName); Blog = await _blogAppService.GetByShortNameAsync(BlogShortName);
Post = await _postAppService.GetForReadingAsync(new GetPostInput { BlogId = Blog.Id, Url = PostUrl }); Post = await _postAppService.GetForReadingAsync(new GetPostInput { BlogId = Blog.Id, Url = PostUrl });
FormattedContent = RenderMarkdown(Post.Content);
CommentsWithReplies = await _commentAppService.GetHierarchicalListOfPostAsync(new GetCommentListOfPostAsync() { PostId = Post.Id }); CommentsWithReplies = await _commentAppService.GetHierarchicalListOfPostAsync(new GetCommentListOfPostAsync() { PostId = Post.Id });
CountComments(); CountComments();
} }
@ -87,14 +84,20 @@ namespace Volo.Blogging.Pages.Blog.Posts
} }
} }
public IHtmlContent RenderMarkdown(string content) public string GetTwitterShareUrl(string title, string url)
{ {
byte[] bytes = Encoding.Default.GetBytes(content); var readAtString = " | Read More At";
var utf8Content = Encoding.UTF8.GetString(bytes); var linkedAccounts = "" ;
var otherCharsLength = (readAtString + linkedAccounts).Length;
var maxTitleLength = 280 - TwitterLinkLength - otherCharsLength;
title = title.Length < maxTitleLength ? title : title.Substring(0, maxTitleLength -3) + "...";
var text = title + readAtString + url + linkedAccounts;
var html = CommonMarkConverter.Convert(utf8Content); var builder = new UriBuilder("https://twitter.com/intent/tweet") { Query = "text=" + HttpUtility.UrlEncode(text) };
return builder.ToString();
return new HtmlString(html);
} }
public class PostDetailsViewModel public class PostDetailsViewModel

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

@ -24,72 +24,96 @@
} }
<div class="vs-blog"> <div class="vs-blog">
<div class="vs-blog-header">
<div class="container hero-container pb-5 bg-white"> <div class="vs-blog-title">
<div class="row">
<div class="col">
<h1 class="my-0 display-inline-block">
@Model.Blog.Name
<small class="text-muted">
@L["BLOG"]
</small>
</h1>
</div>
<div class="col-sm-4 text-right">
<br />
@if (await Authorization.IsGrantedAsync(BloggingPermissions.Posts.Create))
{
<a asp-page="./New" asp-route-blogShortName="@Model.BlogShortName">@L["CreateANewPost"]</a>
}
<span class="vs-seperator">|</span>
<a href="#">
<i class="fa fa-twitter"></i>
</a>
<span class="vs-seperator">|</span>
<a href="#">
<i class="fa fa-github"></i>
</a>
<span class="vs-seperator">|</span>
<a href="#">
<i class="fa fa-stack-overflow"></i>
</a>
</div>
</div>
</div>
</div>
<div class="pb-3">
<div class="row"> <div class="row">
<div class="col-md-8"> <div class="col-md-8">
<section class="hero-section"> <section class="hero-section">
<div class="owl-carousel owl-theme"> @for (var index = 0; index < Model.Posts.Count && index < 1; index++)
{
var post = Model.Posts[index];
<div class="hero-articles">
<div class="img-container">
<a asp-page="./Detail" asp-route-postUrl="@post.Url" asp-route-blogShortName="@Model.BlogShortName">
<img src="@post.CoverImage" class="hero-article-img">
</a>
</div>
<div class="hero-content">
<p class="tags">
@foreach (var tag in post.Tags)
{
<a asp-page="/Blog/Posts/Index" asp-route-blogShortName="@Model.BlogShortName" asp-route-tagName="@tag.Name" class="tag">@tag.Name</a>
}
</p>
<h2>
<a asp-page="./Detail" asp-route-postUrl="@post.Url" asp-route-blogShortName="@Model.BlogShortName">@post.Title</a>
</h2>
<p class="article-sum">
@(GetShortContent(post.Content))
</p>
<a asp-page="./Detail" asp-route-postUrl="@post.Url" asp-route-blogShortName="@Model.BlogShortName" class="btn btn-primary btn-rounded">@L["ContinueReading"]</a>
@for (var index = 0; index < Model.Posts.Count && index < 3; index++) </div>
{ <div class="article-owner">
var post = Model.Posts[index]; <div class="article-infos">
<div class="item"> <div class="user-card">
<div class="hero-articles"> <a>
<div class="img-container"> <img src="https://placeimg.com/120/120/people" class="article-avatar">
<a asp-page="./Detail" asp-route-postUrl="@post.Url" asp-route-blogShortName="@Model.BlogShortName"> </a>
<img src="@post.CoverImage" class="hero-article-img"> <a>
<strong>@post.Writer.UserName</strong>, @ConvertDatetimeToTimeAgo(post.CreationTime)
</a>
<span class="vs-seperator">|</span>
<a>
<i class="fa fa-eye"></i> @L["ReadWithCount", post.ReadCount]
</a>
<span class="vs-seperator">|</span>
<a>
<i class="fa fa-comment"></i> @L["CommentWithCount", @post.CommentCount]
</a> </a>
</div>
<div class="hero-content">
<p class="tags">
@foreach (var tag in post.Tags)
{
<a asp-page="/Blog/Posts/Index" asp-route-blogShortName="@Model.BlogShortName" asp-route-tagName="@tag.Name" class="tag">@tag.Name</a>
}
</p>
<h2>
<a asp-page="./Detail" asp-route-postUrl="@post.Url" asp-route-blogShortName="@Model.BlogShortName">@post.Title</a>
</h2>
<p class="article-sum">
@(post.Content == null ? "" : (post.Content.Length > 150 ? post.Content.Substring(0, 150) + "..." : post.Content))
</p>
<a asp-page="./Detail" asp-route-postUrl="@post.Url" asp-route-blogShortName="@Model.BlogShortName" class="btn btn-primary btn-rounded">@L["ContinueReading"]</a>
</div>
<div class="article-owner">
<div class="article-infos">
<div class="user-card">
<a>
<img src="https://placeimg.com/120/120/people" class="article-avatar">
</a>
<a>
<strong>Armağan Ünlü</strong>, @ConvertDatetimeToTimeAgo(post.CreationTime)
</a>
<span class="seperator">|</span>
<a>
<i class="fa fa-eye"></i> @post.ReadCount @L["Read"]
</a>
<span class="seperator">|</span>
<a>
<i class="fa fa-comment"></i> @post.CommentCount @L["Comment"]
</a>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
} </div>
</div>
}
</section> </section>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<h2>@L["WhatIsNew"]</h2>
<div class="list-group"> <div class="list-group">
@for (var index = 0; index < Model.Posts.Count && index < 3; index++) @for (var index = 0; index < Model.Posts.Count && index < 3; index++)
{ {
var post = Model.Posts[index]; var post = Model.Posts[index];
@ -104,14 +128,14 @@
</div> </div>
</div> </div>
<div class="col-8 pl-0"> <div class="col-8 pl-0">
<h3> <h3 class="font-125">
<a asp-page="./Detail" asp-route-postUrl="@post.Url" asp-route-blogShortName="@Model.BlogShortName">@post.Title</a> <a asp-page="./Detail" asp-route-postUrl="@post.Url" asp-route-blogShortName="@Model.BlogShortName">@post.Title</a>
</h3> </h3>
<div class="article-owner"> <div class="article-owner">
<div class="article-infos"> <div class="article-infos">
<div class="user-card"> <div class="user-card">
<a asp-page="./Detail" asp-route-postUrl="@post.Url" asp-route-blogShortName="@Model.BlogShortName"> <a asp-page="./Detail" asp-route-postUrl="@post.Url" asp-route-blogShortName="@Model.BlogShortName">
<strong>Armağan Ünlü</strong>, @ConvertDatetimeToTimeAgo(post.CreationTime) <strong>@post.Writer.UserName</strong>, @ConvertDatetimeToTimeAgo(post.CreationTime)
</a> </a>
</div> </div>
</div> </div>
@ -122,116 +146,99 @@
</div> </div>
} }
</div> </div>
@if (await Authorization.IsGrantedAsync(BloggingPermissions.Posts.Create))
{
<a asp-page="./New" asp-route-blogShortName="@Model.BlogShortName">@L["CreateANewPost"]</a>
}
</div> </div>
</div> </div>
</div> </div>
<div class="container last-post-section pb-5"> <div class="pb-5">
<div class="row"> <div class="row">
<div class="col-md-8"> <div class="col-md-8 box-articles">
<div class="row"> <div class="vs-blog-title">
<div class="col"> <h2>@L["LastPosts"]</h2>
<div class="titline">
<h2>@L["LastPosts"]</h2>
</div>
</div>
</div> </div>
<div class="list-group">
@for (var index = 3; index < Model.Posts.Count; index++)
{
var post = Model.Posts[index];
var oddPost = index % 2 == 1;
@for (var index = 3; index < Model.Posts.Count; index++) <div class="list-group-item">
{ <div class="row @(oddPost?"align-middle":"")">
var post = Model.Posts[index]; <div class="col-md-4 order-md-@(oddPost?"last":"first")">
var oddPost = index % 2 == 1; <div class="img-container">
<a asp-page="./Detail" asp-route-postUrl="@post.Url" asp-route-blogShortName="@Model.BlogShortName">
<section class="box-articles"> <img src="@post.CoverImage" class="box-article-img">
<div class="row @(oddPost?"align-middle":"")"> </a>
<div class="col-md-4 order-md-@(oddPost?"last":"first")"> </div>
<div class="img-container">
<a asp-page="./Detail" asp-route-postUrl="@post.Url" asp-route-blogShortName="@Model.BlogShortName">
<img src="@post.CoverImage" class="box-article-img">
</a>
</div> </div>
</div> <div class="col order-md-@(oddPost?"first":"last")">
<div class="col-md-8 order-md-@(oddPost?"first":"last")"> <p class="tags">
<p class="tags"> @foreach (var tag in post.Tags)
@foreach (var tag in post.Tags) {
{ <a asp-page="/Blog/Posts/Index" asp-route-blogShortName="@Model.BlogShortName" asp-route-tagName="@tag.Name" class="tag">@tag.Name</a>
<a asp-page="/Blog/Posts/Index" asp-route-blogShortName="@Model.BlogShortName" asp-route-tagName="@tag.Name" class="tag">@tag.Name</a> }
} </p>
</p> <h3>
<h3> <a asp-page="./Detail" asp-route-postUrl="@post.Url" asp-route-blogShortName="@Model.BlogShortName">@post.Title</a>
<a asp-page="./Detail" asp-route-postUrl="@post.Url" asp-route-blogShortName="@Model.BlogShortName">@post.Title</a> </h3>
</h3> <p>
<p> @(GetShortContent(post.Content))
@(post.Content == null ? "" : (post.Content.Length > 250 ? post.Content.Substring(0, 150) : post.Content))... </p>
</p> <div class="article-owner">
<div class="article-owner"> <div class="article-infos">
<div class="article-infos"> <div class="user-card">
<div class="user-card"> <a href="#">
<a href="#"> <img src="https://placeimg.com/120/120/pople" class="article-avatar">
<img src="https://placeimg.com/120/120/pople" class="article-avatar"> </a>
</a> <a>
<a> <strong>@post.Writer.UserName</strong>, @ConvertDatetimeToTimeAgo(post.CreationTime)
<strong>Armağan Ünlü</strong>, @ConvertDatetimeToTimeAgo(post.CreationTime) </a>
</a> <span class="vs-seperator">|</span>
<span class="seperator">|</span> <a>
<a> <i class="fa fa-eye"></i> @post.ReadCount
<i class="fa fa-eye"></i> @post.ReadCount </a>
</a> <span class="vs-seperator">|</span>
<span class="seperator">|</span> <a>
<a> <i class="fa fa-comment"></i> @post.CommentCount
<i class="fa fa-comment"></i> @post.CommentCount </a>
</a> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</section> }
} </div>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<div class="pb-5 sidebox" id="sidebar"> <div class="vs-blog-title">
<h2>@L["PopularTags"]</h2>
<div class="titline"> </div>
<h2>@L["PopularTags"]</h2> <div class="list-group small-list popular-tags">
</div> @foreach (var popularTag in Model.PopularTags)
<div class="sideboxes"> {
<div class="popular-tags"> <div class="list-group-item">
@foreach (var popularTag in Model.PopularTags) <a asp-page="/Blog/Posts/Index" asp-route-blogShortName="@Model.BlogShortName" asp-route-tagName="@popularTag.Name">@popularTag.Name <span>(@popularTag.UsageCount @L["Posts"])</span></a>
{
<a asp-page="/Blog/Posts/Index" asp-route-blogShortName="@Model.BlogShortName" asp-route-tagName="@popularTag.Name">@popularTag.Name <span>(@popularTag.UsageCount @L["Posts"])</span></a>
}
</div> </div>
</div> }
<hr>
<footer>
<a href="#">About</a>
<span class="seperator">|</span>
<a href="#">Privacy Policy</a>
<span class="seperator">|</span>
<a href="#">Terms</a>
<p> </div>
Copyright Volosoft - Blog
</p>
</footer>
<div class="vs-footer">
<a href="#">About</a>
<span class="vs-seperator">|</span>
<a href="#">Privacy Policy</a>
<span class="vs-seperator">|</span>
<a href="#">Terms</a>
<p>
&copy; Copyright Volosoft 2018 - Blog
</p>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div>
</div>

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

@ -3,6 +3,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.RazorPages;
using Volo.Blogging.Blogs; using Volo.Blogging.Blogs;
using Volo.Blogging.Blogs.Dtos;
using Volo.Blogging.Posts; using Volo.Blogging.Posts;
using Volo.Blogging.Tagging; using Volo.Blogging.Tagging;
using Volo.Blogging.Tagging.Dtos; using Volo.Blogging.Tagging.Dtos;
@ -21,6 +22,8 @@ namespace Volo.Blogging.Pages.Blog.Posts
[BindProperty(SupportsGet = true)] [BindProperty(SupportsGet = true)]
public string TagName { get; set; } public string TagName { get; set; }
public BlogDto Blog { get; set; }
public IReadOnlyList<PostWithDetailsDto> Posts { get; set; } public IReadOnlyList<PostWithDetailsDto> Posts { get; set; }
public IReadOnlyList<TagDto> PopularTags { get; set; } public IReadOnlyList<TagDto> PopularTags { get; set; }
@ -34,8 +37,8 @@ namespace Volo.Blogging.Pages.Blog.Posts
public async Task OnGetAsync() public async Task OnGetAsync()
{ {
var blog = await _blogAppService.GetByShortNameAsync(BlogShortName); Blog = await _blogAppService.GetByShortNameAsync(BlogShortName);
Posts = (await _postAppService.GetListByBlogIdAndTagName(blog.Id, TagName)).Items; Posts = (await _postAppService.GetListByBlogIdAndTagName(Blog.Id, TagName)).Items;
PopularTags = (await _tagAppService.GetPopularTags(new GetPopularTagsInput {ResultCount = 10})); PopularTags = (await _tagAppService.GetPopularTags(new GetPopularTagsInput {ResultCount = 10}));
} }
} }

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

@ -4,6 +4,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
using Volo.Blogging.Blogs; using Volo.Blogging.Blogs;
using Volo.Blogging.Blogs.Dtos;
using Volo.Blogging.Posts; using Volo.Blogging.Posts;
namespace Volo.Blogging.Pages.Blog.Posts namespace Volo.Blogging.Pages.Blog.Posts

9
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/detail.js

@ -1,5 +1,7 @@
(function ($) { (function ($) {
var l = abp.localization.getResource('Blogging');
$('div .replyForm').hide(); $('div .replyForm').hide();
$('div .editForm').hide(); $('div .editForm').hide();
@ -25,6 +27,7 @@
$('.replyLink').click(function (event) { $('.replyLink').click(function (event) {
event.preventDefault(); event.preventDefault();
$('div .editForm').hide();
var linkElement = $(this); var linkElement = $(this);
var replyCommentId = linkElement.attr('data-relpyid'); var replyCommentId = linkElement.attr('data-relpyid');
@ -48,8 +51,8 @@
if (deleteCommentId != '' && deleteCommentId !== undefined) { if (deleteCommentId != '' && deleteCommentId !== undefined) {
abp.message.confirm( abp.message.confirm(
'Comment will be deleted.', // TODO: localize l('CommentDeletionWarningMessage'), // TODO: localize
'Are you sure?', l('Are you sure?'),
function(isConfirmed) { function(isConfirmed) {
if (isConfirmed) { if (isConfirmed) {
$.ajax({ $.ajax({
@ -68,6 +71,8 @@
$('.updateLink').click(function (event) { $('.updateLink').click(function (event) {
event.preventDefault(); event.preventDefault();
$('div .replyForm').hide();
var linkElement = $(this); var linkElement = $(this);
var updateCommentId = $(this).attr('data-updateid'); var updateCommentId = $(this).attr('data-updateid');

9
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/new.css.map

@ -0,0 +1,9 @@
{
"version": 3,
"file": "new.css",
"sources": [
"new.scss"
],
"mappings": "AAAA,AACI,sBADkB,CAClB,gBAAgB,AAAC,CACb,gBAAgB,CAAE,OAAQ,CAC1B,UAAU,CAAE,MAAO,CAKtB,AARL,AAKQ,sBALc,CAClB,gBAAgB,CAIZ,kBAAkB,AAAC,CACf,UAAU,CAAE,IAAK,CACpB,AAPT,AAUI,sBAVkB,CAUlB,KAAK,AAAC,CACF,gBAAgB,CAAE,OAAQ,CAC7B,AAZL,AAcI,sBAdkB,CAclB,cAAc,AAAC,CACX,UAAU,CAAE,MAAO,CACtB",
"names": []
}

3
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/new.min.css

@ -0,0 +1,3 @@
#qa-new-post-container .new-post-editor{background-color:#F9F9F9;min-height:19.5em}#qa-new-post-container .new-post-editor .CodeMirror-scroll{min-height:20em}#qa-new-post-container .card{background-color:#f6f8fa}#qa-new-post-container .loading-cover{min-height:19.5em}
/*# sourceMappingURL=new.css.map */

2
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Shared/Scripts/blog.js

@ -28,7 +28,7 @@
autoplay: true, autoplay: true,
autoHeight: true, autoHeight: true,
autoplaySpeed: 1000, autoplaySpeed: 1000,
items: 1 items: 1,
}); });
$('.card-article-container .owl-carousel').owlCarousel({ $('.card-article-container .owl-carousel').owlCarousel({
loop: true, loop: true,

169
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Shared/Styles/_bootstrap-overwrite.scss

@ -1,4 +1,3 @@
a, a:hover { a, a:hover {
text-decoration: none; text-decoration: none;
color: #000000; color: #000000;
@ -12,15 +11,15 @@ h1,h2,h3,h4,h5,h6,.tab-title {
font-weight: 700; font-weight: 700;
} }
h1 { h1 {
font-size: 2.0em; font-size: 2.25em;
margin: 1.5rem 0 .75rem; margin: 1.5rem 0 .75rem;
} }
h2, .tab-title { h2, .tab-title {
font-size: 1.5em; font-size: 1.75em;
margin: 1.5rem 0 .75rem; margin: 1.5rem 0 .75rem;
} }
h3 { h3 {
font-size: 1.25em; font-size: 1.5em;
margin: 1.5rem 0 .75rem; margin: 1.5rem 0 .75rem;
} }
h4 { h4 {
@ -48,155 +47,57 @@ input, select, textarea, .form-control , .btn {
} }
.no-border { .no-border {
border: 0; border: 0;
} }
/* Owl overwrite */
.owl-theme .owl-nav {
margin-top: 0 !important;
text-align: center;
-webkit-tap-highlight-color: transparent;
position: absolute;
bottom: 120px;
left: 0;
right: 0;
font-size: 3em;
color: white;
> button {
width: 40px;
height: 40px;
padding: 0 !important;
display: inline-block !important;
line-height: 27px !important;
text-align: center;
background: none !important;
transition: .3s;
&.owl-next {
position: absolute;
right: 10%;
}
&.owl-prev {
position: absolute;
left: 10%;
}
> span {
width: 40px;
height: 40px;
padding: 0;
border: 2px solid white;
border-radius: 40px;
display: inline-block;
line-height: 29px;
text-align: center;
}
}
}
.btn-rounded { .btn-rounded {
border-radius: 30px; border-radius: 30px;
} }
.list-group {
.list-group {
.list-group-item { .list-group-item {
position: relative; position: relative;
display: block; display: block;
padding: 21px 0 21px 0; padding: 0 0 20px 0;
margin-bottom: -1px; background: none;
background-color: #fff; border-radius: 0;
font-size: .8em;
border: 0; border: 0;
border: 1px solid #f5f5f5;
border-width: 1px 0;
&:hover { &:hover {
background: none; background: none;
} }
&+.list-group-item {
margin-top: -1px; & + .list-group-item {
border-top: 1px solid #f5f5f5;
padding: 20px 0;
} }
h3 { h3 {
margin-top: 0; margin-top: 0;
} }
} }
}
.tab-content {
padding: 30px 0px 0;
}
.container { &.small-list {
padding-right: 20px; .list-group-item {
padding-left: 20px; padding: 10px 0;
} & + .list-group-item {
.row { padding: 10px 0;
margin-right: -20px; }
margin-left: -20px; }
}
.col, .col-1, .col-10, .col-11, .col-12, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-auto, .col-lg, .col-lg-1, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-auto, .col-md, .col-md-1, .col-md-10, .col-md-11, .col-md-12, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-auto, .col-sm, .col-sm-1, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-auto, .col-xl, .col-xl-1, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-auto {
padding-right: 20px;
padding-left: 20px;
}
@media (min-width: 1200px) {
.container {
max-width: 1080px;
} }
}
.font-75 {
font-size: .75em;
} }
/* .font-85 {
.hero-container {
width: 96%;
max-width: 1440px;
}
*/
.f-85 {
font-size: .85em; font-size: .85em;
} }
.font-92 {
.nav-tabs { font-size: .92em;
margin-bottom: 20px;
border-bottom: 3px solid #000;
.nav-item {
margin-bottom: -16px;
background: white;
border-left: 5px solid #fff;
.tab-title {
font-weight: 700;
line-height: 1.1;
padding: 7px;
margin-left: -14px;
}
}
.nav-link {
font-size: .8em;
border-radius: 50px;
padding: .25rem .5rem;
font-weight: 700;
color: grey;
&:hover, &:focus {
background:#eee;
}
}
.nav-link.active,
.nav-item.show .nav-link {
background: black;
border-color: white;
color: white;
border-radius: 50px;
}
}
.pb-5, .py-5 {
padding-bottom: 4rem!important;
} }
.font-100 {
.pt-5, .py-5 { font-size: 1em;
padding-top: 4rem!important;
} }
footer { .font-125 {
font-size: .8em; font-size: 1.25em;
line-height: 2; }
.seperator {
padding: 0 5px;
opacity: .5;
}
}

40
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Shared/Styles/_header.scss

@ -1,19 +1,21 @@
header { .vs-blog-title {
position: fixed; padding-bottom: 15px;
width: 100%; margin-bottom: 25px;
top: 0; border-bottom: 1px solid #ddd;
left: 0; h1,h2,h3,h4,h5, h6 {
z-index: 100; margin: 0;
background:#f8f9fa; padding: 0;
.navbar-blog { }
padding: 20px 0; }
ul {
li { .vs-footer {
a { padding-top: 15px;
color: black; margin-top: 25px;
} border-top: 1px solid #ddd;
} }
}
} .vs-seperator {
} padding: 0 4px;
opacity: .3;
}

140
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Shared/Styles/_home.css

@ -0,0 +1,140 @@
.hero-section {
padding: 0; }
.hero-section .hero-articles {
position: relative;
overflow: hidden; }
.hero-section .hero-articles .hero-content {
position: absolute;
left: 12%;
right: 12%;
bottom: 80px;
z-index: 4;
text-align: center; }
.hero-section .hero-articles .hero-content h2 {
margin-top: 0;
font-size: 2.5em;
font-weight: bold; }
.hero-section .hero-articles .hero-content a {
color: white;
text-shadow: 0px 0px 20px rgba(0, 0, 0, 0.5); }
.hero-section .hero-articles .hero-content p {
color: white; }
.hero-section .hero-articles .tags .tag {
background: rgba(208, 208, 208, 0.3);
color: #fff !important; }
.hero-section .hero-articles .tags .tag:hover {
background: white;
color: black !important; }
.hero-section .hero-articles .article-owner {
text-align: center;
position: relative;
z-index: 12; }
.hero-section .hero-articles .article-owner .article-infos {
color: black; }
.hero-section .hero-articles .article-owner .article-infos a {
color: black; }
.hero-section .hero-articles .article-owner .article-infos .seperator {
margin: 0 4px;
color: rgba(255, 255, 255, 0.2); }
.hero-section .hero-articles .article-owner .article-infos img.article-avatar {
width: 64px;
margin: -25px 10px 0px 0;
border: 3px solid #fff;
display: inline-block;
border-radius: 50%; }
.hero-section .hero-articles .img-container {
background: black; }
.hero-section .hero-articles .img-container img {
filter: grayscale(10%); }
.hero-section .hero-articles .img-container::after {
content: "";
display: block;
position: absolute;
z-index: 1;
bottom: 0px;
width: 100%;
top: 1% !important;
background: linear-gradient(to bottom, transparent 0, rgba(0, 0, 0, 0.91) 89%, rgba(0, 0, 0, 0.93) 93%) !important;
transition: .2s all ease-in-out;
opacity: .9; }
.hero-section .hero-articles:hover .img-container::after {
opacity: 1; }
.article-owner .article-infos {
color: #000; }
.article-owner .article-infos a {
color: rgba(0, 0, 0, 0.6); }
.article-owner .article-infos .seperator {
margin: 0 4px;
color: rgba(0, 0, 0, 0.2); }
.article-owner .article-infos img.article-avatar {
width: 30px;
margin: -1px 4px 0 0;
display: inline-block;
border-radius: 50%; }
.card-articles .card-content {
padding: 10px 0px 10px; }
.card-articles .card-content h3 {
margin: 10px 0; }
.card-articles .card-content h3 a {
font-weight: 700; }
.card-articles .article-owner {
text-align: left; }
.card-articles .article-owner .article-infos {
color: #000; }
.card-articles .article-owner .article-infos a {
color: rgba(0, 0, 0, 0.6); }
.card-articles .article-owner .article-infos .seperator {
margin: 0 4px;
color: rgba(0, 0, 0, 0.2); }
.card-articles .article-owner .article-infos img.article-avatar {
width: 30px;
margin: -1px 4px 0 0;
display: inline-block;
border-radius: 50%; }
.article-owner {
font-size: .85em; }
.user-link-icons {
position: absolute;
right: 18px;
top: 15px;
z-index: 3; }
.user-link-icons a {
display: inline-block;
color: #eee;
margin-left: 12px;
font-size: 1.25em; }
.user-link-icons a:hover {
color: #fff; }
.tags .tag {
display: inline-block;
padding: 2px 8px;
background: rgba(208, 208, 208, 0.3);
border-radius: 30px;
margin: 0 1px 3px 0;
color: #b1b1b1 !important;
font-size: .7em;
line-height: 1.6em;
text-transform: uppercase; }
.tags .tag:hover {
background: black;
color: white !important; }
.popular-tags a {
display: block;
font-size: .9em; }
.popular-tags a span {
float: right;
opacity: .3;
font-size: .9em; }
.img-container {
position: relative;
overflow: hidden;
border-radius: 4px; }

1
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Shared/Styles/_home.min.css

@ -0,0 +1 @@
.hero-section{padding:0;}.hero-section .hero-articles{position:relative;overflow:hidden;}.hero-section .hero-articles .hero-content{position:absolute;left:12%;right:12%;bottom:80px;z-index:4;text-align:center;}.hero-section .hero-articles .hero-content h2{margin-top:0;font-size:2.5em;font-weight:bold;}.hero-section .hero-articles .hero-content a{color:#fff;text-shadow:0 0 20px rgba(0,0,0,.5);}.hero-section .hero-articles .hero-content p{color:#fff;}.hero-section .hero-articles .tags .tag{background:rgba(208,208,208,.3);color:#fff !important;}.hero-section .hero-articles .tags .tag:hover{background:#fff;color:#000 !important;}.hero-section .hero-articles .article-owner{text-align:center;position:relative;z-index:12;}.hero-section .hero-articles .article-owner .article-infos{color:#000;}.hero-section .hero-articles .article-owner .article-infos a{color:#000;}.hero-section .hero-articles .article-owner .article-infos .seperator{margin:0 4px;color:rgba(255,255,255,.2);}.hero-section .hero-articles .article-owner .article-infos img.article-avatar{width:64px;margin:-25px 10px 0 0;border:3px solid #fff;display:inline-block;border-radius:50%;}.hero-section .hero-articles .img-container{background:#000;}.hero-section .hero-articles .img-container img{filter:grayscale(10%);}.hero-section .hero-articles .img-container::after{content:"";display:block;position:absolute;z-index:1;bottom:0;width:100%;top:1% !important;background:linear-gradient(to bottom,transparent 0,rgba(0,0,0,.91) 89%,rgba(0,0,0,.93) 93%) !important;transition:.2s all ease-in-out;opacity:.9;}.hero-section .hero-articles:hover .img-container::after{opacity:1;}.article-owner .article-infos{color:#000;}.article-owner .article-infos a{color:rgba(0,0,0,.6);}.article-owner .article-infos .seperator{margin:0 4px;color:rgba(0,0,0,.2);}.article-owner .article-infos img.article-avatar{width:30px;margin:-1px 4px 0 0;display:inline-block;border-radius:50%;}.card-articles .card-content{padding:10px 0 10px;}.card-articles .card-content h3{margin:10px 0;}.card-articles .card-content h3 a{font-weight:700;}.card-articles .article-owner{text-align:left;}.card-articles .article-owner .article-infos{color:#000;}.card-articles .article-owner .article-infos a{color:rgba(0,0,0,.6);}.card-articles .article-owner .article-infos .seperator{margin:0 4px;color:rgba(0,0,0,.2);}.card-articles .article-owner .article-infos img.article-avatar{width:30px;margin:-1px 4px 0 0;display:inline-block;border-radius:50%;}.article-owner{font-size:.85em;}.user-link-icons{position:absolute;right:18px;top:15px;z-index:3;}.user-link-icons a{display:inline-block;color:#eee;margin-left:12px;font-size:1.25em;}.user-link-icons a:hover{color:#fff;}.tags .tag{display:inline-block;padding:2px 8px;background:rgba(208,208,208,.3);border-radius:30px;margin:0 1px 3px 0;color:#b1b1b1 !important;font-size:.7em;line-height:1.6em;text-transform:uppercase;}.tags .tag:hover{background:#000;color:#fff !important;}.popular-tags a{display:block;font-size:.9em;}.popular-tags a span{float:right;opacity:.3;font-size:.9em;}.img-container{position:relative;overflow:hidden;border-radius:4px;}

88
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Shared/Styles/_home.scss

@ -1,4 +1,4 @@
section.hero-section { .hero-section {
padding: 0; padding: 0;
.hero-articles { .hero-articles {
position: relative; position: relative;
@ -57,9 +57,7 @@ section.hero-section {
} }
} }
.img-container { .img-container {
background: black; background: black;
border-top-left-radius: 0;
border-top-right-radius: 0;
img { img {
filter: grayscale(10%); filter: grayscale(10%);
} }
@ -85,39 +83,26 @@ section.hero-section {
} }
} }
} }
.box-articles {
padding: 0 0 40px 0; .article-owner {
&+.box-articles { .article-infos {
padding: 40px 0; color: #000;
border-top: 1px solid #f5f5f5;
border-bottom: 1px solid #f5f5f5;
margin-top: -1px;
}
h3 {
margin: 10px 0;
a { a {
font-weight: 700; color: rgba(0, 0, 0, 0.6);
} }
} .seperator {
.article-owner { margin: 0 4px;
.article-infos { color: rgba(0, 0, 0, 0.2);
color: #000;
a {
color: rgba(0, 0, 0, 0.6);
}
.seperator {
margin: 0 4px;
color: rgba(0, 0, 0, 0.2);
}
img.article-avatar {
width: 30px;
margin: -1px 4px 0 0;
display: inline-block;
border-radius: 50%;
}
} }
} img.article-avatar {
} width: 30px;
margin: -1px 4px 0 0;
display: inline-block;
border-radius: 50%;
}
}
}
.card-articles { .card-articles {
.card-content .card-content
@ -172,37 +157,32 @@ section.hero-section {
} }
.tags { .tags {
margin:5px 0;
.tag { .tag {
display: inline-block; display: inline-block;
padding: 0px 6px; padding: 2px 8px;
background: rgba(208, 208, 208, 0.3); background: rgba(208, 208, 208, 0.3);
border-radius: 30px; border-radius: 30px;
margin: 4px 1px; margin: 0 1px 3px 0;
color: #b1b1b1 !important; color: #b1b1b1 !important;
font-size: .64em; font-size: .7em;
line-height: 1.6em; line-height: 1.6em;
text-transform: uppercase; text-transform: uppercase;
&:hover {
background: black; &:hover {
background: black;
color: white !important; color: white !important;
} }
} }
} }
.popular-tags { .popular-tags {
a { a {
display: block; display: block;
padding: 10px 15px; font-size: .9em;
background: white;
margin-bottom: 2px;
font-size: .85em;
&:hover {
background: #f5f5f5;
}
span { span {
float: right; float: right;
opacity: .3; opacity: .3;
font-size: .85em; font-size: .9em;
} }
} }
} }
@ -213,10 +193,4 @@ section.hero-section {
overflow: hidden; overflow: hidden;
border-radius: 4px; border-radius: 4px;
} }
.sidebox {
.sideboxes {
padding: 30px;
background: #fafafa;
}
}

30
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Shared/Styles/_post.scss

@ -52,32 +52,36 @@
.media { .media {
font-size: .95em; font-size: .95em;
} }
} }
.comment-area { .comment-area {
.comment-owner { .comment-owner {
margin-bottom: 2px;
span { span {
font-weight: 300; font-weight: 300;
color: #999; color: #999;
font-size: .8em; font-size: .8em;
} }
} }
>.media {
padding: 30px 0; > .media {
padding: 30px 0;
border-bottom: 1px solid #f1f1f1; border-bottom: 1px solid #f1f1f1;
.media { .media {
padding: 20px 0; padding: 20px 0;
} }
} }
.comment-buttons { .comment-buttons {
background: #f8f9fa ; padding: 4px 8px;
border-bottom: 1px solid #f1f1f1;
padding: 6px 10px;
font-size: .85em;
.seperator { .seperator {
color: #ddd; color: #ddd;
margin: 0 8px; margin: 0 8px;
} }
.count { .count {
color: #fff; color: #fff;
background: #ddd; background: #ddd;
@ -85,17 +89,27 @@
padding: 1px 3px; padding: 1px 3px;
font-size: 10px; font-size: 10px;
border-radius: 3px; border-radius: 3px;
&.count-up { &.count-up {
background: #999; background: #999;
} }
} }
a {
opacity: .5;
&:hover {
opacity: .75;
}
}
} }
p { p {
margin-bottom: 6px; margin-bottom: 6px;
} }
.comment-avatar { .comment-avatar {
width: 64px; width: 64px;
} }
.answer-avatar { .answer-avatar {
width: 48px; width: 48px;
} }

289
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Shared/Styles/blog.css

@ -1,15 +1,9 @@
div.vs-blog { div.vs-blog {
position: relative; position: relative;
background: #fff; background: white;
font-family: Helvetica, sans-serif; padding: 0 30px;
font-size: 15px; font-family: 'Open Sans', Helvetica, Arial, sans-serif;
/* Owl overwrite */ font-size: 14px; }
/*
.hero-container {
width: 96%;
max-width: 1440px;
}
*/ }
div.vs-blog a, div.vs-blog a:hover { div.vs-blog a, div.vs-blog a:hover {
text-decoration: none; text-decoration: none;
color: #000000; } color: #000000; }
@ -20,13 +14,13 @@
font-family: Helvetica, Roboto, sans-serif; font-family: Helvetica, Roboto, sans-serif;
font-weight: 700; } font-weight: 700; }
div.vs-blog h1 { div.vs-blog h1 {
font-size: 2.0em; font-size: 2.25em;
margin: 1.5rem 0 .75rem; } margin: 1.5rem 0 .75rem; }
div.vs-blog h2, div.vs-blog .tab-title { div.vs-blog h2, div.vs-blog .tab-title {
font-size: 1.5em; font-size: 1.75em;
margin: 1.5rem 0 .75rem; } margin: 1.5rem 0 .75rem; }
div.vs-blog h3 { div.vs-blog h3 {
font-size: 1.25em; font-size: 1.5em;
margin: 1.5rem 0 .75rem; } margin: 1.5rem 0 .75rem; }
div.vs-blog h4 { div.vs-blog h4 {
font-size: 1.25em; font-size: 1.25em;
@ -46,172 +40,99 @@
background: aqua; } background: aqua; }
div.vs-blog .no-border { div.vs-blog .no-border {
border: 0; } border: 0; }
div.vs-blog .owl-theme .owl-nav {
margin-top: 0 !important;
text-align: center;
-webkit-tap-highlight-color: transparent;
position: absolute;
bottom: 120px;
left: 0;
right: 0;
font-size: 3em;
color: white; }
div.vs-blog .owl-theme .owl-nav > button {
width: 40px;
height: 40px;
padding: 0 !important;
display: inline-block !important;
line-height: 27px !important;
text-align: center;
background: none !important;
transition: .3s; }
div.vs-blog .owl-theme .owl-nav > button.owl-next {
position: absolute;
right: 10%; }
div.vs-blog .owl-theme .owl-nav > button.owl-prev {
position: absolute;
left: 10%; }
div.vs-blog .owl-theme .owl-nav > button > span {
width: 40px;
height: 40px;
padding: 0;
border: 2px solid white;
border-radius: 40px;
display: inline-block;
line-height: 29px;
text-align: center; }
div.vs-blog .btn-rounded { div.vs-blog .btn-rounded {
border-radius: 30px; } border-radius: 30px; }
div.vs-blog .list-group .list-group-item { div.vs-blog .list-group .list-group-item {
position: relative; position: relative;
display: block; display: block;
padding: 21px 0 21px 0; padding: 0 0 20px 0;
margin-bottom: -1px; background: none;
background-color: #fff; border-radius: 0;
font-size: .8em; border: 0; }
border: 0;
border: 1px solid #f5f5f5;
border-width: 1px 0; }
div.vs-blog .list-group .list-group-item:hover { div.vs-blog .list-group .list-group-item:hover {
background: none; } background: none; }
div.vs-blog .list-group .list-group-item + .list-group-item { div.vs-blog .list-group .list-group-item + .list-group-item {
margin-top: -1px; } border-top: 1px solid #f5f5f5;
padding: 20px 0; }
div.vs-blog .list-group .list-group-item h3 { div.vs-blog .list-group .list-group-item h3 {
margin-top: 0; } margin-top: 0; }
div.vs-blog .tab-content { div.vs-blog .list-group.small-list .list-group-item {
padding: 30px 0px 0; } padding: 10px 0; }
div.vs-blog .container { div.vs-blog .list-group.small-list .list-group-item + .list-group-item {
padding-right: 20px; padding: 10px 0; }
padding-left: 20px; } div.vs-blog .font-75 {
div.vs-blog .row { font-size: .75em; }
margin-right: -20px; div.vs-blog .font-85 {
margin-left: -20px; }
div.vs-blog .col, div.vs-blog .col-1, div.vs-blog .col-10, div.vs-blog .col-11, div.vs-blog .col-12, div.vs-blog .col-2, div.vs-blog .col-3, div.vs-blog .col-4, div.vs-blog .col-5, div.vs-blog .col-6, div.vs-blog .col-7, div.vs-blog .col-8, div.vs-blog .col-9, div.vs-blog .col-auto, div.vs-blog .col-lg, div.vs-blog .col-lg-1, div.vs-blog .col-lg-10, div.vs-blog .col-lg-11, div.vs-blog .col-lg-12, div.vs-blog .col-lg-2, div.vs-blog .col-lg-3, div.vs-blog .col-lg-4, div.vs-blog .col-lg-5, div.vs-blog .col-lg-6, div.vs-blog .col-lg-7, div.vs-blog .col-lg-8, div.vs-blog .col-lg-9, div.vs-blog .col-lg-auto, div.vs-blog .col-md, div.vs-blog .col-md-1, div.vs-blog .col-md-10, div.vs-blog .col-md-11, div.vs-blog .col-md-12, div.vs-blog .col-md-2, div.vs-blog .col-md-3, div.vs-blog .col-md-4, div.vs-blog .col-md-5, div.vs-blog .col-md-6, div.vs-blog .col-md-7, div.vs-blog .col-md-8, div.vs-blog .col-md-9, div.vs-blog .col-md-auto, div.vs-blog .col-sm, div.vs-blog .col-sm-1, div.vs-blog .col-sm-10, div.vs-blog .col-sm-11, div.vs-blog .col-sm-12, div.vs-blog .col-sm-2, div.vs-blog .col-sm-3, div.vs-blog .col-sm-4, div.vs-blog .col-sm-5, div.vs-blog .col-sm-6, div.vs-blog .col-sm-7, div.vs-blog .col-sm-8, div.vs-blog .col-sm-9, div.vs-blog .col-sm-auto, div.vs-blog .col-xl, div.vs-blog .col-xl-1, div.vs-blog .col-xl-10, div.vs-blog .col-xl-11, div.vs-blog .col-xl-12, div.vs-blog .col-xl-2, div.vs-blog .col-xl-3, div.vs-blog .col-xl-4, div.vs-blog .col-xl-5, div.vs-blog .col-xl-6, div.vs-blog .col-xl-7, div.vs-blog .col-xl-8, div.vs-blog .col-xl-9, div.vs-blog .col-xl-auto {
padding-right: 20px;
padding-left: 20px; }
@media (min-width: 1200px) {
div.vs-blog .container {
max-width: 1080px; } }
div.vs-blog .f-85 {
font-size: .85em; } font-size: .85em; }
div.vs-blog .nav-tabs { div.vs-blog .font-92 {
margin-bottom: 20px; font-size: .92em; }
border-bottom: 3px solid #000; } div.vs-blog .font-100 {
div.vs-blog .nav-tabs .nav-item { font-size: 1em; }
margin-bottom: -16px; div.vs-blog .font-125 {
background: white; font-size: 1.25em; }
border-left: 5px solid #fff; } div.vs-blog .vs-blog-title {
div.vs-blog .nav-tabs .nav-item .tab-title { padding-bottom: 15px;
font-weight: 700; margin-bottom: 25px;
line-height: 1.1; border-bottom: 1px solid #ddd; }
padding: 7px; div.vs-blog .vs-blog-title h1, div.vs-blog .vs-blog-title h2, div.vs-blog .vs-blog-title h3, div.vs-blog .vs-blog-title h4, div.vs-blog .vs-blog-title h5, div.vs-blog .vs-blog-title h6 {
margin-left: -14px; } margin: 0;
div.vs-blog .nav-tabs .nav-link { padding: 0; }
font-size: .8em; div.vs-blog .vs-footer {
border-radius: 50px; padding-top: 15px;
padding: .25rem .5rem; margin-top: 25px;
font-weight: 700; border-top: 1px solid #ddd; }
color: grey; } div.vs-blog .vs-seperator {
div.vs-blog .nav-tabs .nav-link:hover, div.vs-blog .nav-tabs .nav-link:focus { padding: 0 4px;
background: #eee; } opacity: .3; }
div.vs-blog .nav-tabs .nav-link.active, div.vs-blog .hero-section {
div.vs-blog .nav-tabs .nav-item.show .nav-link {
background: black;
border-color: white;
color: white;
border-radius: 50px; }
div.vs-blog .pb-5, div.vs-blog .py-5 {
padding-bottom: 4rem !important; }
div.vs-blog .pt-5, div.vs-blog .py-5 {
padding-top: 4rem !important; }
div.vs-blog footer {
font-size: .8em;
line-height: 2; }
div.vs-blog footer .seperator {
padding: 0 5px;
opacity: .5; }
div.vs-blog header {
position: fixed;
width: 100%;
top: 0;
left: 0;
z-index: 100;
background: #f8f9fa; }
div.vs-blog header .navbar-blog {
padding: 20px 0; }
div.vs-blog header .navbar-blog ul li a {
color: black; }
div.vs-blog section.hero-section {
padding: 0; } padding: 0; }
div.vs-blog section.hero-section .hero-articles { div.vs-blog .hero-section .hero-articles {
position: relative; position: relative;
overflow: hidden; } overflow: hidden; }
div.vs-blog section.hero-section .hero-articles .hero-content { div.vs-blog .hero-section .hero-articles .hero-content {
position: absolute; position: absolute;
left: 12%; left: 12%;
right: 12%; right: 12%;
bottom: 80px; bottom: 80px;
z-index: 4; z-index: 4;
text-align: center; } text-align: center; }
div.vs-blog section.hero-section .hero-articles .hero-content h2 { div.vs-blog .hero-section .hero-articles .hero-content h2 {
margin-top: 0; margin-top: 0;
font-size: 2.5em; font-size: 2.5em;
font-weight: bold; } font-weight: bold; }
div.vs-blog section.hero-section .hero-articles .hero-content a { div.vs-blog .hero-section .hero-articles .hero-content a {
color: white; color: white;
text-shadow: 0px 0px 20px rgba(0, 0, 0, 0.5); } text-shadow: 0px 0px 20px rgba(0, 0, 0, 0.5); }
div.vs-blog section.hero-section .hero-articles .hero-content p { div.vs-blog .hero-section .hero-articles .hero-content p {
color: white; } color: white; }
div.vs-blog section.hero-section .hero-articles .tags .tag { div.vs-blog .hero-section .hero-articles .tags .tag {
background: rgba(208, 208, 208, 0.3); background: rgba(208, 208, 208, 0.3);
color: #fff !important; } color: #fff !important; }
div.vs-blog section.hero-section .hero-articles .tags .tag:hover { div.vs-blog .hero-section .hero-articles .tags .tag:hover {
background: white; background: white;
color: black !important; } color: black !important; }
div.vs-blog section.hero-section .hero-articles .article-owner { div.vs-blog .hero-section .hero-articles .article-owner {
text-align: center; text-align: center;
position: relative; position: relative;
z-index: 12; } z-index: 12; }
div.vs-blog section.hero-section .hero-articles .article-owner .article-infos { div.vs-blog .hero-section .hero-articles .article-owner .article-infos {
color: black; } color: black; }
div.vs-blog section.hero-section .hero-articles .article-owner .article-infos a { div.vs-blog .hero-section .hero-articles .article-owner .article-infos a {
color: black; } color: black; }
div.vs-blog section.hero-section .hero-articles .article-owner .article-infos .seperator { div.vs-blog .hero-section .hero-articles .article-owner .article-infos .seperator {
margin: 0 4px; margin: 0 4px;
color: rgba(255, 255, 255, 0.2); } color: rgba(255, 255, 255, 0.2); }
div.vs-blog section.hero-section .hero-articles .article-owner .article-infos img.article-avatar { div.vs-blog .hero-section .hero-articles .article-owner .article-infos img.article-avatar {
width: 64px; width: 64px;
margin: -25px 10px 0px 0; margin: -25px 10px 0px 0;
border: 3px solid #fff; border: 3px solid #fff;
display: inline-block; display: inline-block;
border-radius: 50%; } border-radius: 50%; }
div.vs-blog section.hero-section .hero-articles .img-container { div.vs-blog .hero-section .hero-articles .img-container {
background: black; background: black; }
border-top-left-radius: 0; div.vs-blog .hero-section .hero-articles .img-container img {
border-top-right-radius: 0; }
div.vs-blog section.hero-section .hero-articles .img-container img {
filter: grayscale(10%); } filter: grayscale(10%); }
div.vs-blog section.hero-section .hero-articles .img-container::after { div.vs-blog .hero-section .hero-articles .img-container::after {
content: ""; content: "";
display: block; display: block;
position: absolute; position: absolute;
@ -222,31 +143,20 @@
background: linear-gradient(to bottom, transparent 0, rgba(0, 0, 0, 0.91) 89%, rgba(0, 0, 0, 0.93) 93%) !important; background: linear-gradient(to bottom, transparent 0, rgba(0, 0, 0, 0.91) 89%, rgba(0, 0, 0, 0.93) 93%) !important;
transition: .2s all ease-in-out; transition: .2s all ease-in-out;
opacity: .9; } opacity: .9; }
div.vs-blog section.hero-section .hero-articles:hover .img-container::after { div.vs-blog .hero-section .hero-articles:hover .img-container::after {
opacity: 1; } opacity: 1; }
div.vs-blog .box-articles { div.vs-blog .article-owner .article-infos {
padding: 0 0 40px 0; } color: #000; }
div.vs-blog .box-articles + .box-articles { div.vs-blog .article-owner .article-infos a {
padding: 40px 0; color: rgba(0, 0, 0, 0.6); }
border-top: 1px solid #f5f5f5; div.vs-blog .article-owner .article-infos .seperator {
border-bottom: 1px solid #f5f5f5; margin: 0 4px;
margin-top: -1px; } color: rgba(0, 0, 0, 0.2); }
div.vs-blog .box-articles h3 { div.vs-blog .article-owner .article-infos img.article-avatar {
margin: 10px 0; } width: 30px;
div.vs-blog .box-articles h3 a { margin: -1px 4px 0 0;
font-weight: 700; } display: inline-block;
div.vs-blog .box-articles .article-owner .article-infos { border-radius: 50%; }
color: #000; }
div.vs-blog .box-articles .article-owner .article-infos a {
color: rgba(0, 0, 0, 0.6); }
div.vs-blog .box-articles .article-owner .article-infos .seperator {
margin: 0 4px;
color: rgba(0, 0, 0, 0.2); }
div.vs-blog .box-articles .article-owner .article-infos img.article-avatar {
width: 30px;
margin: -1px 4px 0 0;
display: inline-block;
border-radius: 50%; }
div.vs-blog .card-articles .card-content { div.vs-blog .card-articles .card-content {
padding: 10px 0px 10px; } padding: 10px 0px 10px; }
div.vs-blog .card-articles .card-content h3 { div.vs-blog .card-articles .card-content h3 {
@ -281,40 +191,30 @@
font-size: 1.25em; } font-size: 1.25em; }
div.vs-blog .user-link-icons a:hover { div.vs-blog .user-link-icons a:hover {
color: #fff; } color: #fff; }
div.vs-blog .tags { div.vs-blog .tags .tag {
margin: 5px 0; } display: inline-block;
div.vs-blog .tags .tag { padding: 2px 8px;
display: inline-block; background: rgba(208, 208, 208, 0.3);
padding: 0px 6px; border-radius: 30px;
background: rgba(208, 208, 208, 0.3); margin: 0 1px 3px 0;
border-radius: 30px; color: #b1b1b1 !important;
margin: 4px 1px; font-size: .7em;
color: #b1b1b1 !important; line-height: 1.6em;
font-size: .64em; text-transform: uppercase; }
line-height: 1.6em; div.vs-blog .tags .tag:hover {
text-transform: uppercase; } background: black;
div.vs-blog .tags .tag:hover { color: white !important; }
background: black;
color: white !important; }
div.vs-blog .popular-tags a { div.vs-blog .popular-tags a {
display: block; display: block;
padding: 10px 15px; font-size: .9em; }
background: white;
margin-bottom: 2px;
font-size: .85em; }
div.vs-blog .popular-tags a:hover {
background: #f5f5f5; }
div.vs-blog .popular-tags a span { div.vs-blog .popular-tags a span {
float: right; float: right;
opacity: .3; opacity: .3;
font-size: .85em; } font-size: .9em; }
div.vs-blog .img-container { div.vs-blog .img-container {
position: relative; position: relative;
overflow: hidden; overflow: hidden;
border-radius: 4px; } border-radius: 4px; }
div.vs-blog .sidebox .sideboxes {
padding: 30px;
background: #fafafa; }
div.vs-blog .post-detail h1 { div.vs-blog .post-detail h1 {
padding: 0px 0; padding: 0px 0;
line-height: 1.25em; line-height: 1.25em;
@ -354,20 +254,19 @@
font-size: .95em; } font-size: .95em; }
div.vs-blog .media .media { div.vs-blog .media .media {
font-size: .95em; } font-size: .95em; }
div.vs-blog .comment-area .comment-owner span { div.vs-blog .comment-area .comment-owner {
font-weight: 300; margin-bottom: 2px; }
color: #999; div.vs-blog .comment-area .comment-owner span {
font-size: .8em; } font-weight: 300;
color: #999;
font-size: .8em; }
div.vs-blog .comment-area > .media { div.vs-blog .comment-area > .media {
padding: 30px 0; padding: 30px 0;
border-bottom: 1px solid #f1f1f1; } border-bottom: 1px solid #f1f1f1; }
div.vs-blog .comment-area > .media .media { div.vs-blog .comment-area > .media .media {
padding: 20px 0; } padding: 20px 0; }
div.vs-blog .comment-area .comment-buttons { div.vs-blog .comment-area .comment-buttons {
background: #f8f9fa; padding: 4px 8px; }
border-bottom: 1px solid #f1f1f1;
padding: 6px 10px;
font-size: .85em; }
div.vs-blog .comment-area .comment-buttons .seperator { div.vs-blog .comment-area .comment-buttons .seperator {
color: #ddd; color: #ddd;
margin: 0 8px; } margin: 0 8px; }
@ -380,6 +279,10 @@
border-radius: 3px; } border-radius: 3px; }
div.vs-blog .comment-area .comment-buttons .count.count-up { div.vs-blog .comment-area .comment-buttons .count.count-up {
background: #999; } background: #999; }
div.vs-blog .comment-area .comment-buttons a {
opacity: .5; }
div.vs-blog .comment-area .comment-buttons a:hover {
opacity: .75; }
div.vs-blog .comment-area p { div.vs-blog .comment-area p {
margin-bottom: 6px; } margin-bottom: 6px; }
div.vs-blog .comment-area .comment-avatar { div.vs-blog .comment-area .comment-avatar {

24
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Shared/Styles/blog.css.map

File diff suppressed because one or more lines are too long

2
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Shared/Styles/blog.min.css

File diff suppressed because one or more lines are too long

18
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Shared/Styles/blog.scss

@ -1,11 +1,13 @@
div.vs-blog { div.vs-blog {
position: relative; position: relative;
background: #fff; background: white;
font-family: Helvetica, sans-serif; padding: 0 30px;
font-size: 15px; font-family: 'Open Sans', Helvetica, Arial, sans-serif;
font-size: 14px;
@import "bootstrap-overwrite.scss"; @import "bootstrap-overwrite.scss";
@import "header.scss"; @import "header.scss";
@import "home.scss"; @import "home.scss";
@import "post.scss"; @import "post.scss";
@import "custom.scss"; @import "custom.scss";
} }

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

@ -36,6 +36,8 @@
<Content Remove="Pages\**\*.css" /> <Content Remove="Pages\**\*.css" />
<Content Remove="compilerconfig.json" /> <Content Remove="compilerconfig.json" />
<Content Remove="Properties\launchSettings.json" /> <Content Remove="Properties\launchSettings.json" />
<None Remove="Pages\Admin\Blogs\create.js" />
<None Remove="Pages\Admin\Blogs\edit.js" />
<None Include="compilerconfig.json" /> <None Include="compilerconfig.json" />
<None Include="Properties\launchSettings.json" /> <None Include="Properties\launchSettings.json" />
</ItemGroup> </ItemGroup>

4
modules/blogging/src/Volo.Blogging.Web/compilerconfig.json

@ -2,5 +2,9 @@
{ {
"outputFile": "Pages/Blog/Shared/Styles/blog.css", "outputFile": "Pages/Blog/Shared/Styles/blog.css",
"inputFile": "Pages/Blog/Shared/Styles/blog.scss" "inputFile": "Pages/Blog/Shared/Styles/blog.scss"
},
{
"outputFile": "Pages/Blog/Shared/Styles/_home.css",
"inputFile": "Pages/Blog/Shared/Styles/_home.scss"
} }
] ]

2
modules/docs/src/Volo.Docs.Application/Volo/Docs/Documents/GithubDocumentStore.cs

@ -12,7 +12,7 @@ namespace Volo.Docs.Documents
{ {
public class GithubDocumentStore : IDocumentStore, ITransientDependency public class GithubDocumentStore : IDocumentStore, ITransientDependency
{ {
public const string Type = "Github"; public const string Type = "Github"; //TODO: Conver to "github"
public async Task<Document> FindDocumentByNameAsync(Project project, string documentName, string version) public async Task<Document> FindDocumentByNameAsync(Project project, string documentName, string version)
{ {

1
modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Projects/ProjectConsts.cs

@ -6,6 +6,5 @@
public const int MaxShortNameLength = 32; public const int MaxShortNameLength = 32;
public const int MaxDefaultDocumentNameLength = 128; public const int MaxDefaultDocumentNameLength = 128;
public const int MaxNavigationDocumentNameLength = 128; public const int MaxNavigationDocumentNameLength = 128;
public static int MaxOrganizationLength = 128;
} }
} }

1
modules/docs/src/Volo.Docs.Domain/Volo/Docs/DocsConsts.cs

@ -3,6 +3,7 @@
public static class DocsConsts public static class DocsConsts
{ {
public const string DefaultDbTablePrefix = "Docs"; public const string DefaultDbTablePrefix = "Docs";
public const string DefaultDbSchema = null; public const string DefaultDbSchema = null;
} }
} }

3
modules/docs/src/Volo.Docs.Domain/Volo/Docs/DocsDomainModule.cs

@ -5,7 +5,8 @@ namespace Volo.Docs
{ {
[DependsOn( [DependsOn(
typeof(DocsDomainSharedModule), typeof(DocsDomainSharedModule),
typeof(AbpDddDomainModule))] typeof(AbpDddDomainModule)
)]
public class DocsDomainModule : AbpModule public class DocsDomainModule : AbpModule
{ {

22
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Project.cs

@ -9,25 +9,43 @@ namespace Volo.Docs.Projects
{ {
public class Project : AggregateRoot<Guid>, IHasExtraProperties public class Project : AggregateRoot<Guid>, IHasExtraProperties
{ {
/// <summary>
/// Name of the project for display purposes.
/// </summary>
public virtual string Name { get; protected set; } public virtual string Name { get; protected set; }
/// <summary>
/// A short name of the project to be seen in URLs.
/// </summary>
public virtual string ShortName { get; protected set; } public virtual string ShortName { get; protected set; }
/// <summary>
/// The format of the document (e.g. "md" for Markdown, "html" for HTML).
/// </summary>
public virtual string Format { get; protected set; } public virtual string Format { get; protected set; }
/// <summary>
/// The document for the initial page.
/// </summary>
public virtual string DefaultDocumentName { get; protected set; } public virtual string DefaultDocumentName { get; protected set; }
/// <summary>
/// The document to be used for the navigation menu (index).
/// </summary>
public virtual string NavigationDocumentName { get; protected set; } public virtual string NavigationDocumentName { get; protected set; }
/// <summary>
/// The source of the documents (e.g. Github).
/// </summary>
public virtual string DocumentStoreType { get; protected set; } public virtual string DocumentStoreType { get; protected set; }
public virtual string GoogleCustomSearchId { get; set; } public virtual string GoogleCustomSearchId { get; set; }
public Dictionary<string, object> ExtraProperties { get; protected set; } public virtual Dictionary<string, object> ExtraProperties { get; protected set; }
protected Project() protected Project()
{ {
ExtraProperties = new Dictionary<string, object>(); //TODO: Needed (by mongodb)..? ExtraProperties = new Dictionary<string, object>();
} }
public Project(Guid id, [NotNull] string name, [NotNull] string shortName, [NotNull] string defaultDocumentName, [NotNull] string navigationDocumentName, string googleCustomSearchId) public Project(Guid id, [NotNull] string name, [NotNull] string shortName, [NotNull] string defaultDocumentName, [NotNull] string navigationDocumentName, string googleCustomSearchId)

1
templates/mvc/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj

@ -8,6 +8,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.1" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

2
templates/mvc/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs

@ -62,7 +62,7 @@ namespace MyCompanyName.MyProjectName
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
var hostingEnvironment = context.Services.GetHostingEnvironment(); var hostingEnvironment = context.Services.GetHostingEnvironment();
var configuration = context.Services.BuildConfiguration(); var configuration = context.Services.GetConfiguration();
ConfigureDatabaseServices(context.Services, configuration); ConfigureDatabaseServices(context.Services, configuration);
ConfigureAutoMapper(context.Services); ConfigureAutoMapper(context.Services);

Loading…
Cancel
Save