Browse Source

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

pull/441/head
Armağan Ünlü 7 years ago
parent
commit
c49ff165ac
  1. 2
      README.md
  2. 30
      docs/docs-nav.json
  3. 10
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/Default.cshtml
  4. 12
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Toolbar/UserMenu/Default.cshtml
  5. 4
      framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AutoMapperExpressionExtensions.cs
  6. 25
      framework/src/Volo.Abp.Core/Volo/Abp/Modularity/AbpModule.cs
  7. 23
      framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItem.cs
  8. 3
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json
  9. 3
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json
  10. 9
      framework/src/Volo.Abp.VirtualFileSystem/Microsoft/Extensions/FileProviders/AbpFileInfoExtensions.cs
  11. 4
      modules/account/src/Volo.Abp.Account.Web/AbpAccountUserMenuContributor.cs
  12. 2
      modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/tr.json
  13. 2
      modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain/Volo/Abp/BackgroundJobs/BackgroundJobsDomainModule.cs
  14. 625
      modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20180912113852_Added_BlogUsers.Designer.cs
  15. 35
      modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20180912113852_Added_BlogUsers.cs
  16. 650
      modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20180918060116_Added_SocialLinks_To_Blog.Designer.cs
  17. 68
      modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20180918060116_Added_SocialLinks_To_Blog.cs
  18. 645
      modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20180918061015_SocialLinks_Made_Nullable.Designer.cs
  19. 98
      modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20180918061015_SocialLinks_Made_Nullable.cs
  20. 59
      modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/BloggingTestAppDbContextModelSnapshot.cs
  21. 10933
      modules/blogging/app/Volo.BloggingTestApp/Logs/logs.txt
  22. 10
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo.Blogging.Application.Contracts.csproj
  23. 14
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/BloggingApplicationContractsModule.cs
  24. 7
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/BloggingPermissionDefinitionProvider.cs
  25. 3
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/BloggingPermissions.cs
  26. 14
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Blogs/BlogDto.cs
  27. 24
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Blogs/Dtos/BlogDto.cs
  28. 21
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Blogs/Dtos/CreateBlogDto.cs
  29. 23
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Blogs/Dtos/UpdateBlogDto.cs
  30. 9
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Blogs/IBlogAppService.cs
  31. 5
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Comments/Dtos/CommentWithDetailsDto.cs
  32. 4
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Comments/Dtos/CommentWithRepliesDto.cs
  33. 4
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Comments/ICommentAppService.cs
  34. 14
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Localization/ApplicationContracts/en.json
  35. 14
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Localization/ApplicationContracts/tr.json
  36. 24
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/BlogUserDto.cs
  37. 2
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/GetPostInput.cs
  38. 2
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/IPostAppService.cs
  39. 20
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/PostDto.cs
  40. 2
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/PostWithDetailsDto.cs
  41. 6
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/BloggingApplicationAutoMapperProfile.cs
  42. 18
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/BloggingApplicationModule.cs
  43. 53
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Blogs/BlogAppService.cs
  44. 79
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Comments/CommentAppService.cs
  45. 61
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Comments/CommentAuthorizationHandler.cs
  46. 13
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/CommonOperations.cs
  47. 114
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Posts/PostAppService.cs
  48. 61
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Posts/PostAuthorizationHandler.cs
  49. 2
      modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Blogs/BlogConsts.cs
  50. 0
      modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Posts/PostConsts.cs
  51. 1
      modules/blogging/src/Volo.Blogging.Domain/Volo.Blogging.Domain.csproj
  52. 21
      modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Blogs/Blog.cs
  53. 5
      modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Blogs/IBlogRepository.cs
  54. 2
      modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Comments/ICommentRepository.cs
  55. 7
      modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Posts/IPostTagRepository.cs
  56. 4
      modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Tagging/ITagRepository.cs
  57. 43
      modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Users/BlogUser.cs
  58. 22
      modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Users/BlogUserLookupService.cs
  59. 9
      modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Users/IBlogUserLookupService.cs
  60. 14
      modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Users/IBlogUserRepository.cs
  61. 1
      modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo.Blogging.EntityFrameworkCore.csproj
  62. 17
      modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Blogs/EfCoreBlogRepository.cs
  63. 6
      modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Comments/EfCoreCommentRepository.cs
  64. 3
      modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/EntityFrameworkCore/BloggingDbContext.cs
  65. 15
      modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/EntityFrameworkCore/BloggingDbContextModelBuilderExtensions.cs
  66. 3
      modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/EntityFrameworkCore/IBloggingDbContext.cs
  67. 17
      modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Posts/EfCorePostTagRepository.cs
  68. 15
      modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Tagging/EfCoreTagRepository.cs
  69. 27
      modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Users/EfCoreQaUserRepository.cs
  70. 1
      modules/blogging/src/Volo.Blogging.HttpApi.Client/Volo.Blogging.HttpApi.Client.csproj
  71. 10
      modules/blogging/src/Volo.Blogging.HttpApi.Client/Volo/Blogging/BloggingHttpApiClientModule.cs
  72. 1
      modules/blogging/src/Volo.Blogging.HttpApi/Volo.Blogging.HttpApi.csproj
  73. 5
      modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/BloggingHttpApiModule.cs
  74. 77
      modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/BlogsController.cs
  75. 7
      modules/blogging/src/Volo.Blogging.Web/AbpBloggingWebAutoMapperProfile.cs
  76. 26
      modules/blogging/src/Volo.Blogging.Web/Areas/Blog/Controllers/PostsController.cs
  77. 210
      modules/blogging/src/Volo.Blogging.Web/Areas/Blog/Helpers/TagHelpers/GravatarTagHelper.cs
  78. 14
      modules/blogging/src/Volo.Blogging.Web/BloggingMenuContributor.cs
  79. 8
      modules/blogging/src/Volo.Blogging.Web/BloggingWebModule.cs
  80. 27
      modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/en.json
  81. 27
      modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/tr.json
  82. 33
      modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/Create.cshtml
  83. 65
      modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/Create.cshtml.cs
  84. 34
      modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/Edit.cshtml
  85. 85
      modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/Edit.cshtml.cs
  86. 45
      modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/Index.cshtml
  87. 13
      modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/Index.cshtml.cs
  88. 14
      modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/create.js
  89. 14
      modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/edit.js
  90. 84
      modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/index.js
  91. 66
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/BloggingPage.cs
  92. 2
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Index.cshtml
  93. 4
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Index.cshtml.cs
  94. 270
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Detail.cshtml
  95. 42
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Detail.cshtml.cs
  96. 2
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Edit.cshtml.cs
  97. 305
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Index.cshtml
  98. 10
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Index.cshtml.cs
  99. 3
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/New.cshtml.cs
  100. 38
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/detail.js

2
README.md

@ -1,6 +1,6 @@
# 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.

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"
}]
}]
}]
}

10
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/Default.cshtml

@ -2,6 +2,10 @@
@model ApplicationMenu
@foreach (var menuItem in Model.Items)
{
var elementId = string.IsNullOrEmpty(menuItem.ElementId) ? string.Empty : $"id=\"{menuItem.ElementId}\"";
var cssClass = string.IsNullOrEmpty(menuItem.CssClass) ? string.Empty : menuItem.CssClass;
var disabled = menuItem.IsDisabled ? "disabled" : string.Empty;
if (menuItem.IsLeaf)
{
if (menuItem.Url == null)
@ -9,7 +13,7 @@
continue;
}
<li class="nav-item @(menuItem.IsDisabled ? "disabled" : "")">
<li class="nav-item @cssClass @disabled" @elementId>
<a class="nav-link" href="@(menuItem.Url ?? "#")">
@if (menuItem.Icon != null)
{
@ -29,7 +33,9 @@
<div class="dropdown-menu" aria-labelledby="Menu_@(menuItem.Name)">
@foreach (var childMenuItem in menuItem.Items)
{
<a class="dropdown-item" href="@(childMenuItem.Url ?? "#")">@childMenuItem.DisplayName</a>
<a class="dropdown-item @cssClass @disabled" href="@(childMenuItem.Url ?? "#")" @Html.Raw(elementId)>
@childMenuItem.DisplayName
</a>
}
</div>
</li>

12
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Toolbar/UserMenu/Default.cshtml

@ -11,16 +11,20 @@
<a class="btn btn-link dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@CurrentUser.UserName
</a>
@if (Model.Items.Any())
{
<div class="dropdown-menu" aria-labelledby="dropdownMenuLink">
@foreach (var menuItem in Model.Items)
{
<a class="dropdown-item" href="@menuItem.Url">@menuItem.DisplayName</a>
}
var elementId = string.IsNullOrEmpty(menuItem.ElementId) ? string.Empty : $"id=\"{menuItem.ElementId}\"";
var cssClass = string.IsNullOrEmpty(menuItem.CssClass) ? string.Empty : menuItem.CssClass;
var disabled = menuItem.IsDisabled ? "disabled" : string.Empty;
@*<a class="dropdown-item" href="/Account/Logout">@L["Logout"]</a>*@
<a class="dropdown-item @cssClass @disabled" href="@(menuItem.Url ?? "#")" @Html.Raw(elementId)>
@menuItem.DisplayName
</a>
}
</div>
}
</div>

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 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.Reflection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace Volo.Abp.Modularity
@ -91,6 +92,30 @@ namespace Volo.Abp.Modularity
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)
where TOptions : class
{

23
framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItem.cs

@ -37,7 +37,7 @@ namespace Volo.Abp.UI.Navigation
/// Default value: 1000.
/// </summary>
public int Order { get; set; }
/// <summary>
/// The URL to navigate when this menu item is selected.
/// </summary>
@ -65,7 +65,7 @@ namespace Volo.Abp.UI.Navigation
/// Can be used to disable this menu item.
/// </summary>
public bool IsDisabled { get; set; }
/// <inheritdoc cref="IHasMenuItems.Items"/>
[NotNull]
public IList<ApplicationMenuItem> Items { get; }
@ -75,14 +75,27 @@ namespace Volo.Abp.UI.Navigation
/// </summary>
public object CustomData { get; set; }
/// <summary>
/// Can be used to render the element with a specific Id for DOM selections.
/// </summary>
public string ElementId { get; set; }
/// <summary>
/// Can be used to render the element with extra CSS classes.
/// </summary>
public string CssClass { get; set; }
public ApplicationMenuItem(
[NotNull] string name,
[NotNull] string name,
[NotNull] string displayName,
string url = null,
string icon = null,
int order = DefaultOrder,
object customData = null,
string target = null)
string target = null,
string elementId = null,
string cssClass = null)
{
Check.NotNullOrWhiteSpace(name, nameof(name));
Check.NotNullOrWhiteSpace(displayName, nameof(displayName));
@ -94,6 +107,8 @@ namespace Volo.Abp.UI.Navigation
Order = order;
CustomData = customData;
Target = target;
ElementId = elementId;
CssClass = cssClass;
Items = new List<ApplicationMenuItem>();
}

3
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json

@ -33,6 +33,7 @@
"PagerNext": "Next",
"PagerPrevious": "Previous",
"PagerInfo": "Showing {0} to {1} of {2} entries.",
"DatatableActionDropdownDefaultText": "Actions"
"DatatableActionDropdownDefaultText": "Actions",
"ChangePassword": "Change password"
}
}

3
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json

@ -33,6 +33,7 @@
"PagerNext": "Sonraki",
"PagerPrevious": "Önceki",
"PagerInfo": "{2} kayıttan {0} ile {1} arası gösteriliyor.",
"DatatableActionDropdownDefaultText": "İşlemler"
"DatatableActionDropdownDefaultText": "İşlemler",
"ChangePassword": "Şifre değiştir"
}
}

9
framework/src/Volo.Abp.VirtualFileSystem/Microsoft/Extensions/FileProviders/AbpFileInfoExtensions.cs

@ -1,6 +1,6 @@
using System.IO;
using JetBrains.Annotations;
using System.IO;
using System.Text;
using JetBrains.Annotations;
using Volo.Abp;
namespace Microsoft.Extensions.FileProviders
@ -24,7 +24,10 @@ namespace Microsoft.Extensions.FileProviders
using (var stream = fileInfo.CreateReadStream())
{
return encoding.GetString(stream.GetAllBytes());
using (var streamReader = new StreamReader(stream, encoding, true))
{
return streamReader.ReadToEnd();
}
}
}
}

4
modules/account/src/Volo.Abp.Account.Web/AbpAccountUserMenuContributor.cs

@ -10,7 +10,7 @@ namespace Volo.Abp.Account.Web
{
public AbpAccountUserMenuContributor()
{
}
public Task ConfigureMenuAsync(MenuConfigurationContext context)
@ -22,6 +22,8 @@ namespace Volo.Abp.Account.Web
var l = context.ServiceProvider.GetRequiredService<IStringLocalizer<AbpUiResource>>();
context.Menu.AddItem(new ApplicationMenuItem("Account.ChangePassword", l["ChangePassword"], icon: "fa fa-key", url: "#", elementId: "abp-account-change-password"));
context.Menu.AddItem(new ApplicationMenuItem("Account.Logout", l["Logout"], url: "/Account/Logout", icon: "fa fa-power-off", order: int.MaxValue - 1000));
return Task.CompletedTask;

2
modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/tr.json

@ -9,7 +9,7 @@
"UseAnotherServiceToLogin": "Başka bir servisle giriş yap",
"UserLockedOutMessage": "Kullanıcı hesabı hatalı giriş denemeleri nedeniyle kilitlenmiştir. Lütfen bir süre bekleyip tekrar deneyin.",
"InvalidUserNameOrPassword": "Kullanıcı adı ya da şifre geçersiz!",
"LoginIsNotAllowed": "You are not allowed to login! E-posta adresinizi ya da telefon numaranızı doğrulamanız gerekiyor.",
"LoginIsNotAllowed": "Giriş yapamazsınız! E-posta adresinizi ya da telefon numaranızı doğrulamanız gerekiyor.",
"SelfRegistrationDisabledMessage": "Bu uygulama için kullanıcıların kendi kendilerine kaydolmaları engellenmiştir. Yeni bir kullanıcı kaydetmek için lütfen uygulama yöneticisi ile iletişime geçin."
}
}

2
modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain/Volo/Abp/BackgroundJobs/BackgroundJobsDomainModule.cs

@ -9,7 +9,7 @@ namespace Volo.Abp.BackgroundJobs
typeof(AbpBackgroundJobsModule),
typeof(AbpAutoMapperModule)
)]
public class BackgroundJobsDomainModule : AbpModule
public class BackgroundJobsDomainModule : AbpModule //TODO: Rename to AbpBackgroundJobsDomainModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{

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");
}
}
}

650
modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20180918060116_Added_SocialLinks_To_Blog.Designer.cs

@ -0,0 +1,650 @@
// <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("20180918060116_Added_SocialLinks_To_Blog")]
partial class Added_SocialLinks_To_Blog
{
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<string>("Facebook")
.IsRequired()
.HasColumnName("Facebook")
.HasMaxLength(128);
b.Property<string>("Github")
.IsRequired()
.HasColumnName("Github")
.HasMaxLength(128);
b.Property<string>("Instagram")
.IsRequired()
.HasColumnName("Instagram")
.HasMaxLength(128);
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.Property<string>("StackOverflow")
.IsRequired()
.HasColumnName("StackOverflow")
.HasMaxLength(128);
b.Property<string>("Twitter")
.IsRequired()
.HasColumnName("Twitter")
.HasMaxLength(128);
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
}
}
}

68
modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20180918060116_Added_SocialLinks_To_Blog.cs

@ -0,0 +1,68 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace Volo.BloggingTestApp.EntityFrameworkCore.Migrations
{
public partial class Added_SocialLinks_To_Blog : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Facebook",
table: "BlgBlogs",
maxLength: 128,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "Github",
table: "BlgBlogs",
maxLength: 128,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "Instagram",
table: "BlgBlogs",
maxLength: 128,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "StackOverflow",
table: "BlgBlogs",
maxLength: 128,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "Twitter",
table: "BlgBlogs",
maxLength: 128,
nullable: false,
defaultValue: "");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Facebook",
table: "BlgBlogs");
migrationBuilder.DropColumn(
name: "Github",
table: "BlgBlogs");
migrationBuilder.DropColumn(
name: "Instagram",
table: "BlgBlogs");
migrationBuilder.DropColumn(
name: "StackOverflow",
table: "BlgBlogs");
migrationBuilder.DropColumn(
name: "Twitter",
table: "BlgBlogs");
}
}
}

645
modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20180918061015_SocialLinks_Made_Nullable.Designer.cs

@ -0,0 +1,645 @@
// <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("20180918061015_SocialLinks_Made_Nullable")]
partial class SocialLinks_Made_Nullable
{
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<string>("Facebook")
.HasColumnName("Facebook")
.HasMaxLength(128);
b.Property<string>("Github")
.HasColumnName("Github")
.HasMaxLength(128);
b.Property<string>("Instagram")
.HasColumnName("Instagram")
.HasMaxLength(128);
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.Property<string>("StackOverflow")
.HasColumnName("StackOverflow")
.HasMaxLength(128);
b.Property<string>("Twitter")
.HasColumnName("Twitter")
.HasMaxLength(128);
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
}
}
}

98
modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20180918061015_SocialLinks_Made_Nullable.cs

@ -0,0 +1,98 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace Volo.BloggingTestApp.EntityFrameworkCore.Migrations
{
public partial class SocialLinks_Made_Nullable : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "Twitter",
table: "BlgBlogs",
maxLength: 128,
nullable: true,
oldClrType: typeof(string),
oldMaxLength: 128);
migrationBuilder.AlterColumn<string>(
name: "StackOverflow",
table: "BlgBlogs",
maxLength: 128,
nullable: true,
oldClrType: typeof(string),
oldMaxLength: 128);
migrationBuilder.AlterColumn<string>(
name: "Instagram",
table: "BlgBlogs",
maxLength: 128,
nullable: true,
oldClrType: typeof(string),
oldMaxLength: 128);
migrationBuilder.AlterColumn<string>(
name: "Github",
table: "BlgBlogs",
maxLength: 128,
nullable: true,
oldClrType: typeof(string),
oldMaxLength: 128);
migrationBuilder.AlterColumn<string>(
name: "Facebook",
table: "BlgBlogs",
maxLength: 128,
nullable: true,
oldClrType: typeof(string),
oldMaxLength: 128);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "Twitter",
table: "BlgBlogs",
maxLength: 128,
nullable: false,
oldClrType: typeof(string),
oldMaxLength: 128,
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "StackOverflow",
table: "BlgBlogs",
maxLength: 128,
nullable: false,
oldClrType: typeof(string),
oldMaxLength: 128,
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "Instagram",
table: "BlgBlogs",
maxLength: 128,
nullable: false,
oldClrType: typeof(string),
oldMaxLength: 128,
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "Github",
table: "BlgBlogs",
maxLength: 128,
nullable: false,
oldClrType: typeof(string),
oldMaxLength: 128,
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "Facebook",
table: "BlgBlogs",
maxLength: 128,
nullable: false,
oldClrType: typeof(string),
oldMaxLength: 128,
oldNullable: true);
}
}
}

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

@ -305,6 +305,18 @@ namespace Volo.BloggingTestApp.EntityFrameworkCore.Migrations
.HasColumnName("Description")
.HasMaxLength(1024);
b.Property<string>("Facebook")
.HasColumnName("Facebook")
.HasMaxLength(128);
b.Property<string>("Github")
.HasColumnName("Github")
.HasMaxLength(128);
b.Property<string>("Instagram")
.HasColumnName("Instagram")
.HasMaxLength(128);
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnName("IsDeleted")
@ -326,6 +338,14 @@ namespace Volo.BloggingTestApp.EntityFrameworkCore.Migrations
.HasColumnName("ShortName")
.HasMaxLength(32);
b.Property<string>("StackOverflow")
.HasColumnName("StackOverflow")
.HasMaxLength(128);
b.Property<string>("Twitter")
.HasColumnName("Twitter")
.HasMaxLength(128);
b.HasKey("Id");
b.ToTable("BlgBlogs");
@ -501,6 +521,45 @@ namespace Volo.BloggingTestApp.EntityFrameworkCore.Migrations
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")

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

File diff suppressed because it is too large

10
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo.Blogging.Application.Contracts.csproj

@ -9,6 +9,16 @@
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<None Remove="Volo\Blogging\Localization\ApplicationContracts\en.json" />
<None Remove="Volo\Blogging\Localization\ApplicationContracts\tr.json" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Volo\Blogging\Localization\ApplicationContracts\en.json" />
<EmbeddedResource Include="Volo\Blogging\Localization\ApplicationContracts\tr.json" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Blogging.Domain.Shared\Volo.Blogging.Domain.Shared.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Ddd.Application\Volo.Abp.Ddd.Application.csproj" />

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

@ -1,6 +1,9 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem;
using Volo.Blogging.Localization;
namespace Volo.Blogging
{
@ -13,6 +16,17 @@ namespace Volo.Blogging
{
options.DefinitionProviders.Add<BloggingPermissionDefinitionProvider>();
});
context.Services.Configure<VirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<BloggingApplicationContractsModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Get<BloggingResource>()
.AddVirtualJson("/Localization/Resources/Blogging/ApplicationContracts");
});
}
}
}

7
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/BloggingPermissionDefinitionProvider.cs

@ -10,17 +10,18 @@ namespace Volo.Blogging
{
var bloggingGroup = context.AddGroup(BloggingPermissions.GroupName, L("Permission:Blogging"));
var blogs = bloggingGroup.AddPermission(BloggingPermissions.Blogs.Default, L("Permission:BlogManagement"));
var blogs = bloggingGroup.AddPermission(BloggingPermissions.Blogs.Default, L("Permission:Blogs"));
blogs.AddChild(BloggingPermissions.Blogs.Management, L("Permission:Management"));
blogs.AddChild(BloggingPermissions.Blogs.Update, L("Permission:Edit"));
blogs.AddChild(BloggingPermissions.Blogs.Delete, L("Permission:Delete"));
blogs.AddChild(BloggingPermissions.Blogs.Create, L("Permission:Create"));
var posts = bloggingGroup.AddPermission(BloggingPermissions.Posts.Default, L("Permission:PostManagement"));
var posts = bloggingGroup.AddPermission(BloggingPermissions.Posts.Default, L("Permission:Posts"));
posts.AddChild(BloggingPermissions.Posts.Update, L("Permission:Edit"));
posts.AddChild(BloggingPermissions.Posts.Delete, L("Permission:Delete"));
posts.AddChild(BloggingPermissions.Posts.Create, L("Permission:Create"));
var tags = bloggingGroup.AddPermission(BloggingPermissions.Tags.Default, L("Permission:Tag"));
var tags = bloggingGroup.AddPermission(BloggingPermissions.Tags.Default, L("Permission:Tags"));
tags.AddChild(BloggingPermissions.Tags.Update, L("Permission:Edit"));
tags.AddChild(BloggingPermissions.Tags.Delete, L("Permission:Delete"));
tags.AddChild(BloggingPermissions.Tags.Create, L("Permission:Create"));

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

@ -7,9 +7,11 @@
public static class Blogs
{
public const string Default = GroupName + ".Blog";
public const string Management = Default + ".Management";
public const string Delete = Default + ".Delete";
public const string Update = Default + ".Update";
public const string Create = Default + ".Create";
}
public static class Posts
@ -42,6 +44,7 @@
{
GroupName,
Blogs.Default,
Blogs.Management,
Blogs.Delete,
Blogs.Update,
Blogs.Create,

14
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Blogs/BlogDto.cs

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

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

@ -0,0 +1,24 @@
using System;
using Volo.Abp.Application.Dtos;
namespace Volo.Blogging.Blogs.Dtos
{
public class BlogDto : FullAuditedEntityDto<Guid>
{
public string Name { get; set; }
public string ShortName { get; set; }
public string Description { get; set; }
public string Facebook { get; set; }
public string Twitter { get; set; }
public string Instagram { get; set; }
public string Github { get; set; }
public string StackOverflow { get; set; }
}
}

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

@ -0,0 +1,21 @@
namespace Volo.Blogging.Blogs.Dtos
{
public class CreateBlogDto
{
public string Name { get; set; }
public string ShortName { get; set; }
public string Description { get; set; }
public string Facebook { get; set; }
public string Twitter { get; set; }
public string Instagram { get; set; }
public string Github { get; set; }
public string StackOverflow { get; set; }
}
}

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

@ -0,0 +1,23 @@
using System;
namespace Volo.Blogging.Blogs.Dtos
{
public class UpdateBlogDto
{
public string Name { get; set; }
public string ShortName { get; set; }
public string Description { get; set; }
public string Facebook { get; set; }
public string Twitter { get; set; }
public string Instagram { get; set; }
public string Github { get; set; }
public string StackOverflow { get; set; }
}
}

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

@ -2,15 +2,24 @@
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Blogging.Blogs.Dtos;
namespace Volo.Blogging.Blogs
{
public interface IBlogAppService : IApplicationService
{
Task<PagedResultDto<BlogDto>> GetListPagedAsync(PagedAndSortedResultRequestDto input);
Task<ListResultDto<BlogDto>> GetListAsync();
Task<BlogDto> GetByShortNameAsync(string shortName);
Task<BlogDto> GetAsync(Guid id);
Task<BlogDto> Create(CreateBlogDto input);
Task<BlogDto> Update(Guid id, UpdateBlogDto input);
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 Volo.Abp.Application.Dtos;
using Volo.Blogging.Posts;
namespace Volo.Blogging.Comments.Dtos
{
public class CommentDto : FullAuditedEntityDto<Guid>
public class CommentWithDetailsDto : FullAuditedEntityDto<Guid>
{
public Guid? RepliedCommentId { 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 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<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);
}

14
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Localization/ApplicationContracts/en.json

@ -0,0 +1,14 @@
{
"culture": "en",
"texts": {
"Permission:Blogging": "Blog",
"Permission:Blogs": "Blogs",
"Permission:Posts": "Posts",
"Permission:Tags": "Tags",
"Permission:Comments": "Comments",
"Permission:Management": "Management",
"Permission:Edit": "Edit",
"Permission:Create": "Create",
"Permission:Delete": "Delete"
}
}

14
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Localization/ApplicationContracts/tr.json

@ -0,0 +1,14 @@
{
"culture": "tr",
"texts": {
"Permission:Blogging": "Blog",
"Permission:Blogs": "Bloglar",
"Permission:Posts": "Yazılar",
"Permission:Tags": "Etiketler",
"Permission:Comments": "Yorumlar",
"Permission:Management": "Yönetme",
"Permission:Edit": "Düzenle",
"Permission:Create": "Ekle",
"Permission:Delete": "Sil"
}
}

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
{

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 DeleteAsync(Guid id);
Task<PostWithDetailsDto> CreateAsync(CreatePostDto input);
Task<PostWithDetailsDto> UpdateAsync(Guid id, UpdatePostDto input);

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

@ -1,20 +0,0 @@
using System;
using Volo.Abp.Application.Dtos;
namespace Volo.Blogging.Posts
{
public class PostDto : FullAuditedEntityDto<Guid>
{
public Guid BlogId { get; protected set; }
public string Title { get; protected set; }
public string CoverImage { get; protected set; }
public string Url { get; set; }
public int ReadCount { get; set; }
public string Content { get; set; }
}
}

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 BlogUserDto Writer { get; set; }
public List<TagDto> Tags { get; set; }
}
}

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

@ -1,6 +1,7 @@
using AutoMapper;
using Volo.Abp.AutoMapper;
using Volo.Blogging.Blogs;
using Volo.Blogging.Blogs.Dtos;
using Volo.Blogging.Comments;
using Volo.Blogging.Comments.Dtos;
using Volo.Blogging.Posts;
@ -14,9 +15,8 @@ namespace Volo.Blogging
public BloggingApplicationAutoMapperProfile()
{
CreateMap<Blog, BlogDto>();
CreateMap<Post, PostDto>();
CreateMap<Post, PostWithDetailsDto>().Ignore(x=>x.CommentCount);
CreateMap<Comment, CommentDto>();
CreateMap<Post, PostWithDetailsDto>().Ignore(x=>x.Writer).Ignore(x=>x.CommentCount);
CreateMap<Comment, CommentWithDetailsDto>().Ignore(x => x.Writer);
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.Caching;
using Volo.Abp.Modularity;
using Volo.Blogging.Comments;
using Volo.Blogging.Posts;
namespace Volo.Blogging
{
@ -18,6 +21,19 @@ namespace Volo.Blogging
{
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>();
}
}
}

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

@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Entities;
using Volo.Blogging.Blogs.Dtos;
namespace Volo.Blogging.Blogs
{
@ -15,6 +17,18 @@ namespace Volo.Blogging.Blogs
{
_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()
{
var blogs = await _blogRepository.GetListAsync();
@ -42,5 +56,44 @@ namespace Volo.Blogging.Blogs
return ObjectMapper.Map<Blog, BlogDto>(blog);
}
[Authorize(BloggingPermissions.Blogs.Create)]
public async Task<BlogDto> Create(CreateBlogDto input)
{
var newBlog = await _blogRepository.InsertAsync(new Blog(GuidGenerator.Create(), input.Name, input.ShortName)
{
Description = input.Description,
Facebook = input.Facebook,
Twitter = input.Twitter,
Instagram = input.Instagram,
Github = input.Github,
StackOverflow = input.StackOverflow
});
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;
blog.Facebook = input.Facebook;
blog.Twitter = input.Twitter;
blog.Instagram = input.Instagram;
blog.Github = input.Github;
blog.StackOverflow = input.StackOverflow;
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 Volo.Abp.Application.Services;
using Volo.Abp.Guids;
using Volo.Abp.Users;
using Volo.Blogging.Comments.Dtos;
using Volo.Blogging.Posts;
using Volo.Blogging.Users;
namespace Volo.Blogging.Comments
{
// [Authorize(BloggingPermissions.Comments.Default)]
public class CommentAppService : ApplicationService, ICommentAppService
{
protected IBlogUserLookupService UserLookupService;
private readonly ICommentRepository _commentRepository;
private readonly IGuidGenerator _guidGenerator;
public CommentAppService(ICommentRepository commentRepository, IGuidGenerator guidGenerator)
public CommentAppService(ICommentRepository commentRepository, IGuidGenerator guidGenerator, IBlogUserLookupService userLookupService)
{
_commentRepository = commentRepository;
_guidGenerator = guidGenerator;
UserLookupService = userLookupService;
}
public async Task<List<CommentWithRepliesDto>> GetHierarchicalListOfPostAsync(GetCommentListOfPostAsync 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>();
foreach (var commentDto in comments)
@ -45,72 +73,43 @@ namespace Volo.Blogging.Comments
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);
return new List<CommentDto>(
ObjectMapper.Map<List<Comment>, List<CommentDto>>(comments));
return new List<CommentWithDetailsDto>(
ObjectMapper.Map<List<Comment>, List<CommentWithDetailsDto>>(comments));
}
[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);
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);
if (CurrentUser.Id != comment.CreatorId)
{
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);
}
await AuthorizationService.CheckAsync(comment, CommonOperations.Update);
private async Task<CommentDto> UpdateCommentAsync(Guid id, Comment comment, UpdateCommentDto input)
{
comment.SetText(input.Text);
comment = await _commentRepository.UpdateAsync(comment);
return ObjectMapper.Map<Comment, CommentDto>(comment);
return ObjectMapper.Map<Comment, CommentWithDetailsDto>(comment);
}
public async Task DeleteAsync(Guid id)
{
var comment = await _commentRepository.GetAsync(id);
if (CurrentUser.Id != comment.CreatorId)
{
await DeleteAsAdminAsync(id);
return;
}
await DeleteCommentAsync(id);
}
await AuthorizationService.CheckAsync(comment, CommonOperations.Delete);
[Authorize(BloggingPermissions.Comments.Delete)]
private async Task DeleteAsAdminAsync(Guid id)
{
await DeleteCommentAsync(id);
}
private async Task DeleteCommentAsync(Guid id)
{
await _commentRepository.DeleteAsync(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) };
}
}

114
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.Tagging;
using Volo.Blogging.Tagging.Dtos;
using Volo.Blogging.Users;
namespace Volo.Blogging.Posts
{
@ -19,13 +20,16 @@ namespace Volo.Blogging.Posts
//[Authorize(BloggingPermissions.Posts.Default)]
public class PostAppService : ApplicationService, IPostAppService
{
protected IBlogUserLookupService UserLookupService { get; }
private readonly IPostRepository _postRepository;
private readonly ITagRepository _tagRepository;
private readonly IPostTagRepository _postTagRepository;
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;
_tagRepository = tagRepository;
_postTagRepository = postTagRepository;
@ -35,26 +39,61 @@ namespace Volo.Blogging.Posts
public async Task<ListResultDto<PostWithDetailsDto>> GetListByBlogIdAndTagName(Guid id, string tagName)
{
var posts = _postRepository.GetPostsByBlogId(id);
var tag = tagName.IsNullOrWhiteSpace() ? null : await _tagRepository.FindByNameAsync(tagName);
var userDictionary = new Dictionary<Guid, BlogUserDto>();
var postDtos = new List<PostWithDetailsDto>(ObjectMapper.Map<List<Post>, List<PostWithDetailsDto>>(posts));
var postDtos = new List<PostWithDetailsDto>(
ObjectMapper.Map<List<Post>, List<PostWithDetailsDto>>(posts));
if (tag != null)
{
postDtos = await FilterPostsByTag(postDtos, tag);
}
foreach (var postDto in postDtos)
{
postDto.Tags = await GetTagsOfPost(postDto);
postDto.CommentCount = await _commentRepository.GetCommentCountOfPostAsync(postDto.Id);
}
if (!tagName.IsNullOrWhiteSpace())
foreach (var postDto in postDtos)
{
var tag = await _tagRepository.GetByNameAsync(tagName);
postDtos = postDtos.Where(p => p.Tags.Any(t => t.Id == tag.Id)).ToList();
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)
{
if (postDto.CreatorId.HasValue && userDictionary.ContainsKey((Guid)postDto.CreatorId))
{
postDto.Writer = userDictionary[(Guid)postDto.CreatorId];
}
}
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)
{
var post = await _postRepository.GetPostByUrl(input.BlogId, input.Url);
@ -63,7 +102,14 @@ namespace Volo.Blogging.Posts
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;
}
@ -74,16 +120,41 @@ namespace Volo.Blogging.Posts
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;
}
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)]
public async Task<PostWithDetailsDto> UpdateAsync(Guid id, UpdatePostDto input)
{
var post = await _postRepository.GetAsync(id);
input.Url = await RenameUrlIfItAlreadyExistAsync(input.BlogId, input.Url, post);
await AuthorizationService.CheckAsync(post, CommonOperations.Update);
post.SetTitle(input.Title);
post.SetUrl(input.Url);
post.Content = input.Content;
@ -100,6 +171,8 @@ namespace Volo.Blogging.Posts
[Authorize(BloggingPermissions.Posts.Create)]
public async Task<PostWithDetailsDto> CreateAsync(CreatePostDto input)
{
input.Url = await RenameUrlIfItAlreadyExistAsync(input.BlogId, input.Url);
var post = new Post(
id: GuidGenerator.Create(),
blogId: input.BlogId,
@ -107,7 +180,8 @@ namespace Volo.Blogging.Posts
title: input.Title,
coverImage: input.CoverImage,
url: input.Url
) {Content = input.Content};
)
{ Content = input.Content };
await _postRepository.InsertAsync(post);
@ -117,6 +191,18 @@ namespace Volo.Blogging.Posts
return ObjectMapper.Map<Post, PostWithDetailsDto>(post);
}
private async Task<string> RenameUrlIfItAlreadyExistAsync(Guid blogId, string url, Post existingPost = null)
{
var postList = await _postRepository.GetListAsync();
if (postList.Where(p => p.Url == url).WhereIf(existingPost != null, p => existingPost.Id != p.Id).Any())
{
return url + "-" + Guid.NewGuid().ToString().Substring(0, 5);
}
return url;
}
private async Task SaveTags(List<String> newTags, Post post)
{
@ -171,9 +257,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));
@ -186,7 +272,7 @@ namespace Volo.Blogging.Posts
{
return new List<string>();
}
return new List<string>(tags.Split(",").Select(t=>t.Trim()));
return new List<string>(tags.Split(",").Select(t => t.Trim()));
}
}
}

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;
}
}
}

2
modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Blogs/BlogConsts.cs

@ -7,5 +7,7 @@
public const int MaxShortNameLength = 32;
public const int MaxDescriptionLength = 1024;
public const int MaxSocialLinkLength = 128;
}
}

0
modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Posts/BlogConsts.cs → modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Posts/PostConsts.cs

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

@ -11,6 +11,7 @@
<ItemGroup>
<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" />
</ItemGroup>

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

@ -16,6 +16,21 @@ namespace Volo.Blogging.Blogs
[CanBeNull]
public virtual string Description { get; set; }
[CanBeNull]
public virtual string Facebook { get; set; }
[CanBeNull]
public virtual string Twitter { get; set; }
[CanBeNull]
public virtual string Instagram { get; set; }
[CanBeNull]
public virtual string Github { get; set; }
[CanBeNull]
public virtual string StackOverflow { get; set; }
protected Blog()
{
@ -33,5 +48,11 @@ namespace Volo.Blogging.Blogs
Name = Check.NotNullOrWhiteSpace(name, nameof(name));
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.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
@ -7,5 +8,9 @@ namespace Volo.Blogging.Blogs
public interface IBlogRepository : IBasicRepository<Blog, Guid>
{
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<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
{
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> FindByNameAsync(string name);
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>
<ProjectReference Include="..\Volo.Blogging.Domain\Volo.Blogging.Domain.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>
</Project>

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

@ -1,4 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
@ -19,5 +22,19 @@ namespace Volo.Blogging.Blogs
{
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
.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.Posts;
using Volo.Blogging.Tagging;
using Volo.Blogging.Users;
namespace Volo.Blogging.EntityFrameworkCore
{
@ -14,6 +15,8 @@ namespace Volo.Blogging.EntityFrameworkCore
public static string TablePrefix { get; set; } = BloggingConsts.DefaultDbTablePrefix;
public static string Schema { get; set; } = BloggingConsts.DefaultDbSchema;
public DbSet<BlogUser> Users { get; set; }
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }

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

@ -3,10 +3,12 @@ using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using Volo.Abp;
using Volo.Abp.EntityFrameworkCore.Modeling;
using Volo.Abp.Users.EntityFrameworkCore;
using Volo.Blogging.Blogs;
using Volo.Blogging.Comments;
using Volo.Blogging.Posts;
using Volo.Blogging.Tagging;
using Volo.Blogging.Users;
namespace Volo.Blogging.EntityFrameworkCore
{
@ -21,6 +23,14 @@ namespace Volo.Blogging.EntityFrameworkCore
var options = new BloggingModelBuilderConfigurationOptions();
optionsAction?.Invoke(options);
builder.Entity<BlogUser>(b =>
{
b.ToTable(options.TablePrefix + "Users", options.Schema);
b.ConfigureAbpUser(options);
b.ConfigureExtraProperties();
});
builder.Entity<Blog>(b =>
{
b.ToTable(options.TablePrefix + "Blogs", options.Schema);
@ -28,6 +38,11 @@ namespace Volo.Blogging.EntityFrameworkCore
b.ConfigureFullAudited();
b.Property(x => x.Name).IsRequired().HasMaxLength(BlogConsts.MaxNameLength).HasColumnName(nameof(Blog.Name));
b.Property(x => x.Facebook).HasMaxLength(BlogConsts.MaxSocialLinkLength).HasColumnName(nameof(Blog.Facebook));
b.Property(x => x.Twitter).HasMaxLength(BlogConsts.MaxSocialLinkLength).HasColumnName(nameof(Blog.Twitter));
b.Property(x => x.Instagram).HasMaxLength(BlogConsts.MaxSocialLinkLength).HasColumnName(nameof(Blog.Instagram));
b.Property(x => x.Github).HasMaxLength(BlogConsts.MaxSocialLinkLength).HasColumnName(nameof(Blog.Github));
b.Property(x => x.StackOverflow).HasMaxLength(BlogConsts.MaxSocialLinkLength).HasColumnName(nameof(Blog.StackOverflow));
b.Property(x => x.ShortName).IsRequired().HasMaxLength(BlogConsts.MaxShortNameLength).HasColumnName(nameof(Blog.ShortName));
b.Property(x => x.Description).IsRequired(false).HasMaxLength(BlogConsts.MaxDescriptionLength).HasColumnName(nameof(Blog.Description));
});

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.Posts;
using Volo.Blogging.Tagging;
using Volo.Blogging.Users;
namespace Volo.Blogging.EntityFrameworkCore
{
[ConnectionStringName("Blogging")]
public interface IBloggingDbContext : IEfCoreDbContext
{
DbSet<BlogUser> Users { get; }
DbSet<Blog> Blogs { 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.Blogging.EntityFrameworkCore;
@ -9,5 +13,16 @@ namespace Volo.Blogging.Posts
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)
{
return await DbSet.FirstAsync(t=>t.Name == name);
}
public async Task<Tag> FindByNameAsync(string 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();
}
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>
<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>
</Project>

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

@ -1,12 +1,20 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Http.Client;
using Volo.Abp.Modularity;
namespace Volo.Blogging
{
[DependsOn(
typeof(BloggingApplicationContractsModule))]
typeof(BloggingApplicationContractsModule),
typeof(AbpHttpClientModule))]
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>
<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>
</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;
namespace Volo.Blogging
{
[DependsOn(
typeof(BloggingApplicationContractsModule))]
typeof(BloggingApplicationContractsModule),
typeof(AbpAspNetCoreMvcModule))]
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);
}
}
}

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

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

26
modules/blogging/src/Volo.Blogging.Web/Areas/Blog/Controllers/PostsController.cs

@ -0,0 +1,26 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Blogging.Posts;
namespace Volo.Blogging.Areas.Blog.Controllers
{
[Area("Blog")]
[Route("Blog/[controller]/[action]")]
public class PostsController : AbpController
{
private readonly IPostAppService _postAppService;
public PostsController(IPostAppService postAppService)
{
_postAppService = postAppService;
}
[HttpPost]
public async Task Delete(Guid id)
{
await _postAppService.DeleteAsync(id);
}
}
}

210
modules/blogging/src/Volo.Blogging.Web/Areas/Blog/Helpers/TagHelpers/GravatarTagHelper.cs

@ -0,0 +1,210 @@
using System;
using System.ComponentModel;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace Volo.Blogging.Areas.Blog.Helpers.TagHelpers
{
/// <inheritdoc />
/// <summary>
/// Returns a Globally Recognised Avatar https://en.gravatar.com
/// </summary>
[HtmlTargetElement("img", Attributes = "gravatar-email")]
public class GravatarTagHelper : TagHelper
{
private readonly IHttpContextAccessor _contextAccessor;
public GravatarTagHelper(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
/// <summary>
/// Email Address for the Gravatar
/// </summary>
[HtmlAttributeName("gravatar-email")]
public string Email { get; set; }
/// <summary>
/// Gravatar content rating (note that Gravatars are self-rated)
/// </summary>
[HtmlAttributeName("gravatar-rating")]
public GravatarRating Rating { get; set; } = GravatarRating.GeneralAudiences;
/// <summary>
/// Size in pixels (default: 80)
/// </summary>
[HtmlAttributeName("gravatar-size")]
public int Size { get; set; } = 80;
/// <summary>
/// URL to a custom default image (e.g: 'Url.Content("~/images/no-grvatar.png")' )
/// </summary>
[HtmlAttributeName("default-image-url")]
public string DefaultImageUrl { get; set; } = "";
/// <summary>
/// Prefer the default image over the users own Gravatar
/// </summary>
[HtmlAttributeName("force-default-image")]
public bool ForceDefaultImage { get; set; }
/// <summary>
/// Default image if user hasn't created a Gravatar
/// </summary>
[HtmlAttributeName("default-image")]
public GravatarDefaultImage DefaultImage { get; set; } = GravatarDefaultImage.Default;
/// <summary>
/// Always do secure (https) requests
/// </summary>
[HtmlAttributeName("force-secure-request")]
public bool ForceSecureRequest { get; set; } = true;
public override void Process(TagHelperContext context, TagHelperOutput output)
{
var emailAddress = Email == null ? string.Empty : Email.Trim().ToLower();
var url = string.Format("{0}://{1}.gravatar.com/avatar/{2}?s={3}{4}{5}{6}",
GetUrlScheme(),
GetUrlPrefix(),
GetMd5Hash(emailAddress),
Size,
GetDefaultImageParameter(),
GetForceDefaultImageParameter(),
GetRatingParameter()
);
output.Attributes.SetAttribute("src", url);
}
private string GetUrlScheme()
{
return ForceSecureRequest || _contextAccessor.HttpContext.Request.IsHttps
? "https" : "http";
}
private string GetUrlPrefix()
{
return ForceSecureRequest || _contextAccessor.HttpContext.Request.IsHttps ? "secure" : "www";
}
private string GetDefaultImageParameter()
{
return "&d=" + (!string.IsNullOrEmpty(DefaultImageUrl)
? System.Net.WebUtility.UrlEncode(DefaultImageUrl)
: GetEnumDescription(DefaultImage));
}
private string GetForceDefaultImageParameter()
{
return ForceDefaultImage ? "&f=y" : "";
}
private string GetRatingParameter()
{
return "&r=" + GetEnumDescription(Rating);
}
/// <summary>
/// Generates an MD5 hash of the given string
/// </summary>
/// <remarks>Source: http://msdn.microsoft.com/en-us/library/system.security.cryptography.md5.aspx </remarks>
private static string GetMd5Hash(string input)
{
// Convert the input string to a byte array and compute the hash.
var data = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(input));
// Create a new Stringbuilder to collect the bytes
// and create a string.
var sBuilder = new StringBuilder();
// Loop through each byte of the hashed data
// and format each one as a hexadecimal string.
foreach (var t in data)
{
sBuilder.Append(t.ToString("x2"));
}
// Return the hexadecimal string.
return sBuilder.ToString();
}
/// <summary>
/// Returns the value of a Description for a given Enum value
/// </summary>
/// <remarks>Source: http://blogs.msdn.com/b/abhinaba/archive/2005/10/21/483337.aspx </remarks>
/// <param name="en"></param>
/// <returns></returns>
private static string GetEnumDescription(Enum en)
{
var type = en.GetType();
var memInfo = type.GetMember(en.ToString());
if (memInfo == null || memInfo.Length <= 0)
{
return en.ToString();
}
var attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attrs != null && attrs.Any())
{
return ((DescriptionAttribute)attrs.First()).Description;
}
return en.ToString();
}
public enum GravatarDefaultImage
{
/// <summary>Default Gravatar logo</summary>
[Description("")]
Default,
/// <summary>404 - do not load any image if none is associated with the email hash, instead return an HTTP 404 (File Not Found) response</summary>
[Description("404")]
Http404,
/// <summary>Mystery-Man - a simple, cartoon-style silhouetted outline of a person (does not vary by email hash)</summary>
[Description("mm")]
MysteryMan,
/// <summary>Identicon - a geometric pattern based on an email hash</summary>
[Description("identicon")]
Identicon,
/// <summary>MonsterId - a generated 'monster' with different colors, faces, etc</summary>
[Description("monsterid")]
MonsterId,
/// <summary>Wavatar - generated faces with differing features and backgrounds</summary>
[Description("wavatar")]
Wavatar,
/// <summary>Retro - awesome generated, 8-bit arcade-style pixelated faces</summary>
[Description("retro")]
Retro
}
/// <summary>
/// Gravatar allows users to self-rate their images so that they can indicate if an image is appropriate for a certain audience. By default, only 'G' rated images are displayed unless you indicate that you would like to see higher ratings
/// </summary>
public enum GravatarRating
{
/// <summary>Suitable for display on all websites with any audience type</summary>
[Description("g")]
GeneralAudiences,
/// <summary>May contain rude gestures, provocatively dressed individuals, the lesser swear words, or mild violence</summary>
[Description("pg")]
ParentalGuidance,
/// <summary>May contain such things as harsh profanity, intense violence, nudity, or hard drug use</summary>
[Description("r")]
Restricted,
/// <summary>May contain hardcore sexual imagery or extremely disturbing violence</summary>
[Description("x")]
OnlyMature
}
}
}

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

@ -27,7 +27,21 @@ namespace Volo.Blogging
var rootMenuItem = new ApplicationMenuItem("Blogs", l["Menu:Blogs"], "/Blog");
context.Menu.AddItem(rootMenuItem);
if (await authorizationService.IsGrantedAsync(BloggingPermissions.Blogs.Management))
{
var managementRootMenuItem = new ApplicationMenuItem("BlogManagement", l["Menu:BlogManagement"]);
if (await authorizationService.IsGrantedAsync(BloggingPermissions.Blogs.Management))
{
managementRootMenuItem.AddItem(new ApplicationMenuItem("BlogsAdmin", 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 Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AutoMapper;
using Volo.Abp.Localization;
using Volo.Abp.Localization.Resources.AbpValidation;
@ -15,8 +16,9 @@ namespace Volo.Blogging
{
[DependsOn(
typeof(BloggingHttpApiModule),
typeof(AbpAutoMapperModule),
typeof(AbpAspNetCoreMvcUiBootstrapModule)
typeof(AbpAspNetCoreMvcUiBootstrapModule),
typeof(AbpAspNetCoreMvcUiBundlingModule),
typeof(AbpAutoMapperModule)
)]
public class BloggingWebModule : AbpModule
{
@ -37,7 +39,7 @@ namespace Volo.Blogging
Configure<VirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<BloggingWebModule>("Volo.Blogging");
options.FileSets.AddEmbedded<BloggingWebModule>("Volo.Blogging.Web");
});
Configure<AbpLocalizationOptions>(options =>

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

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

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

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

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

@ -0,0 +1,33 @@
@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;
}
<form asp-page="/Admin/Blogs/Create">
<abp-modal size="@(AbpModalSize.Large)">
<abp-modal-header title="@L["Create"].Value"></abp-modal-header>
<abp-modal-body>
<abp-tabs>
<abp-tab title="@L["Info"].Value">
<abp-input asp-for="Blog.Name"/>
<abp-input asp-for="Blog.ShortName"/>
<abp-input asp-for="Blog.Description"/>
</abp-tab>
<abp-tab title="@L["Social"].Value">
<abp-input asp-for="Blog.Facebook" />
<abp-input asp-for="Blog.Twitter" />
<abp-input asp-for="Blog.Instagram" />
<abp-input asp-for="Blog.Github" />
<abp-input asp-for="Blog.StackOverflow" />
</abp-tab>
</abp-tabs>
</abp-modal-body>
<abp-modal-footer buttons="@(AbpModalButtons.Cancel|AbpModalButtons.Save)">
</abp-modal-footer>
</abp-modal>
</form>

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

@ -0,0 +1,65 @@
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; }
[StringLength(BlogConsts.MaxSocialLinkLength)]
public string Facebook { get; set; }
[StringLength(BlogConsts.MaxSocialLinkLength)]
public string Twitter { get; set; }
[StringLength(BlogConsts.MaxSocialLinkLength)]
public string Instagram { get; set; }
[StringLength(BlogConsts.MaxSocialLinkLength)]
public string Github { get; set; }
[StringLength(BlogConsts.MaxSocialLinkLength)]
public string StackOverflow { get; set; }
}
}
}

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

@ -0,0 +1,34 @@
@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;
}
<form asp-page="/Admin/Blogs/Edit">
<abp-modal size="@(AbpModalSize.Large)">
<abp-modal-header title="@L["Edit"].Value"></abp-modal-header>
<abp-modal-body>
<abp-tabs>
<abp-tab title="@L["Info"].Value">
<abp-input asp-for="Blog.Id" />
<abp-input asp-for="Blog.Name" />
<abp-input asp-for="Blog.ShortName" />
<abp-input asp-for="Blog.Description" />
</abp-tab>
<abp-tab title="@L["Social"].Value">
<abp-input asp-for="Blog.Facebook" />
<abp-input asp-for="Blog.Twitter" />
<abp-input asp-for="Blog.Instagram" />
<abp-input asp-for="Blog.Github" />
<abp-input asp-for="Blog.StackOverflow" />
</abp-tab>
</abp-tabs>
</abp-modal-body>
<abp-modal-footer buttons="@(AbpModalButtons.Cancel|AbpModalButtons.Save)">
</abp-modal-footer>
</abp-modal>
</form>

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

@ -0,0 +1,85 @@
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,
Facebook = Blog.Facebook,
Twitter = Blog.Twitter,
Instagram = Blog.Instagram,
Github = Blog.Github,
StackOverflow = Blog.StackOverflow
});
}
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; }
[StringLength(BlogConsts.MaxSocialLinkLength)]
public string Facebook { get; set; }
[StringLength(BlogConsts.MaxSocialLinkLength)]
public string Twitter { get; set; }
[StringLength(BlogConsts.MaxSocialLinkLength)]
public string Instagram { get; set; }
[StringLength(BlogConsts.MaxSocialLinkLength)]
public string Github { get; set; }
[StringLength(BlogConsts.MaxSocialLinkLength)]
public string StackOverflow { get; set; }
}
}
}

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

@ -0,0 +1,45 @@
@page
@using Microsoft.AspNetCore.Authorization
@using Volo.Blogging
@using Volo.Blogging.Pages.Blog
@inherits BloggingPage
@model Volo.Blogging.Pages.Admin.Blogs.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-script src="/Pages/Admin/Blogs/edit.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>

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

@ -0,0 +1,13 @@
using System.Threading.Tasks;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
namespace Volo.Blogging.Pages.Admin.Blogs
{
public class IndexModel : AbpPageModel
{
public async Task OnGetAsync()
{
}
}
}

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
}
};
});

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

@ -0,0 +1,84 @@
$(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,
scrollCollapse: true,
order: [[3, "desc"]],
ajax: abp.libs.datatables.createAjax(volo.blogging.blogs.getListPaged),
columnDefs: [
{
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.Text;
using CommonMark;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Localization;
using Microsoft.AspNetCore.Mvc.Razor.Internal;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
@ -13,6 +16,8 @@ namespace Volo.Blogging.Pages.Blog
public const string DefaultTitle = "Blog";
public const int MaxShortContentLength = 128;
public string GetTitle(string title = null)
{
if (string.IsNullOrWhiteSpace(title))
@ -23,7 +28,50 @@ namespace Volo.Blogging.Pages.Blog
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;
@ -31,37 +79,37 @@ namespace Volo.Blogging.Pages.Blog
if (diffInDays >= 365)
{
return diffInDays / 365 + L["YearsAgo"].Value;
return L["YearsAgo", diffInDays / 365];
}
if (diffInDays >= 30)
{
return diffInDays / 30 + L["MonthsAgo"].Value;
return L["MonthsAgo", diffInDays / 30];
}
if (diffInDays >= 7)
{
return diffInDays / 7 + L["WeeksAgo"].Value;
return L["WeeksAgo", diffInDays / 7];
}
if (diffInDays >= 1)
{
return diffInDays + L["DaysAgo"].Value;
return L["DaysAgo", diffInDays];
}
var diffInSeconds = (int) timeDiff.TotalSeconds;
if (diffInSeconds >= 3600)
{
return diffInSeconds / 3600 + L["HoursAgo"].Value;
return L["HoursAgo", diffInSeconds / 3600];
}
if (diffInSeconds >= 60)
{
return diffInSeconds / 60 + L["MinutesAgo"].Value;
return L["MinutesAgo", diffInSeconds];
}
if (diffInSeconds >= 1)
{
return diffInSeconds + L["SecondsAgo"].Value;
return L["SecondsAgo", diffInSeconds];
}
return L["Now"].Value;
return L["Now"];
}
}
}

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

@ -12,6 +12,6 @@
<ul>
@foreach (var blog in Model.Blogs)
{
<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>

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

@ -1,8 +1,10 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
using Volo.Blogging.Blogs;
using Volo.Blogging.Blogs.Dtos;
namespace Volo.Blogging.Pages.Blog
{
@ -24,7 +26,7 @@ namespace Volo.Blogging.Pages.Blog
if (result.Items.Count == 1)
{
var blog = result.Items[0];
return RedirectToPage("./Posts/Index", new { blogShortName = blog.ShortName});
return RedirectToPage("./Posts/Index", new { blogShortName = blog.ShortName });
}
Blogs = result.Items;

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

@ -1,8 +1,10 @@
@page
@inherits Volo.Blogging.Pages.Blog.BloggingPage
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Http.Extensions
@using Volo.Blogging
@using Volo.Blogging.Pages.Blog.Posts
@using Volo.Blogging.Areas.Blog.Helpers.TagHelpers
@inject IAuthorizationService Authorization
@model DetailModel
@{
@ -20,7 +22,79 @@
</abp-style-bundle>
}
<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">
@Model.Blog.ShortName
<small class="text-muted">
@L["Blog"]
</small>
</h1>
</div>
<div class="article-owner">
<div class="article-infos">
<div class="user-card">
<a>
<strong> @(Model.Post.Writer == null ? "" : Model.Post.Writer.UserName)</strong>, @ConvertDatetimeToTimeAgo(Model.Post.CreationTime)
</a>
<span class="seperator">|</span>
<a>
<i class="fa fa-eye"></i> @L["ReadWithCount", @Model.Post.ReadCount]
</a>
<span class="seperator">|</span>
<a>
<i class="fa fa-comment"></i> @L["CommentWithCount", @Model.CommentCount]
</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>
@if (!string.IsNullOrWhiteSpace(Model.Blog.Facebook))
{
<span class="vs-seperator">|</span>
<a href="https://Facebook.com/@Model.Blog.Facebook">
<i class="fa fa-facebook"></i>
</a>
}
@if (!string.IsNullOrWhiteSpace(Model.Blog.Twitter))
{
<span class="vs-seperator">|</span>
<a href="https://twitter.com/@Model.Blog.Twitter">
<i class="fa fa-twitter"></i>
</a>
}
@if (!string.IsNullOrWhiteSpace(Model.Blog.Instagram))
{
<span class="vs-seperator">|</span>
<a href="https://Instagram.com/@Model.Blog.Instagram">
<i class="fa fa-instagram"></i>
</a>
}
@if (!string.IsNullOrWhiteSpace(Model.Blog.Github))
{
<span class="vs-seperator">|</span>
<a href="https://Github.com/@Model.Blog.Github">
<i class="fa fa-github"></i>
</a>
}
@if (!string.IsNullOrWhiteSpace(Model.Blog.StackOverflow))
{
<span class="vs-seperator">|</span>
<a href="https://StackOverflow.com/@Model.Blog.StackOverflow">
<i class="fa fa-stack-overflow"></i>
</a>
}
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12 mx-auto">
<section class="hero-section">
<div class="hero-articles">
@ -34,28 +108,26 @@
}
</p>
<h1>
<a href="#">@Model.Post.Title</a>
<a asp-page="./Detail" asp-route-postUrl="@Model.Post.Url" asp-route-blogShortName="@Model.BlogShortName">@Model.Post.Title</a>
</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>
<img gravatar-email="@Model.Post.Writer.Email" default-image="Identicon" class="article-avatar" />
</a>
<a href="#">
<strong> Halil İbrahim Kalkan</strong>, @ConvertDatetimeToTimeAgo(Model.Post.CreationTime)
<a>
<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> @Model.Post.ReadCount @L["Read"]
<span class="vs-seperator">|</span>
<a>
<i class="fa fa-eye"></i> @L["ReadWithCount", @Model.Post.ReadCount]
</a>
<span class="seperator">|</span>
<a href="#">
<i class="fa fa-comment"></i> @Model.CommentCount @L["Comment"]
<span class="vs-seperator">|</span>
<a>
<i class="fa fa-comment"></i> @L["CommentWithCount", @Model.CommentCount]
</a>
@if (await Authorization.IsGrantedAsync(BloggingPermissions.Posts.Update))
{
<span class="seperator">|</span>
@ -63,17 +135,16 @@
<i class="fa fa-pencil"></i> @L["Edit"]
</a>
}
@if (await Authorization.IsGrantedAsync(BloggingPermissions.Posts.Delete) || (CurrentUser.Id == Model.Post.CreatorId))
{
<span class="seperator">|</span>
<a href="#" id="DeletePostLink" data-postid="@Model.Post.Id" data-blogShortName="@Model.BlogShortName">
<i class="fa fa-trash"></i> @L["Delete"]
</a>
}
</div>
</div>
</div>
<div class="user-link-icons">
<a class="add-bookmark" href="#">
<i class="fa fa-bookmark"></i>
</a>
<a class="share-article" href="#">
<i class="fa fa-share-alt"></i>
</a>
</div>
</div>
</section>
</div>
@ -82,7 +153,10 @@
<div class="row">
<div class="col-md-8 mx-auto">
<section class="post-content">
<p>@Html.Raw(Model.FormattedContent)</p>
<p>
@Html.Raw(RenderMarkdownToHtml(Model.Post.Content))
</p>
</section>
</div>
@ -97,41 +171,56 @@
<a asp-page="/Blog/Posts/Index" asp-route-blogShortName="@Model.BlogShortName" asp-route-tagName="@tag.Name" class="tag">@tag.Name</a>
}
</div>
<div class="comment-form mt-4">
<div class="clearfix bg-light p-4">
<h3 class="mt-0">@L["LeaveComment"]</h3>
<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>
<a class="btn-rounded float-right btn btn-primary"
href="@Model.GetTwitterShareUrl(Model.Post.Title, HttpContext.Request.GetEncodedUrl(), "@" + (Model.Blog.Twitter??""))" target="_blank">
<i class="fa fa-twitter"></i>
@L["ShareOnTwitter"]
</a>
@if (await Authorization.IsGrantedAsync(BloggingPermissions.Comments.Create))
{
<div class="comment-form mt-4">
<div class="vs-blog-title mb-0">
<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 class="comment-area">
}
<div class="comment-area">
@foreach (var commentWithRepliesDto in Model.CommentsWithReplies)
{
<div class="media">
<img class="d-flex mr-3 rounded-circle comment-avatar" src="https://placeimg.com/120/120/people" alt="">
<img gravatar-email="@commentWithRepliesDto.Comment.Writer.Email" default-image="Identicon" class="d-flex mr-3 rounded-circle comment-avatar" />
<div class="media-body">
<h5 class="comment-owner">
Armağan Ünlü
@(commentWithRepliesDto.Comment.Writer == null ? "" : commentWithRepliesDto.Comment.Writer.UserName)
<span class="float-right">@ConvertDatetimeToTimeAgo(commentWithRepliesDto.Comment.CreationTime)</span>
</h5>
<p id="@commentWithRepliesDto.Comment.Id">
@commentWithRepliesDto.Comment.Text
</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))
{
<span class="seperator">|</span>
@ -149,34 +238,39 @@
}
</div>
<div class="comment-form mt-4 replyForm">
<div class="clearfix bg-light p-4">
<h3 class="mt-0">@L["ReplyTo"] Armağan Ünlü</h3>
<div>
<form method="post">
<input name="postId" value="@Model.Post.Id" hidden />
<input name="repliedCommentId" value="@commentWithRepliesDto.Comment.Id" hidden />
@if (await Authorization.IsGrantedAsync(BloggingPermissions.Comments.Create))
{
<div class="comment-form mt-4 replyForm">
<div class="clearfix bg-light p-4">
<h3 class="mt-0">
@L["ReplyTo"]
@(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">
<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["Comment"].Value" />
</form>
<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["Comment"].Value" />
</form>
</div>
</div>
</div>
</div>
}
@if (await Authorization.IsGrantedAsync(BloggingPermissions.Comments.Update) || (CurrentUser.Id == commentWithRepliesDto.Comment.CreatorId))
{
<div class="comment-form mt-4 editForm">
<div class="clearfix bg-light p-4">
<div>
<form class="editFormClass">
<input name="commentId" value="@commentWithRepliesDto.Comment.Id" hidden/>
<input name="commentId" value="@commentWithRepliesDto.Comment.Id" hidden />
<div class="form-group">
<textarea class="form-control no-border" name="text" id="textBoxId" rows="4">@commentWithRepliesDto.Comment.Text</textarea>
</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>
</div>
</div>
@ -186,24 +280,27 @@
@foreach (var reply in commentWithRepliesDto.Replies)
{
<div class="media">
<img class="d-flex mr-3 rounded-circle answer-avatar" src="https://placeimg.com/120/120/people?t=1535457179534" alt="">
<img gravatar-email="@reply.Writer.Email" default-image="Identicon" class="d-flex mr-3 rounded-circle comment-avatar" />
<div class="media-body">
<h5 class="comment-owner">
Zlatan Ibrahimovic
@(reply.Writer == null ? "" : reply.Writer.UserName)
<span class="float-right">@ConvertDatetimeToTimeAgo(reply.CreationTime)</span>
</h5>
<p id="@reply.Id">
@reply.Text
</p>
<div class="comment-buttons">
<a href="#" class="tag" data-relpyid="@commentWithRepliesDto.Comment.Id">
<i class="fa fa-reply" aria-hidden="true"></i> @L["Reply"]
</a>
<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>
}
@if (await Authorization.IsGrantedAsync(BloggingPermissions.Comments.Delete) || (CurrentUser.Id == commentWithRepliesDto.Comment.CreatorId))
{
<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"]
</a>
}
@ -211,39 +308,44 @@
@if (await Authorization.IsGrantedAsync(BloggingPermissions.Comments.Update) || (CurrentUser.Id == commentWithRepliesDto.Comment.CreatorId))
{
<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"]
</a>
}
</div>
<div class="comment-form mt-4 replyForm">
<div class="clearfix bg-light p-4">
<h3 class="mt-0">@L["ReplyTo"] Armağan Ünlü</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">
<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>
@if (await Authorization.IsGrantedAsync(BloggingPermissions.Comments.Create))
{
<div class="comment-form mt-4 replyForm">
<div class="clearfix bg-light p-4">
<h3 class="mt-0">
@L["ReplyTo"]
@(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">
<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>
}
@if (await Authorization.IsGrantedAsync(BloggingPermissions.Comments.Update) || (CurrentUser.Id == commentWithRepliesDto.Comment.CreatorId))
{
<div class="comment-form mt-4 editForm">
<div class="clearfix bg-light p-4">
<div>
<form class="editFormClass">
<input name="commentId" value="@reply.Id" hidden/>
<input name="commentId" value="@reply.Id" hidden />
<div class="form-group">
<textarea class="form-control no-border" name="text" id="textBoxId" rows="4">@reply.Text</textarea>
</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>
</div>
</div>

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

@ -1,20 +1,21 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using CommonMark;
using Microsoft.AspNetCore.Html;
using System.Web;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
using Volo.Blogging.Blogs;
using Volo.Blogging.Blogs.Dtos;
using Volo.Blogging.Comments;
using Volo.Blogging.Comments.Dtos;
using Volo.Blogging.Posts;
namespace Volo.Blogging.Pages.Blog.Posts
{
public class DetailModel : PageModel
public class DetailModel : AbpPageModel
{
private const int TwitterLinkLength = 23;
private readonly IPostAppService _postAppService;
private readonly IBlogAppService _blogAppService;
private readonly ICommentAppService _commentAppService;
@ -27,13 +28,11 @@ namespace Volo.Blogging.Pages.Blog.Posts
[BindProperty]
public PostDetailsViewModel NewComment { get; set; }
public int CommentCount { get; set; }
public PostWithDetailsDto Post { get; set; }
public IHtmlContent FormattedContent { get; set; }
public IReadOnlyList<CommentWithRepliesDto> CommentsWithReplies { get; set; }
public BlogDto Blog { get; set; }
@ -45,12 +44,12 @@ namespace Volo.Blogging.Pages.Blog.Posts
_commentAppService = commentAppService;
}
public async void OnGetAsync()
public async Task OnGetAsync()
{
await GetData();
}
public async void OnPostAsync()
public async Task OnPostAsync()
{
await _commentAppService.CreateAsync(new CreateCommentDto()
{
@ -62,18 +61,10 @@ namespace Volo.Blogging.Pages.Blog.Posts
await GetData();
}
public async void OnDeleteAsync(Guid commentId)
{
await _commentAppService.DeleteAsync(commentId);
await GetData();
}
private async Task GetData()
{
Blog = await _blogAppService.GetByShortNameAsync(BlogShortName);
Post = await _postAppService.GetForReadingAsync(new GetPostInput { BlogId = Blog.Id, Url = PostUrl });
FormattedContent = RenderMarkdown(Post.Content);
CommentsWithReplies = await _commentAppService.GetHierarchicalListOfPostAsync(new GetCommentListOfPostAsync() { PostId = Post.Id });
CountComments();
}
@ -87,14 +78,19 @@ namespace Volo.Blogging.Pages.Blog.Posts
}
}
public IHtmlContent RenderMarkdown(string content)
public string GetTwitterShareUrl(string title, string url, string linkedAccounts)
{
byte[] bytes = Encoding.Default.GetBytes(content);
var utf8Content = Encoding.UTF8.GetString(bytes);
var readAtString = " | Read More At ";
var otherCharsLength = (readAtString + linkedAccounts).Length + 1;
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);
return new HtmlString(html);
return (new UriBuilder("https://twitter.com/intent/tweet") { Query = "text=" + HttpUtility.UrlEncode(text) }).ToString();
}
public class PostDetailsViewModel

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

@ -29,7 +29,7 @@ namespace Volo.Blogging.Pages.Blog.Posts
_blogAppService = blogAppService;
}
public async void OnGet()
public async Task OnGet()
{
var postDto = await _postAppService.GetAsync(new Guid(PostId));
Post = ObjectMapper.Map<PostWithDetailsDto, EditPostViewModel>(postDto);

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

@ -24,72 +24,119 @@
}
<div class="vs-blog">
<div class="container hero-container pb-5 bg-white">
<div class="vs-blog-header">
<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>
}
@if (!string.IsNullOrWhiteSpace(Model.Blog.Facebook))
{
<span class="vs-seperator">|</span>
<a href="https://Facebook.com/@Model.Blog.Facebook">
<i class="fa fa-facebook"></i>
</a>
}
@if (!string.IsNullOrWhiteSpace(Model.Blog.Twitter))
{
<span class="vs-seperator">|</span>
<a href="https://twitter.com/@Model.Blog.Twitter">
<i class="fa fa-twitter"></i>
</a>
}
@if (!string.IsNullOrWhiteSpace(Model.Blog.Instagram))
{
<span class="vs-seperator">|</span>
<a href="https://Instagram.com/@Model.Blog.Instagram">
<i class="fa fa-instagram"></i>
</a>
}
@if (!string.IsNullOrWhiteSpace(Model.Blog.Github))
{
<span class="vs-seperator">|</span>
<a href="https://Github.com/@Model.Blog.Github">
<i class="fa fa-github"></i>
</a>
}
@if (!string.IsNullOrWhiteSpace(Model.Blog.StackOverflow))
{
<span class="vs-seperator">|</span>
<a href="https://StackOverflow.com/@Model.Blog.StackOverflow">
<i class="fa fa-stack-overflow"></i>
</a>
}
</div>
</div>
</div>
</div>
<div class="pb-3">
<div class="row">
<div class="col-md-8">
<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++)
{
var post = Model.Posts[index];
<div class="item">
<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">
</div>
<div class="article-owner">
<div class="article-infos">
<div class="user-card">
<a>
<img gravatar-email="@post.Writer.Email" default-image="Identicon" class="article-avatar" />
</a>
<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>
</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>
}
</section>
</div>
<div class="col-md-4">
<h2>@L["WhatIsNew"]</h2>
<div class="list-group">
@for (var index = 0; index < Model.Posts.Count && index < 3; index++)
{
var post = Model.Posts[index];
@ -104,14 +151,14 @@
</div>
</div>
<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>
</h3>
<div class="article-owner">
<div class="article-infos">
<div class="user-card">
<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>
</div>
</div>
@ -122,116 +169,88 @@
</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 class="container last-post-section pb-5">
<div class="pb-5">
<div class="row">
<div class="col-md-8">
<div class="row">
<div class="col">
<div class="titline">
<h2>@L["LastPosts"]</h2>
</div>
</div>
<div class="col-md-8 box-articles">
<div class="vs-blog-title">
<h2>@L["LastPosts"]</h2>
</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++)
{
var post = Model.Posts[index];
var oddPost = index % 2 == 1;
<section class="box-articles">
<div class="row @(oddPost?"align-middle":"")">
<div class="col-md-4 order-md-@(oddPost?"last":"first")">
<div class="img-container">
<a asp-page="./Detail" asp-route-postUrl="@post.Url" asp-route-blogShortName="@Model.BlogShortName">
<img src="@post.CoverImage" class="box-article-img">
</a>
<div class="list-group-item">
<div class="row @(oddPost?"align-middle":"")">
<div class="col-md-4 order-md-@(oddPost?"last":"first")">
<div class="img-container">
<a asp-page="./Detail" asp-route-postUrl="@post.Url" asp-route-blogShortName="@Model.BlogShortName">
<img src="@post.CoverImage" class="box-article-img">
</a>
</div>
</div>
</div>
<div class="col-md-8 order-md-@(oddPost?"first":"last")">
<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>
<h3>
<a asp-page="./Detail" asp-route-postUrl="@post.Url" asp-route-blogShortName="@Model.BlogShortName">@post.Title</a>
</h3>
<p>
@(post.Content == null ? "" : (post.Content.Length > 250 ? post.Content.Substring(0, 150) : post.Content))...
</p>
<div class="article-owner">
<div class="article-infos">
<div class="user-card">
<a href="#">
<img src="https://placeimg.com/120/120/pople" 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
</a>
<span class="seperator">|</span>
<a>
<i class="fa fa-comment"></i> @post.CommentCount
</a>
<div class="col order-md-@(oddPost?"first":"last")">
<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>
<h3>
<a asp-page="./Detail" asp-route-postUrl="@post.Url" asp-route-blogShortName="@Model.BlogShortName">@post.Title</a>
</h3>
<p>
@(GetShortContent(post.Content))
</p>
<div class="article-owner">
<div class="article-infos">
<div class="user-card">
<a>
<img gravatar-email="@post.Writer.Email" default-image="Identicon" class="article-avatar" />
</a>
<a>
<strong>@post.Writer.UserName</strong>, @ConvertDatetimeToTimeAgo(post.CreationTime)
</a>
<span class="vs-seperator">|</span>
<a>
<i class="fa fa-eye"></i> @post.ReadCount
</a>
<span class="vs-seperator">|</span>
<a>
<i class="fa fa-comment"></i> @post.CommentCount
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
}
}
</div>
</div>
<div class="col-md-4">
<div class="pb-5 sidebox" id="sidebar">
<div class="titline">
<h2>@L["PopularTags"]</h2>
</div>
<div class="sideboxes">
<div class="popular-tags">
@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>
}
<div class="vs-blog-title">
<h2>@L["PopularTags"]</h2>
</div>
<div class="list-group small-list popular-tags">
@foreach (var popularTag in Model.PopularTags)
{
<div class="list-group-item">
<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>
<hr>
<footer>
<a href="#">About</a>
<span class="seperator">|</span>
<a href="#">Privacy Policy</a>
<span class="seperator">|</span>
<a href="#">Terms</a>
}
<p>
Copyright Volosoft - Blog
</p>
</footer>
</div>
</div>
</div>
</div>
</div>
</div>

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

@ -2,14 +2,16 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
using Volo.Blogging.Blogs;
using Volo.Blogging.Blogs.Dtos;
using Volo.Blogging.Posts;
using Volo.Blogging.Tagging;
using Volo.Blogging.Tagging.Dtos;
namespace Volo.Blogging.Pages.Blog.Posts
{
public class IndexModel : PageModel
public class IndexModel : AbpPageModel
{
private readonly IPostAppService _postAppService;
private readonly IBlogAppService _blogAppService;
@ -21,6 +23,8 @@ namespace Volo.Blogging.Pages.Blog.Posts
[BindProperty(SupportsGet = true)]
public string TagName { get; set; }
public BlogDto Blog { get; set; }
public IReadOnlyList<PostWithDetailsDto> Posts { get; set; }
public IReadOnlyList<TagDto> PopularTags { get; set; }
@ -34,8 +38,8 @@ namespace Volo.Blogging.Pages.Blog.Posts
public async Task OnGetAsync()
{
var blog = await _blogAppService.GetByShortNameAsync(BlogShortName);
Posts = (await _postAppService.GetListByBlogIdAndTagName(blog.Id, TagName)).Items;
Blog = await _blogAppService.GetByShortNameAsync(BlogShortName);
Posts = (await _postAppService.GetListByBlogIdAndTagName(Blog.Id, TagName)).Items;
PopularTags = (await _tagAppService.GetPopularTags(new GetPopularTagsInput {ResultCount = 10}));
}
}

3
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 Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
using Volo.Blogging.Blogs;
using Volo.Blogging.Blogs.Dtos;
using Volo.Blogging.Posts;
namespace Volo.Blogging.Pages.Blog.Posts
@ -27,7 +28,7 @@ namespace Volo.Blogging.Pages.Blog.Posts
_blogAppService = blogAppService;
}
public async void OnGetAsync()
public async Task OnGetAsync()
{
Blog = await _blogAppService.GetByShortNameAsync(BlogShortName);
Post = new CreatePostViewModel

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

@ -1,5 +1,7 @@
(function ($) {
var l = abp.localization.getResource('Blogging');
$('div .replyForm').hide();
$('div .editForm').hide();
@ -25,6 +27,7 @@
$('.replyLink').click(function (event) {
event.preventDefault();
$('div .editForm').hide();
var linkElement = $(this);
var replyCommentId = linkElement.attr('data-relpyid');
@ -48,8 +51,8 @@
if (deleteCommentId != '' && deleteCommentId !== undefined) {
abp.message.confirm(
'Comment will be deleted.', // TODO: localize
'Are you sure?',
l('CommentDeletionWarningMessage'), // TODO: localize
l('Are you sure?'),
function(isConfirmed) {
if (isConfirmed) {
$.ajax({
@ -66,8 +69,39 @@
}
});
$('#DeletePostLink').click(function (event) {
event.preventDefault();
var linkElement = $(this);
var deleteCommentId = linkElement.attr('data-postid');
var blogShortName = linkElement.attr('data-blogShortName');
if (deleteCommentId != '' && deleteCommentId !== undefined) {
abp.message.confirm(
l('PostDeletionWarningMessage'), // TODO: localize
l('AreYouSure'),
function(isConfirmed) {
if (isConfirmed) {
$.ajax({
type: "POST",
url: "/Blog/Posts/Delete",
data: { id: deleteCommentId },
success: function () {
window.location.replace('/Blog/' + blogShortName);
}
});
}
}
);
}
});
$('#DeletePostRouteLink').click(function (event) {
console.log("goooo");
});
$('.updateLink').click(function (event) {
event.preventDefault();
$('div .replyForm').hide();
var linkElement = $(this);
var updateCommentId = $(this).attr('data-updateid');

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save