├── src └── YuukoBlog │ ├── runtimeconfig.template.json │ ├── Views │ ├── Default │ │ ├── _ViewStart.cshtml │ │ ├── template.json │ │ ├── Shared │ │ │ ├── About.cshtml │ │ │ ├── Tag.cshtml │ │ │ ├── Catalog.cshtml │ │ │ ├── Sidebar.cshtml │ │ │ ├── Calendar.cshtml │ │ │ ├── Roll.cshtml │ │ │ ├── Prompt.cshtml │ │ │ ├── Admin.cshtml │ │ │ ├── PostInfo.cshtml │ │ │ ├── Home.cshtml │ │ │ ├── Post.cshtml │ │ │ └── _Layout.cshtml │ │ └── Admin │ │ │ ├── Login.cshtml │ │ │ ├── Index.cshtml │ │ │ └── Catalog.cshtml │ ├── Hexo │ │ ├── _ViewStart.cshtml │ │ ├── template.json │ │ ├── Shared │ │ │ ├── Prompt.cshtml │ │ │ ├── Home.cshtml │ │ │ ├── Post.cshtml │ │ │ └── _Layout.cshtml │ │ └── Admin │ │ │ ├── Login.cshtml │ │ │ ├── Catalog.cshtml │ │ │ └── Index.cshtml │ ├── Moon │ │ ├── _ViewStart.cshtml │ │ ├── template.json │ │ ├── Shared │ │ │ ├── Prompt.cshtml │ │ │ ├── Home.cshtml │ │ │ ├── Post.cshtml │ │ │ └── _Layout.cshtml │ │ └── Admin │ │ │ ├── Login.cshtml │ │ │ ├── Index.cshtml │ │ │ └── Catalog.cshtml │ └── _ViewImports.cshtml │ ├── Localization │ ├── en-US.json │ └── zh-CN.json │ ├── wwwroot │ ├── assets │ │ ├── Hexo │ │ │ ├── img │ │ │ │ ├── mail.png │ │ │ │ ├── mvp.png │ │ │ │ ├── github.png │ │ │ │ ├── linkedin.png │ │ │ │ └── scrollbar_arrow.png │ │ │ ├── fonts │ │ │ │ ├── FontAwesome.otf │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ ├── fontawesome-webfont.woff │ │ │ │ └── fontawesome-webfont.woff2 │ │ │ ├── js │ │ │ │ ├── pc.js │ │ │ │ ├── mobile.js │ │ │ │ ├── jquery.lazyload.js │ │ │ │ └── main.js │ │ │ └── css │ │ │ │ └── fancybox.css │ │ ├── Default │ │ │ ├── images │ │ │ │ ├── mcp.png │ │ │ │ ├── ms.png │ │ │ │ ├── mvp.png │ │ │ │ ├── accd.png │ │ │ │ ├── azure.png │ │ │ │ ├── mcsd.png │ │ │ │ ├── qzone.png │ │ │ │ ├── acpe-dw.png │ │ │ │ ├── acpe-fl.png │ │ │ │ ├── acpe-fw.png │ │ │ │ ├── acpe-ps.png │ │ │ │ ├── texture.png │ │ │ │ ├── wechat.png │ │ │ │ └── blog-roll.png │ │ │ ├── fonts │ │ │ │ ├── bwicon.eot │ │ │ │ ├── ptsans.eot │ │ │ │ ├── bwicon.woff │ │ │ │ ├── opensans.eot │ │ │ │ ├── oxygenmono.eot │ │ │ │ ├── opensans-light.eot │ │ │ │ ├── opensans-light.ttf │ │ │ │ └── opensans-light.woff │ │ │ ├── scripts │ │ │ │ └── codemirror │ │ │ │ │ └── tablist.js │ │ │ └── styles │ │ │ │ └── simplemde.css │ │ └── Moon │ │ │ ├── images │ │ │ ├── header_bg.png │ │ │ ├── header_left.png │ │ │ ├── search_bg.png │ │ │ ├── search_icon.png │ │ │ ├── header_bright.jpg │ │ │ └── header_bright.psd │ │ │ └── scripts │ │ │ ├── jquery.validate.unobtrusive.min.js │ │ │ └── Main.js │ └── web.config │ ├── Models │ ├── TagViewModel.cs │ ├── CalendarViewModel.cs │ ├── CatalogViewModel.cs │ ├── BlogRollViewModel.cs │ ├── SampleData.cs │ ├── PostTag.cs │ ├── Catalog.cs │ ├── PostViewModel.cs │ ├── Config.cs │ ├── BlogRoll.cs │ ├── Post.cs │ └── BlogContext.cs │ ├── Helpers │ ├── RawHelper.cs │ ├── AdminHelper.cs │ └── TagHelper.cs │ ├── config.sqlite.json │ ├── config.json │ ├── web.config │ ├── Properties │ ├── launchSettings.json │ └── PublishProfiles │ │ └── Publish-publish.ps1 │ ├── Filters │ ├── AdminRequiredAttribute.cs │ └── GuestRequiredAttribute.cs │ ├── YuukoBlog.csproj │ ├── Extensions │ └── SessionUploadAuthorization.cs │ ├── YuukoBlog.xproj │ ├── project.json │ ├── Controllers │ ├── PostController.cs │ ├── BaseController.cs │ ├── HomeController.cs │ └── AdminController.cs │ ├── Startup.cs │ └── Jobs │ └── GitHubRelationJob.cs ├── test └── YuukoBlog.Tests │ ├── project.json │ ├── Properties │ └── AssemblyInfo.cs │ ├── YuukoBlog.Tests.csproj │ └── YuukoBlog.Tests.xproj ├── README.md ├── LICENSE ├── YuukoBlog.sln └── .gitignore /src/YuukoBlog/runtimeconfig.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "gcServer": true 3 | } -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Default/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Hexo/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /src/YuukoBlog/Localization/en-US.json: -------------------------------------------------------------------------------- 1 | { 2 | "_DATE_FORMAT_STRING": "MMM yyyy" 3 | } -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Moon/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "~/Views/" + Template.Current.Identifier + "/Shared/_Layout.cshtml"; 3 | } -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Hexo/img/mail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Hexo/img/mail.png -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Hexo/img/mvp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Hexo/img/mvp.png -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Hexo/img/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Hexo/img/github.png -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Default/images/mcp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Default/images/mcp.png -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Default/images/ms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Default/images/ms.png -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Default/images/mvp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Default/images/mvp.png -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Hexo/img/linkedin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Hexo/img/linkedin.png -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Default/fonts/bwicon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Default/fonts/bwicon.eot -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Default/fonts/ptsans.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Default/fonts/ptsans.eot -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Default/images/accd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Default/images/accd.png -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Default/images/azure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Default/images/azure.png -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Default/images/mcsd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Default/images/mcsd.png -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Default/images/qzone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Default/images/qzone.png -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Default/fonts/bwicon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Default/fonts/bwicon.woff -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Default/fonts/opensans.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Default/fonts/opensans.eot -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Default/images/acpe-dw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Default/images/acpe-dw.png -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Default/images/acpe-fl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Default/images/acpe-fl.png -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Default/images/acpe-fw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Default/images/acpe-fw.png -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Default/images/acpe-ps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Default/images/acpe-ps.png -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Default/images/texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Default/images/texture.png -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Default/images/wechat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Default/images/wechat.png -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Hexo/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Hexo/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Moon/images/header_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Moon/images/header_bg.png -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Moon/images/header_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Moon/images/header_left.png -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Moon/images/search_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Moon/images/search_bg.png -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Moon/images/search_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Moon/images/search_icon.png -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Default/fonts/oxygenmono.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Default/fonts/oxygenmono.eot -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Default/images/blog-roll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Default/images/blog-roll.png -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Hexo/img/scrollbar_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Hexo/img/scrollbar_arrow.png -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Moon/images/header_bright.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Moon/images/header_bright.jpg -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Moon/images/header_bright.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Moon/images/header_bright.psd -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Default/fonts/opensans-light.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Default/fonts/opensans-light.eot -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Default/fonts/opensans-light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Default/fonts/opensans-light.ttf -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Default/fonts/opensans-light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Default/fonts/opensans-light.woff -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Hexo/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Hexo/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Hexo/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Hexo/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Hexo/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Hexo/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Hexo/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukozh/YuukoBlog-NETCore-MySql/HEAD/src/YuukoBlog/wwwroot/assets/Hexo/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /src/YuukoBlog/Models/TagViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace YuukoBlog.Models 2 | { 3 | public class TagViewModel 4 | { 5 | public string Title { get; set; } 6 | public int Count { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Moon/template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Title": "Moon", 3 | "IsDefault": false, 4 | "Author": "Breeswish", 5 | "License": "https://breeswish.org", 6 | "Version": "2.0.0", 7 | "Description": "月光模板" 8 | } 9 | -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Hexo/template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Title": "Hexo", 3 | "IsDefault": false, 4 | "Author": "Xuanwo", 5 | "License": "https://github.com/hexojs/hexo", 6 | "Version": "1.0.0", 7 | "Description": "Hexo" 8 | } 9 | -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Default/template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Title": "Default Template", 3 | "IsDefault": true, 4 | "Author": "Breeswish", 5 | "License": "https://breeswish.org", 6 | "Version": "2.0.0", 7 | "Description": "全局Ajax的简约博客模板" 8 | } 9 | -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Moon/Shared/Prompt.cshtml: -------------------------------------------------------------------------------- 1 | @model Prompt 2 |
3 |
4 |
@Model.Title
5 |
6 |
7 | @Model.Details 8 |
9 |
-------------------------------------------------------------------------------- /src/YuukoBlog/Models/CalendarViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace YuukoBlog.Models 2 | { 3 | public class CalendarViewModel 4 | { 5 | public int Month { get; set; } 6 | public int Year { get; set; } 7 | public int Count { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/YuukoBlog/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using YuukoBlog.Models 2 | @using Microsoft.AspNetCore.Html 3 | @using Microsoft.AspNetCore.Http 4 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 5 | @inject TemplateManager Template 6 | @inject Pomelo.AspNetCore.Localization.IStringReader SR -------------------------------------------------------------------------------- /src/YuukoBlog/Models/CatalogViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace YuukoBlog.Models 4 | { 5 | public class CatalogViewModel 6 | { 7 | public Guid Id { get; set; } 8 | public string Url { get; set; } 9 | public string Title { get; set; } 10 | public int Count { get; set; } 11 | public int PRI { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Default/Shared/About.cshtml: -------------------------------------------------------------------------------- 1 | @if (Model != null && Model.GetType() != typeof(Post)) 2 | { 3 | 10 | } 11 | -------------------------------------------------------------------------------- /src/YuukoBlog/Helpers/RawHelper.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.AspNetCore.Mvc.Rendering 2 | { 3 | public static class RawHelper 4 | { 5 | public static bool IsRaw(this IHtmlHelper self) 6 | { 7 | if (self.ViewContext.HttpContext.Request.Query["raw"] == "true") 8 | return true; 9 | else 10 | return false; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/YuukoBlog/Models/BlogRollViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace YuukoBlog.Models 7 | { 8 | public class BlogRollViewModel 9 | { 10 | public string Name { get; set; } 11 | 12 | public Guid AvatarId { get; set; } 13 | 14 | public string URL { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Default/Shared/Tag.cshtml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Default/Shared/Catalog.cshtml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Default/Shared/Sidebar.cshtml: -------------------------------------------------------------------------------- 1 |
2 | 9 | @Html.Partial("About") 10 | @Html.Partial("Roll") 11 |
12 |
13 | -------------------------------------------------------------------------------- /src/YuukoBlog/Helpers/AdminHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | 3 | namespace Microsoft.AspNetCore.Mvc.Rendering 4 | { 5 | public static class AdminHelper 6 | { 7 | public static bool IsAdmin(this IHtmlHelper self) 8 | { 9 | if (self.ViewContext.HttpContext.Session.GetString("Admin") == "true") 10 | return true; 11 | else 12 | return false; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/YuukoBlog/Models/SampleData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Microsoft.Extensions.DependencyInjection; 4 | 5 | namespace YuukoBlog.Models 6 | { 7 | public static class SampleData 8 | { 9 | public static async Task InitializeYuukoBlog(IServiceProvider serviceProvider) 10 | { 11 | var db = serviceProvider.GetService(); 12 | await db.Database.EnsureCreatedAsync(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/YuukoBlog.Tests/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0-*", 3 | "testRunner": "xunit", 4 | "dependencies": { 5 | "xunit": "2.2.0-beta2-build3300", 6 | "dotnet-test-xunit": "2.2.0-preview2-build1029" 7 | }, 8 | "frameworks": { 9 | "net451": { }, 10 | "netcoreapp1.0": { 11 | "dependencies": { 12 | "Microsoft.NETCore.App": { 13 | "type": "platform", 14 | "version": "1.0.0" 15 | } 16 | } 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Default/Shared/Calendar.cshtml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/YuukoBlog/Models/PostTag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | 5 | namespace YuukoBlog.Models 6 | { 7 | public class PostTag 8 | { 9 | public int Id { get; set; } 10 | 11 | [ForeignKey("Post")] 12 | public Guid PostId { get; set; } 13 | 14 | public virtual Post Post { get; set; } 15 | 16 | [MaxLength(64)] 17 | public string Tag { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/YuukoBlog/Helpers/TagHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using YuukoBlog.Models; 3 | 4 | namespace Microsoft.AspNetCore.Mvc.Rendering 5 | { 6 | public static class TagHelper 7 | { 8 | public static string TagSerialize(this IHtmlHelper self, IEnumerable Tags) 9 | { 10 | var ret = ""; 11 | foreach (var t in Tags) 12 | ret += t.Tag + ", "; 13 | return ret.TrimEnd(' ').TrimEnd(','); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/YuukoBlog/Models/Catalog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | namespace YuukoBlog.Models 6 | { 7 | public class Catalog 8 | { 9 | public Guid Id { get; set; } 10 | 11 | [MaxLength(32)] 12 | public string Url { get; set; } 13 | 14 | public string Title { get; set; } 15 | 16 | public int PRI { get; set; } 17 | 18 | public virtual ICollection Posts { get; set; } = new List(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Hexo/Shared/Prompt.cshtml: -------------------------------------------------------------------------------- 1 | @model Prompt 2 |
3 |
4 | 5 |
6 |

7 | @Model.Title 8 |

9 |
10 |
11 | @Model.Details 12 |
13 |
14 |
-------------------------------------------------------------------------------- /src/YuukoBlog/config.sqlite.json: -------------------------------------------------------------------------------- 1 | { 2 | "AvatarUrl": "https://avatars3.githubusercontent.com/u/2216750?v=3&s=460", 3 | "AboutUrl": "/about", 4 | "Account": "root", 5 | "Password": "123456", 6 | "Site": "あまみや ゆうこ", 7 | "Description": "Amamiya Yuuko's ebullient future", 8 | "Disqus": "yuuko", 9 | "DefaultTemplate": "Default", 10 | "Database": { 11 | "Type": "SQLite", 12 | "ConnectionString": "Data source=yuuko.db" 13 | }, 14 | "BlogRoll": { 15 | "GitHub": "Kagamine", 16 | "Follower": true, 17 | "Following": false 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/YuukoBlog/Models/PostViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace YuukoBlog.Models 5 | { 6 | public class PostViewModel 7 | { 8 | public Guid Id { get; set; } 9 | public string Title { get; set; } 10 | public DateTime Time { get; set; } 11 | public string Summary { get; set; } 12 | public List Tags { get; set; } 13 | public Catalog Catalog { get; set; } 14 | public Guid? CatalogId { get; set; } 15 | public string Url { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/YuukoBlog/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "AvatarUrl": "https://avatars3.githubusercontent.com/u/2216750?v=3&s=460", 3 | "AboutUrl": "/about", 4 | "Account": "root", 5 | "Password": "123456", 6 | "Site": "あまみや ゆうこ", 7 | "Description": "Amamiya Yuuko's ebullient future", 8 | "Disqus": "yuuko", 9 | "DefaultTemplate": "Default", 10 | "Database": { 11 | "Type": "MySQL", 12 | "ConnectionString": "Server=localhost; Uid=root; Pwd=123456; Database=yuuko" 13 | }, 14 | "BlogRoll": { 15 | "GitHub": "Kagamine", 16 | "Follower": true, 17 | "Following": false 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/YuukoBlog/Models/Config.cs: -------------------------------------------------------------------------------- 1 | namespace YuukoBlog.Models 2 | { 3 | public class Config 4 | { 5 | public string Account { get; set; } 6 | public string Password { get; set; } 7 | public string Site { get; set; } 8 | public string Description { get; set; } 9 | public string Disqus { get; set; } 10 | public string AvatarUrl { get; set; } 11 | public string AboutUrl { get; set; } 12 | public string GitHub { get; set; } 13 | public bool Follower { get; set; } 14 | public bool Following { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/YuukoBlog/web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Moon/Admin/Login.cshtml: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
登录
5 |
6 |
7 |

用户名

8 |

9 | 10 |

11 |

密码

12 |

13 | 14 |

15 |

16 | 17 |

18 |
19 |
20 |
-------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Yuuko Blog 2 | 3 | A light-weight single user blog system which based on ASP.Net Core 2.0.0-preview2 4 | 5 | - ASP.Net Core 2.0.0-preview2 6 | - Pomelo.EntityFrameworkCore.MySql 2.0.0-preview2 7 | - Microsoft.EntityFrameworkCore.Sqlite 2.0.0-preview2 8 | - ASP.Net MVC Core 2.0.0-preview2 9 | - Pomelo Extensions for .Net Core 2.0.0-preview2 10 | 11 | ![image](https://cloud.githubusercontent.com/assets/2216750/15952346/94f0e3e2-2ef2-11e6-9e31-9fbc6e389c36.png) 12 | 13 | ![image](https://cloud.githubusercontent.com/assets/2216750/15952645/5b4ecf16-2ef5-11e6-9da7-40a6730d07d8.png) 14 | 15 | ![image](https://cloud.githubusercontent.com/assets/2216750/17664138/88d852f0-6324-11e6-848b-6d594b146af6.png) 16 | -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Default/Shared/Roll.cshtml: -------------------------------------------------------------------------------- 1 | @if (Model != null && Model.GetType() != typeof(Post)) 2 | { 3 | 14 | } -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Default/Shared/Prompt.cshtml: -------------------------------------------------------------------------------- 1 | @model Prompt 2 | @if (Html.IsRaw()) 3 | { 4 | @(new HtmlString("
")) 5 |
6 |
@Model.Title - @ViewBag.Description
7 |
home
8 |
9 |

@Model.Title

10 |
11 |
12 | } 13 |
14 |
15 |
16 |
17 | @Model.Details 18 |
19 |
20 |
21 | @Html.Partial("Sidebar"); 22 |
23 | @if (Html.IsRaw()) 24 | { 25 | @(new HtmlString("
")) 26 | } -------------------------------------------------------------------------------- /src/YuukoBlog/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:48397/", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "YuukoBlog": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "launchUrl": "http://localhost:80", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/YuukoBlog/Models/BlogRoll.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | using Pomelo.AspNetCore.Extensions.BlobStorage.Models; 5 | 6 | namespace YuukoBlog.Models 7 | { 8 | public enum BlogRollType 9 | { 10 | Following, 11 | Follower 12 | } 13 | 14 | public class BlogRoll 15 | { 16 | public Guid Id { get; set; } 17 | 18 | [MaxLength(64)] 19 | public string GitHubId { get; set; } 20 | 21 | [MaxLength(64)] 22 | public string NickName { get; set; } 23 | 24 | public BlogRollType Type { get; set; } 25 | 26 | [ForeignKey("Avatar")] 27 | public Guid? AvatarId { get; set; } 28 | 29 | public virtual Blob Avatar { get; set; } 30 | 31 | [MaxLength(128)] 32 | public string URL { get; set; } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/YuukoBlog.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("")] 10 | [assembly: AssemblyProduct("YuukoBlog.Tests")] 11 | [assembly: AssemblyTrademark("")] 12 | 13 | // Setting ComVisible to false makes the types in this assembly not visible 14 | // to COM components. If you need to access a type in this assembly from 15 | // COM, set the ComVisible attribute to true on that type. 16 | [assembly: ComVisible(false)] 17 | 18 | // The following GUID is for the ID of the typelib if this project is exposed to COM 19 | [assembly: Guid("39257959-3ccf-4aa2-a4a8-e565bfe36699")] 20 | -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Default/Shared/Admin.cshtml: -------------------------------------------------------------------------------- 1 | @if (Html.IsAdmin()) 2 | { 3 | 18 | } -------------------------------------------------------------------------------- /test/YuukoBlog.Tests/YuukoBlog.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | YuukoBlog.Tests 6 | YuukoBlog.Tests 7 | true 8 | 2.0.0-preview2-25407-01 9 | false 10 | false 11 | false 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Hexo/Admin/Login.cshtml: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |
6 |

7 | 登录 8 |

9 |
10 |
11 |

用户名

12 |

13 | 14 |

15 |

密码

16 |

17 | 18 |

19 |

20 | 21 |

22 |
23 |
24 |
25 |
26 | -------------------------------------------------------------------------------- /src/YuukoBlog/Filters/AdminRequiredAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.AspNetCore.Mvc.Filters; 4 | using Microsoft.AspNetCore.Http; 5 | 6 | namespace YuukoBlog.Filters 7 | { 8 | public class AdminRequiredAttribute : ActionFilterAttribute 9 | { 10 | public override void OnActionExecuting(ActionExecutingContext context) 11 | { 12 | if (context.HttpContext.Session.GetString("Admin") != "true") 13 | context.Result = new RedirectResult("/Admin/Login"); 14 | else 15 | base.OnActionExecuting(context); 16 | } 17 | 18 | public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) 19 | { 20 | if (context.HttpContext.Session.GetString("Admin") != "true") 21 | { 22 | context.Result = new RedirectResult("/Admin/Login"); 23 | return Task.FromResult(0); 24 | } 25 | return base.OnActionExecutionAsync(context, next); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Pomelo Foundation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/YuukoBlog.Tests/YuukoBlog.Tests.xproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14.0.25420 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | e1e89de4-6946-4185-97df-a48c3631234b 10 | YuukoBlog.Tests 11 | .\obj 12 | .\bin\ 13 | 14 | 15 | 16 | 2.0 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/YuukoBlog/Localization/zh-CN.json: -------------------------------------------------------------------------------- 1 | { 2 | "Not Found": "没有找到相关资源", 3 | "home": "首页", 4 | "Read More": "更多内容", 5 | "Untitled Post": "新建文章", 6 | "New Catalog": "新建分类", 7 | "The resources have not been found, please check your request.": "您请求的资源没有找到,请检查请求是否正确。", 8 | "Back to home": "返回首页", 9 | "Admin Panel": "管理面板", 10 | "Title": "标题", 11 | "Sub Title": "英文标题", 12 | "Order": "顺序", 13 | "Control": "操作", 14 | "Site name": "站点名称", 15 | "Description": "介绍", 16 | "Avatar URL": "头像URL", 17 | "About-me URL": "关于我URL", 18 | "User Name": "用户名", 19 | "Password": "密码", 20 | "Save": "保存", 21 | "Settings": "参数设定", 22 | "New Post": "新建文章", 23 | "Manage Catalogs": "管理分类", 24 | "Sign Out": "注销登录", 25 | "_DATE_FORMAT_STRING": "yyyy 年 MM 月", 26 | "Is a page": "是否为页面", 27 | "Cancel": "取消编辑", 28 | "Use a ',' to split tags": "使用 ',' 来分隔标签", 29 | "Markdown enabled, you can paste or drag drop an image into this area, the file will be uploaded automaticly.": "文章内容使用Markdown编码,同样您可以使用HTML代码来编辑您的文章。您可以拖拽图片至编辑区或直接通过剪贴板粘贴,图片将会自动上传。", 30 | "Edit this post": "编辑文章", 31 | "Edit": "编辑", 32 | "Remove": "删除", 33 | "Remove this post": "删除文章", 34 | "Share to:": "分享到:" 35 | } -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Default/Admin/Login.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = SR["Admin Panel"]; 3 | } 4 | @if (Html.IsRaw()) 5 | { 6 | @(new HtmlString("
")) 7 |
8 |
@ViewBag.Title
9 |
home
10 |
home
11 |
12 |

@ViewBag.Title

13 |
14 |
15 | } 16 |
17 |
18 |
19 |
20 |
21 |

22 |

23 | 24 |
25 |
26 |
27 |
28 | @Html.Partial("Sidebar"); 29 |
30 | @if (Html.IsRaw()) 31 | { 32 | @(new HtmlString("
")) 33 | } -------------------------------------------------------------------------------- /src/YuukoBlog/YuukoBlog.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | true 6 | YuukoBlog 7 | Exe 8 | YuukoBlog 9 | 2.0.0-preview2-25407-01 10 | 11 | 12 | 13 | 14 | PreserveNewest 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/YuukoBlog/Filters/GuestRequiredAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.Filters; 7 | using Microsoft.AspNetCore.Http; 8 | using Microsoft.Extensions.Internal; 9 | 10 | namespace YuukoBlog.Filters 11 | { 12 | public class GuestRequiredAttribute : ActionFilterAttribute 13 | { 14 | public override void OnActionExecuting(ActionExecutingContext context) 15 | { 16 | if (context.HttpContext.Session.GetString("Admin") == "true") 17 | context.Result = new RedirectResult("/Admin/Index"); 18 | else 19 | base.OnActionExecuting(context); 20 | } 21 | 22 | public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) 23 | { 24 | if (context.HttpContext.Session.GetString("Admin") == "true") 25 | { 26 | context.Result = new RedirectResult("/Admin/Index"); 27 | return Task.FromResult(0); 28 | } 29 | return base.OnActionExecutionAsync(context, next); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Default/scripts/codemirror/tablist.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | var CodeMirror = require("codemirror"); 5 | 6 | CodeMirror.commands.tabAndIndentMarkdownList = function (cm) { 7 | var ranges = cm.listSelections(); 8 | var pos = ranges[0].head; 9 | var eolState = cm.getStateAfter(pos.line); 10 | var inList = eolState.list !== false; 11 | 12 | if (inList) { 13 | cm.execCommand("indentMore"); 14 | return; 15 | } 16 | 17 | if (cm.options.indentWithTabs) { 18 | cm.execCommand("insertTab"); 19 | } 20 | else { 21 | var spaces = Array(cm.options.tabSize + 1).join(" "); 22 | cm.replaceSelection(spaces); 23 | } 24 | }; 25 | 26 | CodeMirror.commands.shiftTabAndUnindentMarkdownList = function (cm) { 27 | var ranges = cm.listSelections(); 28 | var pos = ranges[0].head; 29 | var eolState = cm.getStateAfter(pos.line); 30 | var inList = eolState.list !== false; 31 | 32 | if (inList) { 33 | cm.execCommand("indentLess"); 34 | return; 35 | } 36 | 37 | if (cm.options.indentWithTabs) { 38 | cm.execCommand("insertTab"); 39 | } 40 | else { 41 | var spaces = Array(cm.options.tabSize + 1).join(" "); 42 | cm.replaceSelection(spaces); 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /src/YuukoBlog/Extensions/SessionUploadAuthorization.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Pomelo.AspNetCore.Extensions.BlobStorage; 5 | using YuukoBlog.Extensions; 6 | 7 | namespace YuukoBlog.Extensions 8 | { 9 | public class SessionUploadAuthorization : IBlobUploadAuthorizationProvider 10 | { 11 | private IServiceProvider services; 12 | 13 | public SessionUploadAuthorization(IServiceProvider provider) 14 | { 15 | services = provider; 16 | } 17 | 18 | public bool IsAbleToUpload() 19 | { 20 | var val = services.GetRequiredService().HttpContext.Session.GetString("Admin"); 21 | if (val == "true") 22 | return true; 23 | return false; 24 | } 25 | } 26 | } 27 | 28 | namespace Microsoft.Extensions.DependencyInjection 29 | { 30 | public static class SignedUserUploadAuthorizationProviderServiceCollectionExtensions 31 | { 32 | public static IBlobStorageBuilder AddSessionUploadAuthorization(this IBlobStorageBuilder self) 33 | { 34 | self.Services.AddSingleton(); 35 | return self; 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/YuukoBlog/YuukoBlog.xproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | 10 | a86e6e29-580c-4917-a0b9-22278e54bb87 11 | YuukoBlog 12 | .\obj 13 | .\bin\ 14 | 15 | 16 | 17 | 2.0 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/YuukoBlog/Models/Post.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.ComponentModel.DataAnnotations; 5 | using System.ComponentModel.DataAnnotations.Schema; 6 | 7 | namespace YuukoBlog.Models 8 | { 9 | public class Post : IConvertible 10 | { 11 | public Guid Id { get; set; } 12 | 13 | [MaxLength(256)] 14 | public string Url { get; set; } 15 | 16 | public string Title { get; set; } 17 | 18 | public string Summary { get; set; } 19 | 20 | public string Content { get; set; } 21 | 22 | public DateTime Time { get; set; } 23 | 24 | public bool IsPage { get; set; } 25 | 26 | [ForeignKey("Catalog")] 27 | public Guid? CatalogId { get; set; } 28 | 29 | public virtual Catalog Catalog { get; set; } 30 | 31 | public virtual ICollection Tags { get; set; } = new List(); 32 | 33 | PostViewModel IConvertible.ToType() 34 | { 35 | return new PostViewModel 36 | { 37 | Id = Id, 38 | Summary = Summary, 39 | Catalog = Catalog, 40 | CatalogId = CatalogId, 41 | Tags = Tags.ToList(), 42 | Time = Time, 43 | Title = Title, 44 | Url = Url 45 | }; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/YuukoBlog/Models/BlogContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Pomelo.AspNetCore.Extensions.BlobStorage.Models; 3 | 4 | namespace YuukoBlog.Models 5 | { 6 | public class BlogContext : DbContext, IBlobStorageDbContext 7 | { 8 | public BlogContext(DbContextOptions opt) 9 | : base(opt) 10 | { 11 | } 12 | 13 | public DbSet Posts { get; set; } 14 | 15 | public DbSet PostTags { get; set; } 16 | 17 | public DbSet Catalogs { get; set; } 18 | 19 | public DbSet Blobs { get; set; } 20 | 21 | public DbSet BlogRolls { get; set; } 22 | 23 | protected override void OnModelCreating(ModelBuilder builder) 24 | { 25 | base.OnModelCreating(builder); 26 | 27 | builder.SetupBlobStorage(); 28 | 29 | builder.Entity(e => 30 | { 31 | e.HasIndex(x => x.PRI); 32 | }); 33 | 34 | builder.Entity(e => 35 | { 36 | e.HasIndex(x => x.IsPage); 37 | e.HasIndex(x => x.Time); 38 | e.HasIndex(x => x.Url).IsUnique(); 39 | }); 40 | 41 | builder.Entity(e => 42 | { 43 | e.HasIndex(x => x.Tag); 44 | }); 45 | 46 | builder.Entity(e => 47 | { 48 | e.HasIndex(x => x.GitHubId); 49 | }); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Moon/Shared/Home.cshtml: -------------------------------------------------------------------------------- 1 | @model IEnumerable 2 | @{ 3 | ViewBag.Title = "Home"; 4 | } 5 | @foreach (var x in Model) 6 | { 7 |
8 |
9 | 10 |
11 | @x.Time.ToString() 12 |
13 |
14 | 15 | @if (x.CatalogId != null) 16 | { 17 | @("分类:") 18 | @x.Catalog.Title 19 | } 20 | 21 | 标签: 22 | @foreach (PostTag y in x.Tags) 23 | { 24 | @y.Tag 25 | } 26 | 27 |
28 |
29 |
30 | @Html.Marked(x.Summary) 31 |
32 |
33 | } 34 |
35 |
共@((ViewData["PagingInfo"] as PagingInfo).PageCount)页
36 | @Html.Paging("page larger", "current") 37 |
38 |
39 | 40 | 43 | -------------------------------------------------------------------------------- /src/YuukoBlog/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "Microsoft.AspNetCore.Server.Kestrel": "1.0.0", 4 | "Microsoft.AspNetCore.Diagnostics": "1.0.0", 5 | "Microsoft.AspNetCore.Mvc": "1.0.0", 6 | "Microsoft.AspNetCore.Mvc.TagHelpers": "1.0.0", 7 | "Microsoft.AspNetCore.StaticFiles": "1.0.0", 8 | "Microsoft.AspNetCore.Session": "1.0.0", 9 | "Microsoft.Extensions.Logging.Console": "1.0.0", 10 | "Pomelo.EntityFrameworkCore.MySql": "1.0.0", 11 | "Pomelo.AspNetCore.Extensions": "1.0.0-prerelease-20160813", 12 | "Pomelo.AspNetCore.TimedJob": "1.0.0-prerelease-20160813" 13 | }, 14 | 15 | "frameworks": { 16 | "net451": { }, 17 | "netcoreapp1.0": { 18 | "dependencies": { 19 | "Microsoft.NETCore.App": { 20 | "version": "1.0.0", 21 | "type": "platform" 22 | } 23 | }, 24 | "imports": [ 25 | "dotnet5.6", 26 | "dnxcore50", 27 | "portable-net45+win8" 28 | ] 29 | } 30 | }, 31 | 32 | "buildOptions": { 33 | "emitEntryPoint": true, 34 | "preserveCompilationContext": true 35 | }, 36 | 37 | "runtimeOptions": { 38 | "gcServer": true 39 | }, 40 | 41 | "publishOptions": { 42 | "include": [ 43 | "wwwroot", 44 | "web.config", 45 | "Views", 46 | "Areas/**/Views", 47 | "config.json", 48 | "Localization" 49 | ] 50 | }, 51 | 52 | "runtimes": { 53 | "win7-x64": { }, 54 | "win7-x86": { }, 55 | "osx.10.11-x64": { }, 56 | "ubuntu.14.04-x64": { }, 57 | "centos.7-x64": { }, 58 | "rhel.7.2-x64": { }, 59 | "debian.8-x64": { } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Hexo/Shared/Home.cshtml: -------------------------------------------------------------------------------- 1 | @model IEnumerable 2 | @{ 3 | ViewBag.Title = "Home"; 4 | } 5 | @foreach (var x in Model) 6 | { 7 | 36 | } 37 | @if ((ViewData["PagingInfo"] as PagingInfo).Count > 0) 38 | { 39 |
40 | @Html.Paging("post-nav-page", "post-nav-page-current", "post-nav", null, new[] { "raw" }) 41 |
42 |
43 | } 44 | 45 | 48 | -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Moon/Admin/Index.cshtml: -------------------------------------------------------------------------------- 1 |
2 |
3 |
站点信息
4 |
5 |
6 |
7 |

8 | 网站名称:
9 | 10 |

11 |

12 | 网站介绍:
13 | 14 |

15 |

16 | 头像URL:
17 | 18 |

19 |

20 | Disqus:
21 | 22 |

23 |

24 | 关于URL:
25 | 26 |

27 |

28 | 用户名:
29 | 30 |

31 |

32 | 密码:
33 | 34 |

35 |

36 | GitHub:
37 | 38 |

39 |

40 | Following:
41 | 42 |

43 |

44 | Followers:
45 | 46 |

47 |

48 | 49 |

50 |
51 |
52 |
-------------------------------------------------------------------------------- /src/YuukoBlog/Controllers/PostController.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.EntityFrameworkCore; 4 | 5 | namespace YuukoBlog.Controllers 6 | { 7 | public class PostController : BaseController 8 | { 9 | [Route("Post/{id}")] 10 | public IActionResult Post(string id) 11 | { 12 | var post = DB.Posts 13 | .Include(x => x.Catalog) 14 | .Include(x => x.Tags) 15 | .Where(x => x.Url == id && !x.IsPage) 16 | .SingleOrDefault(); 17 | if (post == null) 18 | return Prompt(x => 19 | { 20 | x.StatusCode = 404; 21 | x.Title = SR["Not Found"]; 22 | x.Details = SR["The resources have not been found, please check your request."]; 23 | x.RedirectUrl = Url.Link("default", new { controller = "Home", action = "Index" }); 24 | x.RedirectText = SR["Back to home"]; 25 | }); 26 | ViewBag.Title = post.Title; 27 | ViewBag.Position = post.CatalogId != null ? post.Catalog.Url : "home"; 28 | return View(post); 29 | } 30 | 31 | [Route("{id}")] 32 | public IActionResult Page(string id) 33 | { 34 | var post = DB.Posts 35 | .Where(x => x.Url == id && x.IsPage) 36 | .SingleOrDefault(); 37 | if (post == null) 38 | return Prompt(x => 39 | { 40 | x.StatusCode = 404; 41 | x.Title = SR["Not Found"]; 42 | x.Details = SR["The resources have not been found, please check your request."]; 43 | x.RedirectUrl = Url.Link("default", new { controller = "Home", action = "Index" }); 44 | x.RedirectText = SR["Back to home"]; 45 | }); 46 | ViewBag.Title = post.Title; 47 | ViewBag.Position = post.CatalogId.HasValue ? post.Catalog.Url : "home"; 48 | return View("Post", post); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Moon/Admin/Catalog.cshtml: -------------------------------------------------------------------------------- 1 | @model IEnumerable 2 | @{ 3 | ViewBag.Title = "管理后台"; 4 | } 5 |
6 |
7 |
站点信息
8 |
9 |
10 |

11 | 新建分类 12 |

13 |
14 |
15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | @foreach (var x in Model) 27 | { 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 41 | 42 | } 43 | 44 |
标题英文标题排序操作
@x.Title@x.Url@x.PRI 36 | 编辑 37 | 删除 38 | 保存 39 | 取消 40 |
45 |
46 |
47 | -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Default/Shared/PostInfo.cshtml: -------------------------------------------------------------------------------- 1 | @if (Model != null && Model.GetType() == typeof(Post) && (Context.Session.GetString("Admin") == "true" || !Model.IsPage)) 2 | { 3 | 41 | } 42 | @if (Html.IsAdmin()) 43 | { 44 |
45 | } -------------------------------------------------------------------------------- /YuukoBlog.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26621.2 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{6B058FF7-8DFD-49CA-AB30-4054D88551E1}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{97ADCA02-71FC-4CE1-8081-881BEE58C7C7}" 9 | ProjectSection(SolutionItems) = preProject 10 | LICENSE = LICENSE 11 | README.md = README.md 12 | EndProjectSection 13 | EndProject 14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{E121BC87-4F9E-440F-ABE5-F840886DC4C9}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "YuukoBlog", "src\YuukoBlog\YuukoBlog.csproj", "{CFD6F23B-DEFB-4727-A163-E454187FB3E5}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "YuukoBlog.Tests", "test\YuukoBlog.Tests\YuukoBlog.Tests.csproj", "{C7238E7C-F497-4D66-B712-B77FDD534333}" 19 | EndProject 20 | Global 21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 22 | Debug|Any CPU = Debug|Any CPU 23 | Release|Any CPU = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 26 | {CFD6F23B-DEFB-4727-A163-E454187FB3E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {CFD6F23B-DEFB-4727-A163-E454187FB3E5}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {CFD6F23B-DEFB-4727-A163-E454187FB3E5}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {CFD6F23B-DEFB-4727-A163-E454187FB3E5}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {C7238E7C-F497-4D66-B712-B77FDD534333}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {C7238E7C-F497-4D66-B712-B77FDD534333}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {C7238E7C-F497-4D66-B712-B77FDD534333}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {C7238E7C-F497-4D66-B712-B77FDD534333}.Release|Any CPU.Build.0 = Release|Any CPU 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | GlobalSection(NestedProjects) = preSolution 39 | {CFD6F23B-DEFB-4727-A163-E454187FB3E5} = {6B058FF7-8DFD-49CA-AB30-4054D88551E1} 40 | {C7238E7C-F497-4D66-B712-B77FDD534333} = {E121BC87-4F9E-440F-ABE5-F840886DC4C9} 41 | EndGlobalSection 42 | GlobalSection(ExtensibilityGlobals) = postSolution 43 | SolutionGuid = {30C6302E-3B8A-4878-8696-B2E2AFCB772D} 44 | EndGlobalSection 45 | EndGlobal 46 | -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Default/Shared/Home.cshtml: -------------------------------------------------------------------------------- 1 | @model IEnumerable 2 | @{ 3 | bool Raw = false; 4 | if (Html.IsRaw()) 5 | { 6 | Raw = true; 7 | Layout = null; 8 | } 9 | } 10 | 11 | @if (Raw) 12 | { 13 | @(new HtmlString("
")) 14 |
15 |
@ViewBag.Title - @ViewBag.Description
16 |
@ViewBag.Position.ToLower()
17 |
@ViewBag.Position.ToLower()
18 |
19 |

@ViewBag.Title@(ViewBag.Position == "home" ? "" : (" » " + ViewBag.Position.ToUpper()))@(ViewContext.RouteData.Values["tag"] == null ? "" : (" » " + ViewContext.RouteData.Values["tag"].ToString().ToUpper()))@(ViewContext.RouteData.Values["year"] == null ? "" : (" » " + ViewContext.RouteData.Values["year"].ToString()))@(ViewContext.RouteData.Values["month"] == null ? "" : (" » " + ViewContext.RouteData.Values["month"].ToString()))@(ViewContext.RouteData.Values["p"] == null ? "" : (" » PAGE" + ViewContext.RouteData.Values["p"].ToString()))

20 |
21 |
22 | } 23 |
24 |
25 |
26 | @foreach (dynamic x in Model) 27 | { 28 |
29 |

@x.Title

30 |
31 | @Html.Marked(x.Summary as string) 32 |
33 | 44 |
Published on @x.Time
45 |
46 | } 47 |
48 | @Html.Paging("post-nav-page", "post-nav-page-current", "post-nav", null, new[] { "raw" }) 49 |
50 |
51 | @Html.Partial("Sidebar") 52 |
53 | @if (Raw) 54 | { 55 | @(new HtmlString("
")) 56 | } -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Hexo/Admin/Catalog.cshtml: -------------------------------------------------------------------------------- 1 | @model IEnumerable 2 | @{ 3 | ViewBag.Title = "管理后台"; 4 | } 5 | 6 |
7 |
8 | 9 |
10 |

11 | @ViewBag.Title 12 |

13 |
14 |
15 |

16 | 新建分类 17 |

18 |
19 |
20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | @foreach (var x in Model) 32 | { 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 46 | 47 | } 48 | 49 |
标题英文标题排序操作
@x.Title@x.Url@x.PRI 41 | 编辑 42 | 删除 43 | 保存 44 | 取消 45 |
50 |
51 |
52 |
-------------------------------------------------------------------------------- /src/YuukoBlog/Views/Hexo/Admin/Index.cshtml: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |

6 | 站点信息 7 |

8 |
9 |
10 |
11 |

12 | 网站名称:
13 | 14 |

15 |

16 | 网站介绍:
17 | 18 |

19 |

20 | 头像URL:
21 | 22 |

23 |

24 | Disqus:
25 | 26 |

27 |

28 | 关于URL:
29 | 30 |

31 |

32 | 用户名:
33 | 34 |

35 |

36 | 密码:
37 | 38 |

39 |

40 | GitHub:
41 | 42 |

43 |

44 | Following:
45 | 46 |

47 |

48 | Followers:
49 | 50 |

51 |

52 | 53 |

54 |
55 |
56 |
57 |
-------------------------------------------------------------------------------- /src/YuukoBlog/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Microsoft.AspNetCore.Builder; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.EntityFrameworkCore; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.Logging; 9 | using Pomelo.AspNetCore.Localization; 10 | using YuukoBlog.Models; 11 | 12 | namespace YuukoBlog 13 | { 14 | public class Startup 15 | { 16 | public void ConfigureServices(IServiceCollection services) 17 | { 18 | IConfiguration Configuration; 19 | services.AddConfiguration(out Configuration); 20 | 21 | if (Configuration["Database:Type"] == "SQLite") 22 | { 23 | services.AddDbContext(x => x.UseSqlite(Configuration["Database:ConnectionString"])); 24 | } 25 | else if (Configuration["Database:Type"] == "MySQL") 26 | { 27 | services.AddDbContext(x => x.UseMySql(Configuration["Database:ConnectionString"])); 28 | } 29 | 30 | services.AddSmartCookies(); 31 | 32 | services.AddMemoryCache(); 33 | services.AddSession(x => x.IdleTimeout = TimeSpan.FromMinutes(20)); 34 | 35 | services.AddBlobStorage() 36 | .AddEntityFrameworkStorage() 37 | .AddSessionUploadAuthorization(); 38 | 39 | services.AddPomeloLocalization(x => 40 | { 41 | x.AddCulture(new string[] { "zh", "zh-CN", "zh-Hans", "zh-Hans-CN", "zh-cn" }, new JsonLocalizedStringStore(Path.Combine("Localization", "zh-CN.json"))); 42 | x.AddCulture(new string[] { "en", "en-US", "en-GB" }, new JsonLocalizedStringStore(Path.Combine("Localization", "en-US.json"))); 43 | }); 44 | 45 | services.AddMvc() 46 | .AddMultiTemplateEngine() 47 | .AddCookieTemplateProvider(); 48 | 49 | services.AddTimedJob(); 50 | } 51 | 52 | public async void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) 53 | { 54 | loggerFactory.AddConsole(LogLevel.Warning, true); 55 | 56 | app.UseStaticFiles(); 57 | app.UseSession(); 58 | app.UseBlobStorage("/assets/shared/scripts/jquery.codecomb.fileupload.js"); 59 | app.UseDeveloperExceptionPage(); 60 | app.UseMvcWithDefaultRoute(); 61 | 62 | await SampleData.InitializeYuukoBlog(app.ApplicationServices); 63 | 64 | app.UseTimedJob(); 65 | } 66 | 67 | public static void Main(string[] args) 68 | { 69 | var host = new WebHostBuilder() 70 | .UseKestrel() 71 | .UseIISIntegration() 72 | .UseContentRoot(Directory.GetCurrentDirectory()) 73 | .UseStartup() 74 | .Build(); 75 | 76 | host.Run(); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Default/Admin/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = SR["Admin Panel"]; 3 | } 4 | @if (Html.IsRaw()) 5 | { 6 | @(new HtmlString("
")) 7 |
8 |
@ViewBag.Title
9 |
home
10 |
home
11 |
12 |

@ViewBag.Title

13 |
14 |
15 | } 16 |
17 |
18 |
19 |
20 |
21 |

22 | @SR["Site name"]:
23 | 24 |

25 |

26 | @SR["Description"]:
27 | 28 |

29 |

30 | @SR["Avatar URL"]:
31 | 32 |

33 |

34 | Disqus:
35 | 36 |

37 |

38 | @SR["About-me URL"]:
39 | 40 |

41 |

42 | @SR["User Name"]:
43 | 44 |

45 |

46 | @SR["Password"]:
47 | 48 |

49 |

50 | @SR["GitHub"]:
51 | 52 |

53 |

54 | @SR["Following"]:
55 | 56 |

57 |

58 | @SR["Followers"]:
59 | 60 |

61 |

62 | 63 |

64 |
65 |
66 |
67 |
68 | @Html.Partial("Sidebar") 69 |
70 | @if (Html.IsRaw()) 71 | { 72 | @(new HtmlString("
")) 73 | } -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Default/Admin/Catalog.cshtml: -------------------------------------------------------------------------------- 1 | @model IEnumerable 2 | @{ 3 | ViewBag.Title = SR["Admin Panel"]; 4 | } 5 | @if (Html.IsRaw()) 6 | { 7 | @(new HtmlString("
")) 8 |
9 |
@ViewBag.Title
10 |
home
11 |
home
12 |
13 |

@ViewBag.Title

14 |
15 |
16 | } 17 |
18 |
19 |
20 |
21 |

22 | @SR["New Catalog"] 23 |

24 |
25 |
26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | @foreach (var x in Model) 38 | { 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 52 | 53 | } 54 | 55 |
@SR["Title"]@SR["Sub Title"]@SR["Order"]@SR["Control"]
@x.Title@x.Url@x.PRI 47 | @SR["Edit"] 48 | @SR["Remove"] 49 | @SR["Save"] 50 | @SR["Cancel"] 51 |
56 |
57 |
58 |
59 | @Html.Partial("Sidebar") 60 |
61 | @if (Html.IsRaw()) 62 | { 63 | @(new HtmlString("
")) 64 | } -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Hexo/js/pc.js: -------------------------------------------------------------------------------- 1 | define([], function () { 2 | 3 | var Tips = (function () { 4 | 5 | var $tipBox = $(".tips-box"); 6 | 7 | return { 8 | show: function () { 9 | $tipBox.removeClass("hide"); 10 | }, 11 | hide: function () { 12 | $tipBox.addClass("hide"); 13 | }, 14 | init: function () { 15 | 16 | } 17 | } 18 | })(); 19 | 20 | var resetTags = function () { 21 | var tags = $(".tagcloud a"); 22 | tags.css({ "font-size": "12px" }); 23 | for (var i = 0, len = tags.length; i < len; i++) { 24 | var num = tags.eq(i).html().length % 5 + 1; 25 | tags[i].className = ""; 26 | tags.eq(i).addClass("color" + num); 27 | } 28 | } 29 | 30 | var slide = function (idx) { 31 | var $wrap = $(".switch-wrap"); 32 | $wrap.css({ 33 | "transform": "translate(-" + idx * 100 + "%, 0 )" 34 | }); 35 | $(".icon-wrap").addClass("hide"); 36 | $(".icon-wrap").eq(idx).removeClass("hide"); 37 | } 38 | 39 | var bind = function () { 40 | var switchBtn = $("#myonoffswitch"); 41 | var tagcloud = $(".second-part"); 42 | var navDiv = $(".first-part"); 43 | switchBtn.click(function () { 44 | if (switchBtn.hasClass("clicked")) { 45 | switchBtn.removeClass("clicked"); 46 | tagcloud.removeClass("turn-left"); 47 | navDiv.removeClass("turn-left"); 48 | } else { 49 | switchBtn.addClass("clicked"); 50 | tagcloud.addClass("turn-left"); 51 | navDiv.addClass("turn-left"); 52 | resetTags(); 53 | } 54 | }); 55 | 56 | var timeout; 57 | var isEnterBtn = false; 58 | var isEnterTips = false; 59 | 60 | $(".icon").bind("mouseenter", function () { 61 | isEnterBtn = true; 62 | Tips.show(); 63 | }).bind("mouseleave", function () { 64 | isEnterBtn = false; 65 | setTimeout(function () { 66 | if (!isEnterTips) { 67 | Tips.hide(); 68 | } 69 | }, 100); 70 | }); 71 | 72 | $(".tips-box").bind("mouseenter", function () { 73 | isEnterTips = true; 74 | Tips.show(); 75 | }).bind("mouseleave", function () { 76 | isEnterTips = false; 77 | setTimeout(function () { 78 | if (!isEnterBtn) { 79 | Tips.hide(); 80 | } 81 | }, 100); 82 | }); 83 | 84 | $(".tips-inner li").bind("click", function () { 85 | var idx = $(this).index(); 86 | slide(idx); 87 | Tips.hide(); 88 | }); 89 | } 90 | 91 | 92 | 93 | return { 94 | init: function () { 95 | resetTags(); 96 | bind(); 97 | Tips.init(); 98 | } 99 | } 100 | }); -------------------------------------------------------------------------------- /src/YuukoBlog/Controllers/BaseController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.EntityFrameworkCore; 5 | using YuukoBlog.Models; 6 | 7 | namespace YuukoBlog.Controllers 8 | { 9 | public class BaseController : BaseController 10 | { 11 | public override void Prepare() 12 | { 13 | base.Prepare(); 14 | 15 | // Building Constants 16 | ViewBag.Position = "home"; 17 | ViewBag.IsPost = false; 18 | ViewBag.Description = Configuration["Description"]; 19 | ViewBag.Title = Configuration["Site"]; 20 | ViewBag.Site = Configuration["Site"]; 21 | ViewBag.AboutUrl = Configuration["AboutUrl"]; 22 | ViewBag.AvatarUrl = Configuration["AvatarUrl"]; 23 | ViewBag.Disqus = Configuration["Disqus"]; 24 | ViewBag.Account = Configuration["Account"]; 25 | ViewBag.DefaultTemplate = Configuration["DefaultTemplate"]; 26 | ViewBag.GitHub = Configuration["BlogRoll:GitHub"]; 27 | ViewBag.Following = Convert.ToBoolean(Configuration["BlogRoll:Following"]); 28 | ViewBag.Follower = Convert.ToBoolean(Configuration["BlogRoll:Follower"]); 29 | 30 | // Building Tags 31 | ViewBag.Tags = DB.PostTags 32 | .OrderBy(x => x.Tag) 33 | .GroupBy(x => x.Tag) 34 | .Select(x => new TagViewModel 35 | { 36 | Title = x.Key, 37 | Count = x.Count() 38 | }) 39 | .ToList(); 40 | 41 | // Building Calendar 42 | ViewBag.Calendars = DB.Posts 43 | .Where(x => !x.IsPage) 44 | .OrderByDescending(x => x.Time) 45 | .GroupBy(x => new { Year = x.Time.Year, Month = x.Time.Month }) 46 | .Select(x => new CalendarViewModel 47 | { 48 | Year = x.Key.Year, 49 | Month = x.Key.Month, 50 | Count = x.Count() 51 | }) 52 | .ToList(); 53 | 54 | // Building Catalogs 55 | ViewBag.Catalogs = DB.Catalogs 56 | .Include(x => x.Posts) 57 | .OrderByDescending(x => x.PRI) 58 | .ToList() 59 | .Select(x => new CatalogViewModel 60 | { 61 | Id = x.Id, 62 | Title = x.Title, 63 | Count = x.Posts.Count(), 64 | PRI = x.PRI, 65 | Url = x.Url 66 | }) 67 | .ToList(); 68 | 69 | // Building Blog Rolls 70 | var rolls = DB.BlogRolls 71 | .Where(x => !string.IsNullOrEmpty(x.URL) && x.AvatarId.HasValue) 72 | .OrderByDescending(x => x.Type) 73 | .Select(x => new BlogRollViewModel 74 | { 75 | AvatarId = x.AvatarId.Value, 76 | Name = x.NickName, 77 | URL = x.URL 78 | }) 79 | .ToList(); 80 | rolls.Reverse(); 81 | ViewBag.Rolls = rolls; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/YuukoBlog/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.EntityFrameworkCore; 5 | using YuukoBlog.Models; 6 | 7 | namespace YuukoBlog.Controllers 8 | { 9 | public class HomeController : BaseController 10 | { 11 | [Route("{p:int?}")] 12 | public IActionResult Index(int p = 1) 13 | { 14 | return PagedView(DB.Posts 15 | .Include(x => x.Catalog) 16 | .Include(x => x.Tags) 17 | .Where(x => !x.IsPage) 18 | .OrderByDescending(x => x.Time), 5, "Home"); 19 | } 20 | 21 | [Route("{year:int}/{month:int}/{p:int?}")] 22 | public IActionResult Calendar(int year, int month, int p = 1) 23 | { 24 | var begin = new DateTime(year, month, 1); 25 | var end = begin.AddMonths(1); 26 | return PagedView(DB.Posts 27 | .Include(x => x.Tags) 28 | .Include(x => x.Catalog) 29 | .Where(x => !x.IsPage) 30 | .Where(x => x.Time >= begin && x.Time <= end) 31 | .OrderByDescending(x => x.Time), 5, "Home"); 32 | } 33 | 34 | [Route("Catalog/{id}/{p:int?}")] 35 | public IActionResult Catalog(string id, int p = 1) 36 | { 37 | var catalog = DB.Catalogs 38 | .Where(x => x.Url == id) 39 | .SingleOrDefault(); 40 | if (catalog == null) 41 | return Prompt(x => 42 | { 43 | x.StatusCode = 404; 44 | x.Title = SR["Not Found"]; 45 | x.Details = SR["The resources have not been found, please check your request."]; 46 | x.RedirectUrl = Url.Link("default", new { controller = "Home", action = "Index" }); 47 | x.RedirectText = SR["Back to home"]; 48 | }); 49 | ViewBag.Position = catalog.Url; 50 | return PagedView(DB.Posts 51 | .Include(x => x.Tags) 52 | .Include(x => x.Catalog) 53 | .Where(x => !x.IsPage && x.CatalogId == catalog.Id) 54 | .OrderByDescending(x => x.Time), 5, "Home"); 55 | } 56 | 57 | [Route("Tag/{tag}/{p:int?}")] 58 | public IActionResult Tag(string tag, int p = 1) 59 | { 60 | return PagedView(DB.Posts 61 | .Include(x => x.Tags) 62 | .Include(x => x.Catalog) 63 | .Where(x => !x.IsPage) 64 | .Where(x => x.Tags.Any(y => y.Tag == tag)) 65 | .OrderByDescending(x => x.Time), 5, "Home"); 66 | } 67 | 68 | [Route("Search/{id}/{p:int?}")] 69 | public IActionResult Search(string id, int p = 1) 70 | { 71 | return PagedView(DB.Posts 72 | .Include(x => x.Tags) 73 | .Include(x => x.Catalog) 74 | .Where(x => !x.IsPage) 75 | .Where(x => x.Title.Contains(id) || id.Contains(x.Title)) 76 | .OrderByDescending(x => x.Time), 5, "Home"); 77 | } 78 | 79 | public IActionResult Template(string Folder, [FromHeader] string Referer) 80 | { 81 | Cookies["ASPNET_TEMPLATE"] = Folder; 82 | return Redirect(Referer ?? "/"); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/YuukoBlog/Properties/PublishProfiles/Publish-publish.ps1: -------------------------------------------------------------------------------- 1 | [cmdletbinding(SupportsShouldProcess=$true)] 2 | param($publishProperties, $packOutput, $nugetUrl) 3 | 4 | # to learn more about this file visit http://go.microsoft.com/fwlink/?LinkId=524327 5 | $publishModuleVersion = '1.0.1' 6 | function Get-VisualStudio2015InstallPath{ 7 | [cmdletbinding()] 8 | param() 9 | process{ 10 | $keysToCheck = @('hklm:\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\14.0', 11 | 'hklm:\SOFTWARE\Microsoft\VisualStudio\14.0', 12 | 'hklm:\SOFTWARE\Wow6432Node\Microsoft\VWDExpress\14.0', 13 | 'hklm:\SOFTWARE\Microsoft\VWDExpress\14.0' 14 | ) 15 | [string]$vsInstallPath=$null 16 | 17 | foreach($keyToCheck in $keysToCheck){ 18 | if(Test-Path $keyToCheck){ 19 | $vsInstallPath = (Get-itemproperty $keyToCheck -Name InstallDir -ErrorAction SilentlyContinue | select -ExpandProperty InstallDir -ErrorAction SilentlyContinue) 20 | } 21 | 22 | if($vsInstallPath){ 23 | break; 24 | } 25 | } 26 | 27 | $vsInstallPath 28 | } 29 | } 30 | 31 | $vsInstallPath = Get-VisualStudio2015InstallPath 32 | $publishModulePath = "{0}Extensions\Microsoft\Web Tools\Publish\Scripts\{1}\" -f $vsInstallPath, $publishModuleVersion 33 | 34 | if(!(Test-Path $publishModulePath)){ 35 | $publishModulePath = "{0}VWDExpressExtensions\Microsoft\Web Tools\Publish\Scripts\{1}\" -f $vsInstallPath, $publishModuleVersion 36 | } 37 | 38 | $defaultPublishSettings = New-Object psobject -Property @{ 39 | LocalInstallDir = $publishModulePath 40 | } 41 | 42 | function Enable-PackageDownloader{ 43 | [cmdletbinding()] 44 | param( 45 | $toolsDir = "$env:LOCALAPPDATA\Microsoft\Web Tools\Publish\package-downloader-$publishModuleVersion\", 46 | $pkgDownloaderDownloadUrl = 'http://go.microsoft.com/fwlink/?LinkId=524325') # package-downloader.psm1 47 | process{ 48 | if(get-module package-downloader){ 49 | remove-module package-downloader | Out-Null 50 | } 51 | 52 | if(!(get-module package-downloader)){ 53 | if(!(Test-Path $toolsDir)){ New-Item -Path $toolsDir -ItemType Directory -WhatIf:$false } 54 | 55 | $expectedPath = (Join-Path ($toolsDir) 'package-downloader.psm1') 56 | if(!(Test-Path $expectedPath)){ 57 | 'Downloading [{0}] to [{1}]' -f $pkgDownloaderDownloadUrl,$expectedPath | Write-Verbose 58 | (New-Object System.Net.WebClient).DownloadFile($pkgDownloaderDownloadUrl, $expectedPath) 59 | } 60 | 61 | if(!$expectedPath){throw ('Unable to download package-downloader.psm1')} 62 | 63 | 'importing module [{0}]' -f $expectedPath | Write-Output 64 | Import-Module $expectedPath -DisableNameChecking -Force 65 | } 66 | } 67 | } 68 | 69 | function Enable-PublishModule{ 70 | [cmdletbinding()] 71 | param() 72 | process{ 73 | if(get-module publish-module){ 74 | remove-module publish-module | Out-Null 75 | } 76 | 77 | if(!(get-module publish-module)){ 78 | $localpublishmodulepath = Join-Path $defaultPublishSettings.LocalInstallDir 'publish-module.psm1' 79 | if(Test-Path $localpublishmodulepath){ 80 | 'importing module [publish-module="{0}"] from local install dir' -f $localpublishmodulepath | Write-Verbose 81 | Import-Module $localpublishmodulepath -DisableNameChecking -Force 82 | $true 83 | } 84 | } 85 | } 86 | } 87 | 88 | try{ 89 | 90 | if (!(Enable-PublishModule)){ 91 | Enable-PackageDownloader 92 | Enable-NuGetModule -name 'publish-module' -version $publishModuleVersion -nugetUrl $nugetUrl 93 | } 94 | 95 | 'Calling Publish-AspNet' | Write-Verbose 96 | # call Publish-AspNet to perform the publish operation 97 | Publish-AspNet -publishProperties $publishProperties -packOutput $packOutput 98 | } 99 | catch{ 100 | "An error occurred during publish.`n{0}" -f $_.Exception.Message | Write-Error 101 | } -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Default/Shared/Post.cshtml: -------------------------------------------------------------------------------- 1 | @model Post 2 | @if (Html.IsRaw()) 3 | { 4 | @(new HtmlString("
")) 5 |
6 |
@Model.Title - @ViewBag.Description
7 | @if (Model.CatalogId.HasValue) 8 | { 9 |
@Model.Catalog.Url.ToLower()
10 |
@Model.Catalog.Title
11 | } 12 | else 13 | { 14 |
home
15 | } 16 |
17 |

@Model.Title

18 |

Published on @Model.Time.ToString()

19 |
20 |
21 | } 22 |
23 |
24 |
25 |
26 | @Html.Marked(Model.Content) 27 |
28 | @if (Html.IsAdmin()) 29 | { 30 |
31 |

32 |

33 |

34 | 41 |

42 |

43 |

44 | 45 |
拖拽文件、粘贴文件或选择文件以添加附件。
46 |
47 |

48 |
49 | 50 |
51 |

52 | 53 |

54 |

55 | @SR["Is a page"] 56 |

57 |

58 | 59 | @SR["Cancel"] 60 |

61 |
62 | } 63 |
64 |
65 | @SR["Share to:"] 66 | 70 |
71 |
72 |

Comments

73 |
74 |
75 |
76 |
77 |
78 | 使用微信扫码 79 |
80 | @Html.Partial("Sidebar") 81 |
82 | @if (Html.IsRaw()) 83 | { 84 | @(new HtmlString("
")) 85 | } -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Moon/Shared/Post.cshtml: -------------------------------------------------------------------------------- 1 | @model Post 2 |
3 |
4 |
@Model.Title
5 |
6 | @Model.Time.ToString() 7 | @if (Html.IsAdmin()) 8 | { 9 | 编辑文章 10 | 删除文章 11 |
12 |
13 | } 14 | 15 |
16 |
17 | 18 | @if (Model.CatalogId != null) 19 | { 20 | @("分类:") 21 | @Model.Catalog.Title 22 | } 23 | 24 | 标签: 25 | @foreach (PostTag x in Model.Tags) 26 | { 27 | @x.Tag 28 | } 29 | 30 |
31 |
32 |
33 |
34 | @Html.Marked(Model.Content) 35 |
36 | @if (Html.IsAdmin()) 37 | { 38 |
39 |

40 |

41 |

42 | 49 |

50 |

51 |

52 | 53 |
拖拽文件、粘贴文件或选择文件以添加附件。
54 |
55 |

56 |
57 | 58 |
59 |

60 | 61 |

62 |

63 | 是否为页面 64 |

65 |

66 | 67 | 取消编辑 68 |

69 |
70 | } 71 |
72 |
73 | 74 |
75 |

76 |

77 | 88 |
89 |

90 |
-------------------------------------------------------------------------------- /src/YuukoBlog/Views/Hexo/Shared/Post.cshtml: -------------------------------------------------------------------------------- 1 | @model Post 2 |
3 | 13 |
14 | 15 |
16 |

17 | @Model.Title 18 |

19 |
20 |
21 |
22 | @Html.Marked(Model.Content) 23 |
24 | @if (Html.IsAdmin()) 25 | { 26 |
27 |
28 | 29 |
30 |

31 |

32 |

33 | 40 |

41 |

42 |

43 | 44 |
拖拽文件、粘贴文件或选择文件以添加附件。
45 |
46 |

47 |
48 | 49 |
50 |

51 | 52 |

53 |

54 | 是否为页面 55 |

56 |

57 | 58 | 59 |

60 |
61 | } 62 |
63 | 74 |
75 |
76 |
77 |
78 | 89 |
90 |
-------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | # TODO: Comment the next line if you want to checkin your web deploy settings 144 | # but database connection strings (with potential passwords) will be unencrypted 145 | *.pubxml 146 | *.publishproj 147 | 148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 149 | # checkin your Azure Web App publish settings, but sensitive information contained 150 | # in these scripts will be unencrypted 151 | PublishScripts/ 152 | 153 | # NuGet Packages 154 | *.nupkg 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | # NuGet v3's project.json files produces more ignoreable files 162 | *.nuget.props 163 | *.nuget.targets 164 | 165 | # Microsoft Azure Build Output 166 | csx/ 167 | *.build.csdef 168 | 169 | # Microsoft Azure Emulator 170 | ecf/ 171 | rcf/ 172 | 173 | # Windows Store app package directories and files 174 | AppPackages/ 175 | BundleArtifacts/ 176 | Package.StoreAssociation.xml 177 | _pkginfo.txt 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | ~$* 188 | *~ 189 | *.dbmdl 190 | *.dbproj.schemaview 191 | *.pfx 192 | *.publishsettings 193 | node_modules/ 194 | orleans.codegen.cs 195 | 196 | # Since there are multiple workflows, uncomment next line to ignore bower_components 197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 198 | #bower_components/ 199 | 200 | # RIA/Silverlight projects 201 | Generated_Code/ 202 | 203 | # Backup & report files from converting an old project file 204 | # to a newer Visual Studio version. Backup files are not needed, 205 | # because we have git ;-) 206 | _UpgradeReport_Files/ 207 | Backup*/ 208 | UpgradeLog*.XML 209 | UpgradeLog*.htm 210 | 211 | # SQL Server files 212 | *.mdf 213 | *.ldf 214 | 215 | # Business Intelligence projects 216 | *.rdl.data 217 | *.bim.layout 218 | *.bim_*.settings 219 | 220 | # Microsoft Fakes 221 | FakesAssemblies/ 222 | 223 | # GhostDoc plugin setting file 224 | *.GhostDoc.xml 225 | 226 | # Node.js Tools for Visual Studio 227 | .ntvs_analysis.dat 228 | 229 | # Visual Studio 6 build log 230 | *.plg 231 | 232 | # Visual Studio 6 workspace options file 233 | *.opt 234 | 235 | # Visual Studio LightSwitch build output 236 | **/*.HTMLClient/GeneratedArtifacts 237 | **/*.DesktopClient/GeneratedArtifacts 238 | **/*.DesktopClient/ModelManifest.xml 239 | **/*.Server/GeneratedArtifacts 240 | **/*.Server/ModelManifest.xml 241 | _Pvt_Extensions 242 | 243 | # Paket dependency manager 244 | .paket/paket.exe 245 | paket-files/ 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml 253 | -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Moon/scripts/jquery.validate.unobtrusive.min.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | /* 16 | ** Unobtrusive validation support library for jQuery and jQuery Validate 17 | ** Copyright (C) Microsoft Corporation. All rights reserved. 18 | */ 19 | (function(a){var d=a.validator,b,e="unobtrusiveValidation";function c(a,b,c){a.rules[b]=c;if(a.message)a.messages[b]=a.message}function j(a){return a.replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g)}function f(a){return a.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function h(a){return a.substr(0,a.lastIndexOf(".")+1)}function g(a,b){if(a.indexOf("*.")===0)a=a.replace("*.",b);return a}function m(c,e){var b=a(this).find("[data-valmsg-for='"+f(e[0].name)+"']"),d=b.attr("data-valmsg-replace"),g=d?a.parseJSON(d)!==false:null;b.removeClass("field-validation-valid").addClass("field-validation-error");c.data("unobtrusiveContainer",b);if(g){b.empty();c.removeClass("input-validation-error").appendTo(b)}else c.hide()}function l(e,d){var c=a(this).find("[data-valmsg-summary=true]"),b=c.find("ul");if(b&&b.length&&d.errorList.length){b.empty();c.addClass("validation-summary-errors").removeClass("validation-summary-valid");a.each(d.errorList,function(){a("
  • ").html(this.message).appendTo(b)})}}function k(d){var b=d.data("unobtrusiveContainer"),c=b.attr("data-valmsg-replace"),e=c?a.parseJSON(c):null;if(b){b.addClass("field-validation-valid").removeClass("field-validation-error");d.removeData("unobtrusiveContainer");e&&b.empty()}}function n(){var b=a(this);b.data("validator").resetForm();b.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors");b.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}function i(c){var b=a(c),d=b.data(e),f=a.proxy(n,c);if(!d){d={options:{errorClass:"input-validation-error",errorElement:"span",errorPlacement:a.proxy(m,c),invalidHandler:a.proxy(l,c),messages:{},rules:{},success:a.proxy(k,c)},attachValidation:function(){b.unbind("reset."+e,f).bind("reset."+e,f).validate(this.options)},validate:function(){b.validate();return b.valid()}};b.data(e,d)}return d}d.unobtrusive={adapters:[],parseElement:function(b,h){var d=a(b),f=d.parents("form")[0],c,e,g;if(!f)return;c=i(f);c.options.rules[b.name]=e={};c.options.messages[b.name]=g={};a.each(this.adapters,function(){var c="data-val-"+this.name,i=d.attr(c),h={};if(i!==undefined){c+="-";a.each(this.params,function(){h[this]=d.attr(c+this)});this.adapt({element:b,form:f,message:i,params:h,rules:e,messages:g})}});a.extend(e,{__dummy__:true});!h&&c.attachValidation()},parse:function(b){var c=a(b).parents("form").andSelf().add(a(b).find("form")).filter("form");a(b).find(":input").filter("[data-val=true]").each(function(){d.unobtrusive.parseElement(this,true)});c.each(function(){var a=i(this);a&&a.attachValidation()})}};b=d.unobtrusive.adapters;b.add=function(c,a,b){if(!b){b=a;a=[]}this.push({name:c,params:a,adapt:b});return this};b.addBool=function(a,b){return this.add(a,function(d){c(d,b||a,true)})};b.addMinMax=function(e,g,f,a,d,b){return this.add(e,[d||"min",b||"max"],function(b){var e=b.params.min,d=b.params.max;if(e&&d)c(b,a,[e,d]);else if(e)c(b,g,e);else d&&c(b,f,d)})};b.addSingleVal=function(a,b,d){return this.add(a,[b||"val"],function(e){c(e,d||a,e.params[b])})};d.addMethod("__dummy__",function(){return true});d.addMethod("regex",function(b,c,d){var a;if(this.optional(c))return true;a=(new RegExp(d)).exec(b);return a&&a.index===0&&a[0].length===b.length});d.addMethod("nonalphamin",function(c,d,b){var a;if(b){a=c.match(/\W/g);a=a&&a.length>=b}return a});if(d.methods.extension){b.addSingleVal("accept","mimtype");b.addSingleVal("extension","extension")}else b.addSingleVal("extension","extension","accept");b.addSingleVal("regex","pattern");b.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url");b.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range");b.add("equalto",["other"],function(b){var i=h(b.element.name),j=b.params.other,d=g(j,i),e=a(b.form).find(":input").filter("[name='"+f(d)+"']")[0];c(b,"equalTo",e)});b.add("required",function(a){(a.element.tagName.toUpperCase()!=="INPUT"||a.element.type.toUpperCase()!=="CHECKBOX")&&c(a,"required",true)});b.add("remote",["url","type","additionalfields"],function(b){var d={url:b.params.url,type:b.params.type||"GET",data:{}},e=h(b.element.name);a.each(j(b.params.additionalfields||b.element.name),function(i,h){var c=g(h,e);d.data[c]=function(){return a(b.form).find(":input").filter("[name='"+f(c)+"']").val()}});c(b,"remote",d)});b.add("password",["min","nonalphamin","regex"],function(a){a.params.min&&c(a,"minlength",a.params.min);a.params.nonalphamin&&c(a,"nonalphamin",a.params.nonalphamin);a.params.regex&&c(a,"regex",a.params.regex)});a(function(){d.unobtrusive.parse(document)})})(jQuery); 20 | -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Hexo/js/mobile.js: -------------------------------------------------------------------------------- 1 | define([], function () { 2 | var _isShow = false; 3 | var $tag, $aboutme, $friends; 4 | 5 | var ctn, radio, scaleW, idx, basicwrap; 6 | 7 | //第一步 -- 初始化 8 | var reset = function () { 9 | //设定窗口比率 10 | radio = document.body.scrollHeight / document.body.scrollWidth; 11 | //设定一页的宽度 12 | scaleW = document.body.scrollWidth; 13 | //设定初始的索引值 14 | idx = 0; 15 | }; 16 | //第一步 -- 组合 17 | var combine = function () { 18 | if ($tag) { 19 | document.getElementById("js-mobile-tagcloud").innerHTML = $tag.innerHTML; 20 | } 21 | if ($aboutme) { 22 | document.getElementById("js-mobile-aboutme").innerHTML = $aboutme.innerHTML; 23 | } 24 | if ($friends) { 25 | document.getElementById("js-mobile-friends").innerHTML = $friends.innerHTML; 26 | } 27 | } 28 | //第三步 -- 根据数据渲染DOM 29 | var renderDOM = function () { 30 | //生成节点 31 | var $viewer = document.createElement("div"); 32 | $viewer.id = "viewer"; 33 | $viewer.className = "hide"; 34 | $tag = document.getElementById("js-tagcloud"); 35 | $aboutme = document.getElementById("js-aboutme"); 36 | $friends = document.getElementById("js-friends"); 37 | var tagStr = $tag ? '标签
    ' : ""; 38 | var friendsStr = $friends ? '友情链接
    ' : ""; 39 | var aboutmeStr = $aboutme ? '关于我
    ' : ""; 40 | 41 | $viewer.innerHTML = '
    \ 42 |
    \ 43 |
    '+ aboutmeStr + friendsStr + tagStr + '
    \ 44 |
    \ 45 |
    \ 46 |
    '; 47 | 48 | //主要图片节点 49 | document.getElementsByTagName("body")[0].appendChild($viewer); 50 | var wrap = document.getElementById("viewer-box"); 51 | basicwrap = wrap; 52 | wrap.style.height = document.body.scrollHeight + 'px'; 53 | }; 54 | 55 | var show = function (target, idx) { 56 | document.getElementById("viewer").className = ""; 57 | setTimeout(function () { 58 | basicwrap.className = "anm-swipe"; 59 | }, 0); 60 | _isShow = true; 61 | document.ontouchstart = function (e) { 62 | if (e.target.tagName != "A") { 63 | return false; 64 | } 65 | } 66 | } 67 | 68 | var hide = function () { 69 | document.getElementById("viewer-box").className = ""; 70 | _isShow = false; 71 | document.ontouchstart = function () { 72 | return true; 73 | } 74 | } 75 | 76 | //第四步 -- 绑定 DOM 事件 77 | var bindDOM = function () { 78 | var scaleW = scaleW; 79 | 80 | //滑动隐藏 81 | document.getElementById("viewer-box").addEventListener("webkitTransitionEnd", function () { 82 | 83 | if (_isShow == false) { 84 | document.getElementById("viewer").className = "hide"; 85 | _isShow = true; 86 | } else { 87 | } 88 | 89 | }, false); 90 | 91 | //点击展示和隐藏 92 | ctn.addEventListener("touchend", function () { 93 | show(); 94 | }, false); 95 | 96 | var $right = document.getElementsByClassName("viewer-box-r")[0]; 97 | var touchStartTime; 98 | var touchEndTime; 99 | $right.addEventListener("touchstart", function () { 100 | touchStartTime = +new Date(); 101 | }, false); 102 | $right.addEventListener("touchend", function () { 103 | touchEndTime = +new Date(); 104 | if (touchEndTime - touchStartTime < 300) { 105 | hide(); 106 | } 107 | touchStartTime = 0; 108 | touchEndTime = 0; 109 | }, false); 110 | 111 | //滚动样式 112 | var $overlay = $("#mobile-nav .overlay"); 113 | var $header = $(".js-mobile-header"); 114 | window.onscroll = function () { 115 | var scrollTop = document.documentElement.scrollTop + document.body.scrollTop; 116 | if (scrollTop >= 69) { 117 | $overlay.addClass("fixed"); 118 | } else { 119 | $overlay.removeClass("fixed"); 120 | } 121 | if (scrollTop >= 160) { 122 | $header.removeClass("hide").addClass("fixed"); 123 | } else { 124 | $header.addClass("hide").removeClass("fixed"); 125 | } 126 | }; 127 | $header[0].addEventListener("touchstart", function () { 128 | $('html, body').animate({ scrollTop: 0 }, 'slow'); 129 | }, false); 130 | }; 131 | 132 | var resetTags = function () { 133 | var tags = $(".tagcloud a"); 134 | tags.css({ "font-size": "12px" }); 135 | for (var i = 0, len = tags.length; i < len; i++) { 136 | var num = tags.eq(i).html().length % 5 + 1; 137 | tags[i].className = ""; 138 | tags.eq(i).addClass("color" + num); 139 | } 140 | } 141 | 142 | return { 143 | init: function () { 144 | //构造函数需要的参数 145 | ctn = document.getElementsByClassName("slider-trigger")[0]; 146 | //构造四步 147 | reset(); 148 | renderDOM(); 149 | combine(); 150 | bindDOM(); 151 | resetTags(); 152 | } 153 | } 154 | }) -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Hexo/css/fancybox.css: -------------------------------------------------------------------------------- 1 | /*! fancyBox v2.1.5 fancyapps.com | fancyapps.com/fancybox/#license */ 2 | .fancybox-wrap, 3 | .fancybox-skin, 4 | .fancybox-outer, 5 | .fancybox-inner, 6 | .fancybox-image, 7 | .fancybox-wrap iframe, 8 | .fancybox-wrap object, 9 | .fancybox-nav, 10 | .fancybox-nav span, 11 | .fancybox-tmp 12 | { 13 | padding: 0; 14 | margin: 0; 15 | border: 0; 16 | outline: none; 17 | vertical-align: top; 18 | } 19 | 20 | .fancybox-wrap { 21 | position: absolute; 22 | top: 0; 23 | left: 0; 24 | z-index: 8020; 25 | } 26 | 27 | .fancybox-skin { 28 | position: relative; 29 | background: #f9f9f9; 30 | color: #444; 31 | text-shadow: none; 32 | -webkit-border-radius: 4px; 33 | -moz-border-radius: 4px; 34 | border-radius: 4px; 35 | } 36 | 37 | .fancybox-opened { 38 | z-index: 8030; 39 | } 40 | 41 | .fancybox-opened .fancybox-skin { 42 | -webkit-box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5); 43 | -moz-box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5); 44 | box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5); 45 | } 46 | 47 | .fancybox-outer, .fancybox-inner { 48 | position: relative; 49 | } 50 | 51 | .fancybox-inner { 52 | overflow: hidden; 53 | } 54 | 55 | .fancybox-type-iframe .fancybox-inner { 56 | -webkit-overflow-scrolling: touch; 57 | } 58 | 59 | .fancybox-error { 60 | color: #444; 61 | font: 14px/20px "Helvetica Neue",Helvetica,Arial,sans-serif; 62 | margin: 0; 63 | padding: 15px; 64 | white-space: nowrap; 65 | } 66 | 67 | .fancybox-image, .fancybox-iframe { 68 | display: block; 69 | width: 100%; 70 | height: 100%; 71 | } 72 | 73 | .fancybox-image { 74 | max-width: 100%; 75 | max-height: 100%; 76 | } 77 | 78 | #fancybox-loading, .fancybox-close, .fancybox-prev span, .fancybox-next span { 79 | background-image: url(fancybox_sprite.png); 80 | } 81 | 82 | #fancybox-loading { 83 | position: fixed; 84 | top: 50%; 85 | left: 50%; 86 | margin-top: -22px; 87 | margin-left: -22px; 88 | background-position: 0 -108px; 89 | opacity: 0.8; 90 | cursor: pointer; 91 | z-index: 8060; 92 | } 93 | 94 | #fancybox-loading div { 95 | width: 44px; 96 | height: 44px; 97 | background: url(fancybox_loading.gif) center center no-repeat; 98 | } 99 | 100 | .fancybox-close { 101 | position: absolute; 102 | top: -18px; 103 | right: -18px; 104 | width: 36px; 105 | height: 36px; 106 | cursor: pointer; 107 | z-index: 8040; 108 | } 109 | 110 | .fancybox-nav { 111 | position: absolute; 112 | top: 0; 113 | width: 40%; 114 | height: 100%; 115 | cursor: pointer; 116 | text-decoration: none; 117 | background: transparent url(blank.gif); /* helps IE */ 118 | -webkit-tap-highlight-color: rgba(0,0,0,0); 119 | z-index: 8040; 120 | } 121 | 122 | .fancybox-prev { 123 | left: 0; 124 | } 125 | 126 | .fancybox-next { 127 | right: 0; 128 | } 129 | 130 | .fancybox-nav span { 131 | position: absolute; 132 | top: 50%; 133 | width: 36px; 134 | height: 34px; 135 | margin-top: -18px; 136 | cursor: pointer; 137 | z-index: 8040; 138 | visibility: hidden; 139 | } 140 | 141 | .fancybox-prev span { 142 | left: 10px; 143 | background-position: 0 -36px; 144 | } 145 | 146 | .fancybox-next span { 147 | right: 10px; 148 | background-position: 0 -72px; 149 | } 150 | 151 | .fancybox-nav:hover span { 152 | visibility: visible; 153 | } 154 | 155 | .fancybox-tmp { 156 | position: absolute; 157 | top: -99999px; 158 | left: -99999px; 159 | max-width: 99999px; 160 | max-height: 99999px; 161 | overflow: visible !important; 162 | } 163 | 164 | /* Overlay helper */ 165 | 166 | .fancybox-lock { 167 | overflow: visible !important; 168 | width: auto; 169 | } 170 | 171 | .fancybox-lock body { 172 | overflow: hidden !important; 173 | } 174 | 175 | .fancybox-lock-test { 176 | overflow-y: hidden !important; 177 | } 178 | 179 | .fancybox-overlay { 180 | position: absolute; 181 | top: 0; 182 | left: 0; 183 | overflow: hidden; 184 | display: none; 185 | z-index: 8010; 186 | background: url(fancybox_overlay.png); 187 | } 188 | 189 | .fancybox-overlay-fixed { 190 | position: fixed; 191 | bottom: 0; 192 | right: 0; 193 | } 194 | 195 | .fancybox-lock .fancybox-overlay { 196 | overflow: auto; 197 | overflow-y: scroll; 198 | } 199 | 200 | /* Title helper */ 201 | 202 | .fancybox-title { 203 | visibility: hidden; 204 | font: normal 13px/20px "Helvetica Neue",Helvetica,Arial,sans-serif; 205 | position: relative; 206 | text-shadow: none; 207 | z-index: 8050; 208 | } 209 | 210 | .fancybox-opened .fancybox-title { 211 | visibility: visible; 212 | } 213 | 214 | .fancybox-title-float-wrap { 215 | position: absolute; 216 | bottom: 0; 217 | right: 50%; 218 | margin-bottom: -35px; 219 | z-index: 8050; 220 | text-align: center; 221 | } 222 | 223 | .fancybox-title-float-wrap .child { 224 | display: inline-block; 225 | margin-right: -100%; 226 | padding: 2px 20px; 227 | background: transparent; /* Fallback for web browsers that doesn't support RGBa */ 228 | background: rgba(0, 0, 0, 0.8); 229 | -webkit-border-radius: 15px; 230 | -moz-border-radius: 15px; 231 | border-radius: 15px; 232 | text-shadow: 0 1px 2px #222; 233 | color: #FFF; 234 | font-weight: bold; 235 | line-height: 24px; 236 | text-align: left; 237 | } 238 | 239 | .fancybox-title-outside-wrap { 240 | position: relative; 241 | margin-top: 10px; 242 | color: #fff; 243 | } 244 | 245 | .fancybox-title-inside-wrap { 246 | padding-top: 10px; 247 | } 248 | 249 | .fancybox-title-over-wrap { 250 | position: absolute; 251 | bottom: 0; 252 | left: 0; 253 | color: #fff; 254 | padding: 10px; 255 | background: #000; 256 | background: rgba(0, 0, 0, .8); 257 | } 258 | 259 | /*Retina graphics!*/ 260 | @media only screen and (-webkit-min-device-pixel-ratio: 1.5), 261 | only screen and (min--moz-device-pixel-ratio: 1.5), 262 | only screen and (min-device-pixel-ratio: 1.5){ 263 | 264 | #fancybox-loading, .fancybox-close, .fancybox-prev span, .fancybox-next span { 265 | background-image: url(fancybox_sprite@2x.png); 266 | background-size: 44px 152px; /*The size of the normal image, half the size of the hi-res image*/ 267 | } 268 | 269 | #fancybox-loading div { 270 | background-image: url(fancybox_loading@2x.gif); 271 | background-size: 24px 24px; /*The size of the normal image, half the size of the hi-res image*/ 272 | } 273 | } -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Hexo/js/jquery.lazyload.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author littenli 3 | * @date 2014-03-10 version 0.2 4 | * @description 图片延时加载,裂图替换,图片错误上报处理 5 | * @update 增加非可视区域延时加载 6 | * @example $(".container").lazy(options); 7 | * 遍历$(".container")节点内的img节点,都应用lazyload;若此节点为img节点,只应用此节点 8 | * options.srcSign {String} 可为空.img节点约定的src标志,默认为lazy-src;响应img节点为: 9 | * options.errCallBack {Function} 可为空.提供img加载失败回调,供业务额外去处理加载失败逻辑 10 | * options.container {Dom} 提供容器节点内可视区域的加载能力,默认为window 11 | */ 12 | (function (root, factory) { 13 | }(this, function ($) { 14 | $.fn.lazyload = function (options) { 15 | return this.each(function () { 16 | 17 | options = options || {}; 18 | var defualts = {}; 19 | 20 | var opts = $.extend({}, defualts, options); 21 | var obj = $(this); 22 | var dom = this; 23 | 24 | var srcSign = options.srcSign || "lazy-src"; 25 | var errCallBack = options.errCallBack || function () { }; 26 | var container = options.container || $(window); 27 | 28 | /** 29 | * @description src正常 30 | */ 31 | var imgload = function (e, target) { 32 | //todo: 上报 33 | } 34 | 35 | /** 36 | * @description src失效 37 | */ 38 | var imgerr = function (e, target, fn, src) { 39 | if (target[0].src && (target[0].src.indexOf("img-err.png") > 0 || target[0].src.indexOf("img-err2.png") > 0)) { 40 | return; 41 | } 42 | var w = target.width(); 43 | var h = target.height(); 44 | target[0].src = "/img/img-err.png"; 45 | 46 | fn(); 47 | //todo: 上报 48 | }; 49 | 50 | var tempImg = function (target) { 51 | var w = target.width(); 52 | var h = target.height(); 53 | var t = target.offset().top; 54 | var l = target.offset().left; 55 | var tempDom = target.clone().addClass("lazy-loding").insertBefore(target); 56 | tempDom[0].src = "/img/img-loading.png"; 57 | target.hide(); 58 | } 59 | /** 60 | * @description src替换,loading过程中添加类lazy-loading; 61 | */ 62 | var setSrc = function (target, srcSign, errCallBack) { 63 | 64 | if (target.attr("src")) return; 65 | 66 | if (options.cache == true) { 67 | console.log(target); 68 | //存进localstorage 69 | var canvas1 = document.getElementById('canvas1'); 70 | var ctx1 = canvas1.getContext('2d'); 71 | var imageData; 72 | 73 | image = new Image(); 74 | image.src = target.attr(srcSign); 75 | image.onload = function () { 76 | ctx1.drawImage(image, 0, 0); 77 | imageData = ctx1.getImageData(0, 0, 500, 250); 78 | console.log(imageData); 79 | } 80 | 81 | } else { 82 | tempImg(target); 83 | 84 | var src = target.attr(srcSign); 85 | target[0].onerror = function (e) { 86 | imgerr(e, target, errCallBack, src); 87 | }; 88 | target[0].onload = function (e) { 89 | target.parent().find(".lazy-loding").remove(); 90 | target.show(); 91 | imgload(e, target); 92 | } 93 | target[0].src = src; 94 | } 95 | } 96 | 97 | /** 98 | * @description 重组 99 | */ 100 | opts.cache = []; 101 | 102 | if (dom.tagName == "IMG") { 103 | var data = { 104 | obj: obj, 105 | tag: "img", 106 | url: obj.attr(srcSign) 107 | }; 108 | opts.cache.push(data); 109 | } else { 110 | var imgArr = obj.find("img"); 111 | imgArr.each(function (index) { 112 | var node = this.nodeName.toLowerCase(), url = $(this).attr(srcSign); 113 | //重组 114 | var data = { 115 | obj: imgArr.eq(index), 116 | tag: node, 117 | url: url 118 | }; 119 | opts.cache.push(data); 120 | }); 121 | } 122 | 123 | 124 | //动态显示数据 125 | var scrollHandle = function () { 126 | var contHeight = container.height(); 127 | var contop; 128 | if ($(window).get(0) === window) { 129 | contop = $(window).scrollTop(); 130 | } else { 131 | contop = container.offset().top; 132 | } 133 | $.each(opts.cache, function (i, data) { 134 | var o = data.obj, tag = data.tag, url = data.url, post, posb; 135 | if (o) { 136 | post = o.offset().top - contop, post + o.height(); 137 | 138 | if ((post >= 0 && post < contHeight) || (posb > 0 && posb <= contHeight)) { 139 | if (url) { 140 | //在浏览器窗口内 141 | if (tag === "img") { 142 | //改变src 143 | setSrc(o, srcSign, errCallBack); 144 | } 145 | } 146 | data.obj = null; 147 | } 148 | } 149 | }); 150 | } 151 | 152 | //加载完毕即执行 153 | scrollHandle(); 154 | //滚动执行 155 | container.bind("scroll", scrollHandle); 156 | container.bind("resize", scrollHandle); 157 | 158 | }); 159 | }; 160 | 161 | })); -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Default/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @ViewBag.Title - @ViewBag.Description 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 17 | 18 | 19 |
    20 |
    34 |
    35 |
    36 |
    37 |
    38 |
    39 | @if (Model is Post) 40 | { 41 |

    @ViewBag.Title

    42 |

    Published on @Model.Time.ToString()

    43 | } 44 | else 45 | { 46 |

    @ViewBag.Title@(ViewBag.Position == "home" ? "" : (" » " + ViewBag.Position.ToUpper()))@(ViewContext.RouteData.Values["tag"] == null ? "" : (" » " + ViewContext.RouteData.Values["tag"].ToString().ToUpper()))@(ViewContext.RouteData.Values["year"] == null ? "" : (" » " + ViewContext.RouteData.Values["year"].ToString()))@(ViewContext.RouteData.Values["month"] == null ? "" : (" » " + ViewContext.RouteData.Values["month"].ToString()))@(ViewContext.RouteData.Values["p"] == null ? "" : (" » PAGE" + ViewContext.RouteData.Values["p"].ToString()))

    47 | } 48 |
    49 |
    50 |
    51 |
    52 |
    53 |
    54 |
    55 | 56 |
    57 | @RenderBody() 58 |
    59 | 86 |
    87 | 88 | 89 | 90 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Moon/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | @ViewBag.Title - @ViewBag.Description 12 | 13 | 14 |
    15 |
    16 |
    17 |
    18 |
    19 |
    20 |
    21 |
    22 |
    23 | 29 | 38 |
    39 |
    40 | 41 |
    42 | @RenderBody() 43 |
    44 | 123 |
    124 |
    125 |
    126 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Moon/scripts/Main.js: -------------------------------------------------------------------------------- 1 | var autoplay = false; 2 | 3 | var blog = window.blog = { 4 | autoHighlight: function () { 5 | if (window.enable_autoHighlight == undefined) { 6 | window.enable_autoHighlight = false 7 | } 8 | if (!window.enable_autoHighlight) { 9 | return 10 | } 11 | var a = null; 12 | $(window).scroll(function () { 13 | var c = $(window).scrollTop(); 14 | var b = $(window).height(); 15 | $(window.enable_autoHighlight).each(function () { 16 | var d = c + (b * 0.35); 17 | if (d > $(this).offset().top && d < $(this).offset().top + $(this).height()) { 18 | if (a !== null) { 19 | a.removeClass("active") 20 | } 21 | a = $(this).addClass("active"); 22 | return false 23 | } 24 | }) 25 | }).scroll() 26 | }, 27 | init: function () { 28 | var a = blog; 29 | a.autoHighlight(); 30 | } 31 | }; 32 | var lock = false; 33 | 34 | function BlogRoll(github) { 35 | if (confirm("Follow me on GitHub will add you here automatically. Do you want to follow me now?")) { 36 | window.location = "https://github.com/" + github; 37 | } 38 | } 39 | 40 | $(document).ready(function () { 41 | // Binding blog roll 42 | $('.sidebar-blog-roll').hover(function () { 43 | var name = $(this).find('img').attr('alt'); 44 | if (!name) 45 | return; 46 | var pos = $(this).position(); 47 | var top = pos.top + $(this).outerHeight(); 48 | var left = pos.left + $(this).outerWidth() / 2; 49 | $('.sidebar-blog-roll-tip').css('top', top); 50 | $('.sidebar-blog-roll-tip').css('left', left); 51 | $('.sidebar-blog-roll-tip').text(name); 52 | $('.sidebar-blog-roll-tip').addClass('active'); 53 | }, function () { 54 | $('.sidebar-blog-roll-tip').removeClass('active'); 55 | }); 56 | 57 | $('#lstTemplate').change(function () { 58 | window.location = "/home/template?folder=" + $(this).val(); 59 | }); 60 | Highlight(); 61 | $('#btnSearch').click(function () { 62 | if ($('#txtSearch').val()) 63 | window.location = "/Search/" + encodeURIComponent($('#txtSearch').val()); 64 | else 65 | window.location = "/"; 66 | }); 67 | 68 | $('#txtSearch').keydown(function (e) { 69 | if (e.keyCode == 13) 70 | { 71 | if ($('#txtSearch').val()) 72 | window.location = "/Search/" + encodeURIComponent($('#txtSearch').val()); 73 | else 74 | window.location = "/"; 75 | } 76 | }); 77 | 78 | blog.init(); 79 | if (typeof (ArticleList) != "undefined") 80 | { 81 | LoadArticles(); 82 | $(window).scroll(function () { 83 | totalheight = parseFloat($(window).height()) + parseFloat($(window).scrollTop()); 84 | if ($(document).height() <= totalheight) { 85 | LoadArticles(); 86 | } 87 | }); 88 | } 89 | }); 90 | 91 | function DropEnable() { 92 | $('.markdown-textbox').unbind().each(function () { 93 | var editor = $(this); 94 | $(this).dragDropOrPaste(function () { 95 | var pos = editor.getCursorPosition(); 96 | var str = editor.val(); 97 | if (pos == 0 && !editor.is(':focus')) 98 | pos = str.length; 99 | editor.val(str.substr(0, pos) + '\r\n![Upload](Uploading...)\r\n' + str.substr(pos)); 100 | }, 101 | function (result) { 102 | var content = editor.val().replace('![Upload](Uploading...)', '![' + result.FileName + '](/file/download/' + result.Id + ')'); 103 | editor.val(content); 104 | }); 105 | }); 106 | } 107 | 108 | function savePost(url) { 109 | $.post('/admin/post/edit', { 110 | __RequestVerificationToken: $('#frmSavePost input[name="__RequestVerificationToken"]').val(), 111 | title: $('#txtTitle').val(), 112 | id: url, 113 | newId: $('#txtUrl').val(), 114 | content: $('#txtContent').val(), 115 | tags: $('#txtTags').val(), 116 | catalog: $('#lstCatalogs').val(), 117 | isPage: $('#chkIsPage').is(':checked') 118 | }, function (html) { 119 | $('.post-body').html(html); 120 | $('.post-edit').slideUp(); 121 | $('.post-body').slideDown(); 122 | Highlight(); 123 | popResult('文章保存成功'); 124 | }); 125 | } 126 | 127 | function editCatalog(id) { 128 | var parent = $('tr[data-catalog="' + id + '"]'); 129 | parent.find('.display').hide(); 130 | parent.find('.editing').fadeIn(); 131 | } 132 | 133 | function cancelEditCatalog() { 134 | $('.editing').hide(); 135 | $('.display').fadeIn(); 136 | } 137 | 138 | function deleteCatalog(id) { 139 | var parent = $('tr[data-catalog="' + id + '"]'); 140 | parent.remove(); 141 | $.post('/admin/catalog/delete', { 142 | id: id, 143 | __RequestVerificationToken: $('#frmDeleteCatalog input[name="__RequestVerificationToken"]').val() 144 | }, function () { 145 | popResult('删除成功'); 146 | }); 147 | } 148 | 149 | function saveCatalog(id) { 150 | var parent = $('tr[data-catalog="' + id + '"]'); 151 | $.post('/admin/catalog/edit/', { 152 | id: id, 153 | __RequestVerificationToken: $('#frmEditCatalog input[name="__RequestVerificationToken"]').val(), 154 | newId: parent.find('.title').val(), 155 | title: parent.find('.title-zh').val(), 156 | order: parent.find('.order').val() 157 | }, function () { 158 | parent.find('.d-title').html(parent.find('.title').val()); 159 | parent.find('.d-title-zh').html(parent.find('.title-zh').val()); 160 | parent.find('.d-order').html(parent.find('.order').val()); 161 | parent.find('.editing').hide(); 162 | parent.find('.display').fadeIn(); 163 | popResult('修改成功'); 164 | }); 165 | } 166 | 167 | function saveConfig() { 168 | $.post('/admin/index', $('#frmConfig').serialize(), function () { 169 | popResult('网站配置信息修改成功'); 170 | }); 171 | } 172 | 173 | function popResult(txt) { 174 | var msg = $('
    ' + txt + '
    '); 175 | msg.css('left', '50%'); 176 | $('body').append(msg); 177 | msg.css('margin-left', '-' + parseInt(msg.outerWidth() / 2) + 'px'); 178 | msg.removeClass('hide'); 179 | setTimeout(function () { 180 | msg.addClass('hide'); 181 | setTimeout(function () { 182 | msg.remove(); 183 | }, 400); 184 | }, 2600); 185 | } 186 | 187 | function uploadAttachment() { 188 | $('#uploadFile').unbind('change').change(function () { 189 | uploadFile(); 190 | }); 191 | $('#uploadFile').click(); 192 | } 193 | 194 | function uploadFile() { 195 | var formData = new FormData($('#frmAjaxUpload')[0]); 196 | var editor = $('.markdown-textbox'); 197 | var pos = editor.getCursorPosition(); 198 | var str = editor.val(); 199 | if (pos == 0 && !editor.is(':focus')) 200 | pos = str.length; 201 | editor.val(str.substr(0, pos) + '\r\n![Upload](Uploading...)\r\n' + str.substr(pos)); 202 | 203 | $.ajax({ 204 | url: '/file/upload', 205 | type: 'POST', 206 | data: formData, 207 | dataType: 'json', 208 | async: false, 209 | cache: false, 210 | contentType: false, 211 | processData: false, 212 | success: function (result) { 213 | var content = editor.val().replace('![Upload](Uploading...)', '![' + result.FileName + '](/file/download/' + result.Id + ')'); 214 | editor.val(content); 215 | }, 216 | error: function (returndata) { 217 | } 218 | }); 219 | } -------------------------------------------------------------------------------- /src/YuukoBlog/wwwroot/assets/Hexo/js/main.js: -------------------------------------------------------------------------------- 1 | require([], function () { 2 | 3 | var isMobileInit = false; 4 | var loadMobile = function () { 5 | require(['/assets/Hexo/js/mobile.js'], function (mobile) { 6 | mobile.init(); 7 | isMobileInit = true; 8 | }); 9 | } 10 | var isPCInit = false; 11 | var loadPC = function () { 12 | require(['/assets/Hexo/js/pc.js'], function (pc) { 13 | pc.init(); 14 | isPCInit = true; 15 | }); 16 | } 17 | 18 | var browser = { 19 | versions: function () { 20 | var u = window.navigator.userAgent; 21 | return { 22 | trident: u.indexOf('Trident') > -1, //IE内核 23 | presto: u.indexOf('Presto') > -1, //opera内核 24 | webKit: u.indexOf('AppleWebKit') > -1, //苹果、谷歌内核 25 | gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1, //火狐内核 26 | mobile: !!u.match(/AppleWebKit.*Mobile.*/), //是否为移动终端 27 | ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端 28 | android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1, //android终端或者uc浏览器 29 | iPhone: u.indexOf('iPhone') > -1 || u.indexOf('Mac') > -1, //是否为iPhone或者安卓QQ浏览器 30 | iPad: u.indexOf('iPad') > -1, //是否为iPad 31 | webApp: u.indexOf('Safari') == -1,//是否为web应用程序,没有头部与底部 32 | weixin: u.indexOf('MicroMessenger') == -1 //是否为微信浏览器 33 | }; 34 | }() 35 | } 36 | 37 | $(window).bind("resize", function () { 38 | if (isMobileInit && isPCInit) { 39 | $(window).unbind("resize"); 40 | return; 41 | } 42 | var w = $(window).width(); 43 | if (w >= 700) { 44 | loadPC(); 45 | } else { 46 | loadMobile(); 47 | } 48 | }); 49 | 50 | if (browser.versions.mobile === true || $(window).width() < 700) { 51 | loadMobile(); 52 | } else { 53 | loadPC(); 54 | } 55 | 56 | //是否使用fancybox 57 | if (yiliaConfig.fancybox === true) { 58 | require(['/assets/Hexo/js/jquery.fancybox.js'], function (pc) { 59 | var isFancy = $(".isFancy"); 60 | if (isFancy.length != 0) { 61 | var imgArr = $(".article-inner img"); 62 | for (var i = 0, len = imgArr.length; i < len; i++) { 63 | var src = imgArr.eq(i).attr("src"); 64 | var title = imgArr.eq(i).attr("alt"); 65 | imgArr.eq(i).replaceWith(""); 66 | } 67 | $(".article-inner .fancy-ctn").fancybox(); 68 | } 69 | }); 70 | 71 | } 72 | //是否开启动画 73 | if (yiliaConfig.animate === true) { 74 | 75 | require(['/assets/Hexo/js/jquery.lazyload.js'], function () { 76 | //avatar 77 | $(".js-avatar").attr("src", $(".js-avatar").attr("lazy-src")); 78 | $(".js-avatar")[0].onload = function () { 79 | $(".js-avatar").addClass("show"); 80 | } 81 | }); 82 | 83 | if (yiliaConfig.isHome === true) { 84 | //content 85 | function showArticle() { 86 | $(".article").each(function () { 87 | if ($(this).offset().top <= $(window).scrollTop() + $(window).height() && !($(this).hasClass('show'))) { 88 | $(this).removeClass("hidden").addClass("show"); 89 | $(this).addClass("is-hiddened"); 90 | } else { 91 | if (!$(this).hasClass("is-hiddened")) { 92 | $(this).addClass("hidden"); 93 | } 94 | } 95 | }); 96 | } 97 | $(window).on('scroll', function () { 98 | showArticle(); 99 | }); 100 | showArticle(); 101 | } 102 | 103 | } 104 | 105 | //是否新窗口打开链接 106 | if (yiliaConfig.open_in_new == true) { 107 | $(".article a[href]").attr("target", "_blank") 108 | } 109 | 110 | }); 111 | 112 | $(document).ready(function () { 113 | // Binding blog roll 114 | $('.sidebar-blog-roll').hover(function () { 115 | var name = $(this).find('img').attr('alt'); 116 | if (!name) 117 | return; 118 | var pos = $(this).position(); 119 | var top = pos.top + $(this).outerHeight(); 120 | var left = pos.left + $(this).outerWidth() / 2; 121 | $('.sidebar-blog-roll-tip').css('top', top); 122 | $('.sidebar-blog-roll-tip').css('left', left); 123 | $('.sidebar-blog-roll-tip').text(name); 124 | $('.sidebar-blog-roll-tip').addClass('active'); 125 | }, function () { 126 | $('.sidebar-blog-roll-tip').removeClass('active'); 127 | }); 128 | 129 | $('#lstTemplate').change(function () { 130 | window.location = "/home/template?folder=" + $(this).val(); 131 | }); 132 | 133 | Highlight(); 134 | }); 135 | 136 | function savePost(url) { 137 | $.post('/admin/post/edit', { 138 | __RequestVerificationToken: $('#frmSavePost input[name="__RequestVerificationToken"]').val(), 139 | title: $('#txtTitle').val(), 140 | id: url, 141 | newId: $('#txtUrl').val(), 142 | content: $('#txtContent').val(), 143 | tags: $('#txtTags').val(), 144 | catalog: $('#lstCatalogs').val(), 145 | isPage: $('#chkIsPage').is(':checked') 146 | }, function (html) { 147 | $('.post-body').html(html); 148 | $('.post-edit').slideUp(); 149 | $('.post-body').slideDown(); 150 | Highlight(); 151 | popResult('文章保存成功'); 152 | }); 153 | } 154 | 155 | function editCatalog(id) { 156 | var parent = $('tr[data-catalog="' + id + '"]'); 157 | parent.find('.display').hide(); 158 | parent.find('.editing').fadeIn(); 159 | } 160 | 161 | function cancelEditCatalog() { 162 | $('.editing').hide(); 163 | $('.display').fadeIn(); 164 | } 165 | 166 | function deleteCatalog(id) { 167 | var parent = $('tr[data-catalog="' + id + '"]'); 168 | parent.remove(); 169 | $.post('/admin/catalog/delete', { 170 | id: id, 171 | __RequestVerificationToken: $('#frmDeleteCatalog input[name="__RequestVerificationToken"]').val() 172 | }, function () { 173 | popResult('删除成功'); 174 | }); 175 | } 176 | 177 | function saveCatalog(id) { 178 | var parent = $('tr[data-catalog="' + id + '"]'); 179 | $.post('/admin/catalog/edit/', { 180 | id: id, 181 | __RequestVerificationToken: $('#frmEditCatalog input[name="__RequestVerificationToken"]').val(), 182 | newId: parent.find('.title').val(), 183 | title: parent.find('.title-zh').val(), 184 | order: parent.find('.order').val() 185 | }, function () { 186 | parent.find('.d-title').html(parent.find('.title').val()); 187 | parent.find('.d-title-zh').html(parent.find('.title-zh').val()); 188 | parent.find('.d-order').html(parent.find('.order').val()); 189 | parent.find('.editing').hide(); 190 | parent.find('.display').fadeIn(); 191 | popResult('修改成功'); 192 | }); 193 | } 194 | 195 | function saveConfig() { 196 | $.post('/admin/index', $('#frmConfig').serialize(), function () { 197 | popResult('网站配置信息修改成功'); 198 | }); 199 | } 200 | 201 | function popResult(txt) { 202 | var msg = $('
    ' + txt + '
    '); 203 | msg.css('left', '50%'); 204 | $('body').append(msg); 205 | msg.css('margin-left', '-' + parseInt(msg.outerWidth() / 2) + 'px'); 206 | msg.removeClass('hide'); 207 | setTimeout(function () { 208 | msg.addClass('hide'); 209 | setTimeout(function () { 210 | msg.remove(); 211 | }, 400); 212 | }, 2600); 213 | } 214 | 215 | function DropEnable() { 216 | $('.markdown-textbox').unbind().each(function () { 217 | var editor = $(this); 218 | $(this).dragDropOrPaste(function () { 219 | var pos = editor.getCursorPosition(); 220 | var str = editor.val(); 221 | if (pos == 0 && !editor.is(':focus')) 222 | pos = str.length; 223 | editor.val(str.substr(0, pos) + '\r\n![Upload](Uploading...)\r\n' + str.substr(pos)); 224 | }, 225 | function (result) { 226 | var content = editor.val().replace('![Upload](Uploading...)', '![' + result.FileName + '](/file/download/' + result.Id + ')'); 227 | editor.val(content); 228 | }); 229 | }); 230 | } 231 | -------------------------------------------------------------------------------- /src/YuukoBlog/Controllers/AdminController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.AspNetCore.Http; 5 | using Microsoft.EntityFrameworkCore; 6 | using Pomelo.Marked; 7 | using YuukoBlog.Filters; 8 | using YuukoBlog.Models; 9 | 10 | namespace YuukoBlog.Controllers 11 | { 12 | public class AdminController : BaseController 13 | { 14 | [AdminRequired] 15 | [HttpGet] 16 | [Route("Admin/Index")] 17 | public IActionResult Index() 18 | { 19 | return View(); 20 | } 21 | 22 | [AdminRequired] 23 | [HttpPost] 24 | [ValidateAntiForgeryToken] 25 | [Route("Admin/Index")] 26 | public IActionResult Index(Config config) 27 | { 28 | Configuration["Account"] = config.Account; 29 | Configuration["Password"] = config.Password; 30 | Configuration["Site"] = config.Site; 31 | Configuration["Description"] = config.Description; 32 | Configuration["Disqus"] = config.Disqus; 33 | Configuration["AvatarUrl"] = config.AvatarUrl; 34 | Configuration["AboutUrl"] = config.AboutUrl; 35 | Configuration["BlogRoll:GitHub"] = config.GitHub; 36 | Configuration["BlogRoll:Follower"] = config.Follower.ToString(); 37 | Configuration["BlogRoll:Following"] = config.Following.ToString(); 38 | return RedirectToAction("Index", "Admin"); 39 | } 40 | 41 | [GuestRequired] 42 | public IActionResult Login() 43 | { 44 | return View(); 45 | } 46 | 47 | [GuestRequired] 48 | [HttpPost] 49 | [ValidateAntiForgeryToken] 50 | public IActionResult Login(string Username, string Password) 51 | { 52 | var tmp = Configuration["Account"]; 53 | if (Username == Configuration["Account"] && Password == Configuration["Password"]) 54 | { 55 | HttpContext.Session.SetString("Admin", "true"); 56 | return RedirectToAction("Index", "Admin"); 57 | } 58 | else 59 | { 60 | return View(); 61 | } 62 | } 63 | 64 | [AdminRequired] 65 | [HttpPost] 66 | [ValidateAntiForgeryToken] 67 | [Route("Admin/Post/Edit")] 68 | public IActionResult PostEdit(string id, string newId, string tags, bool isPage, string title, Guid? catalog, string content) 69 | { 70 | var post = DB.Posts 71 | .Include(x => x.Tags) 72 | .Where(x => x.Url == id) 73 | .SingleOrDefault(); 74 | if (post == null) 75 | return Prompt(x => 76 | { 77 | x.StatusCode = 404; 78 | x.Title = SR["Not Found"]; 79 | x.Details = SR["The resources have not been found, please check your request."]; 80 | x.RedirectUrl = Url.Link("default", new { controller = "Home", action = "Index" }); 81 | x.RedirectText = SR["Back to home"]; 82 | }); 83 | var summary = ""; 84 | var flag = false; 85 | if (content != null) 86 | { 87 | var tmp = content.Split('\n'); 88 | if (tmp.Count() > 16) 89 | { 90 | for (var i = 0; i < 16; i++) 91 | { 92 | if (tmp[i].IndexOf("```") == 0) 93 | flag = !flag; 94 | summary += tmp[i] + '\n'; 95 | } 96 | if (flag) 97 | summary += "```\r\n"; 98 | summary += $"\r\n[{SR["Read More"]} »](/post/{newId})"; 99 | } 100 | else 101 | { 102 | summary = content; 103 | } 104 | } 105 | foreach (var t in post.Tags) 106 | DB.PostTags.Remove(t); 107 | post.Url = newId; 108 | post.Summary = summary; 109 | post.Title = title; 110 | post.Content = content; 111 | post.CatalogId = catalog; 112 | post.IsPage = isPage; 113 | if (!string.IsNullOrEmpty(tags)) 114 | { 115 | var _tags = tags.Split(','); 116 | foreach (var t in _tags) 117 | post.Tags.Add(new PostTag { PostId = post.Id, Tag = t.Trim(' ') }); 118 | } 119 | DB.SaveChanges(); 120 | return Content(Instance.Parse(content)); 121 | } 122 | 123 | [AdminRequired] 124 | [HttpPost] 125 | [ValidateAntiForgeryToken] 126 | [Route("Admin/Post/Delete")] 127 | public IActionResult PostDelete(string id) 128 | { 129 | var post = DB.Posts 130 | .Include(x => x.Tags) 131 | .Where(x => x.Url == id).SingleOrDefault(); 132 | 133 | if (post == null) 134 | return Prompt(x => 135 | { 136 | x.StatusCode = 404; 137 | x.Title = SR["Not Found"]; 138 | x.Details = SR["The resources have not been found, please check your request."]; 139 | x.RedirectUrl = Url.Link("default", new { controller = "Home", action = "Index" }); 140 | x.RedirectText = SR["Back to home"]; 141 | }); 142 | foreach (var t in post.Tags) 143 | DB.PostTags.Remove(t); 144 | DB.Posts.Remove(post); 145 | DB.SaveChanges(); 146 | return RedirectToAction("Index", "Home"); 147 | } 148 | 149 | [AdminRequired] 150 | [HttpPost] 151 | [ValidateAntiForgeryToken] 152 | [Route("Admin/Post/New")] 153 | public IActionResult PostNew() 154 | { 155 | var post = new Post 156 | { 157 | Id = Guid.NewGuid(), 158 | Url = Guid.NewGuid().ToString().Substring(0, 8), 159 | Title = SR["Untitled Post"], 160 | Content = "", 161 | Summary = "", 162 | CatalogId = null, 163 | IsPage = false, 164 | Time = DateTime.Now 165 | }; 166 | DB.Posts.Add(post); 167 | DB.SaveChanges(); 168 | return RedirectToAction("Post", "Post", new { id = post.Url }); 169 | } 170 | 171 | [AdminRequired] 172 | [HttpPost] 173 | [ValidateAntiForgeryToken] 174 | public IActionResult Logout() 175 | { 176 | HttpContext.Session.Clear(); 177 | return RedirectToAction("Index", "Home"); 178 | } 179 | 180 | [AdminRequired] 181 | public IActionResult Catalog() 182 | { 183 | return View(DB.Catalogs.OrderByDescending(x => x.PRI).ToList()); 184 | } 185 | 186 | [AdminRequired] 187 | [HttpPost] 188 | [ValidateAntiForgeryToken] 189 | [Route("Admin/Catalog/Delete")] 190 | public IActionResult CatalogDelete(string id) 191 | { 192 | var catalog = DB.Catalogs.Where(x => x.Url == id).SingleOrDefault(); 193 | if (catalog == null) 194 | return Prompt(x => 195 | { 196 | x.StatusCode = 404; 197 | x.Title = SR["Not Found"]; 198 | x.Details = SR["The resources have not been found, please check your request."]; 199 | x.RedirectUrl = Url.Link("default", new { controller = "Home", action = "Index" }); 200 | x.RedirectText = SR["Back to home"]; 201 | }); 202 | DB.Catalogs.Remove(catalog); 203 | DB.SaveChanges(); 204 | return Content("true"); 205 | } 206 | 207 | [AdminRequired] 208 | [HttpPost] 209 | [ValidateAntiForgeryToken] 210 | [Route("Admin/Catalog/Edit")] 211 | public IActionResult CatalogEdit(string id, string newId, string title, int pri) 212 | { 213 | var catalog = DB.Catalogs.Where(x => x.Url == id).SingleOrDefault(); 214 | if (catalog == null) 215 | return Prompt(x => 216 | { 217 | x.StatusCode = 404; 218 | x.Title = SR["Not Found"]; 219 | x.Details = SR["The resources have not been found, please check your request."]; 220 | x.RedirectUrl = Url.Link("default", new { controller = "Home", action = "Index" }); 221 | x.RedirectText = SR["Back to home"]; 222 | }); 223 | catalog.Url = newId; 224 | catalog.Title = title; 225 | catalog.PRI = pri; 226 | DB.SaveChanges(); 227 | return Content("true"); 228 | } 229 | 230 | [AdminRequired] 231 | [HttpPost] 232 | [ValidateAntiForgeryToken] 233 | [Route("Admin/Catalog/New")] 234 | public IActionResult CatalogNew() 235 | { 236 | var catalog = new Catalog 237 | { 238 | Url = Guid.NewGuid().ToString().Substring(0, 8), 239 | PRI = 0, 240 | Title = SR["New Catalog"] 241 | }; 242 | DB.Catalogs.Add(catalog); 243 | DB.SaveChanges(); 244 | return RedirectToAction("Catalog", "Admin"); 245 | } 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /src/YuukoBlog/Jobs/GitHubRelationJob.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Text.RegularExpressions; 6 | using System.Net.Http; 7 | using Microsoft.Extensions.Configuration; 8 | using Pomelo.AspNetCore.Extensions.BlobStorage.Models; 9 | using Pomelo.AspNetCore.TimedJob; 10 | using YuukoBlog.Models; 11 | 12 | namespace YuukoBlog.Jobs 13 | { 14 | public class GitHubRelationJob : Job 15 | { 16 | public GitHubRelationJob(IConfiguration config, BlogContext db) 17 | { 18 | Config = config; 19 | DB = db; 20 | } 21 | 22 | public IConfiguration Config { get; set; } 23 | 24 | public BlogContext DB { get; set; } 25 | 26 | /// 27 | /// 获取GitHub粉丝信息 28 | /// 29 | [Invoke(Interval = 1000 * 60 * 60 * 2, SkipWhileExecuting = true)] 30 | public async void PullGitHubRelation() 31 | { 32 | Console.WriteLine("Getting blog roll from github.com ..."); 33 | 34 | try 35 | { 36 | var rolls = new List(); 37 | 38 | if (Convert.ToBoolean(Config["BlogRoll:Follower"])) 39 | { 40 | var followers = await GetFollowers(); 41 | rolls.AddRange(followers); 42 | foreach (var x in followers) 43 | await UpdateUserInformation(x, BlogRollType.Follower); 44 | } 45 | 46 | if (Convert.ToBoolean(Config["BlogRoll:Following"])) 47 | { 48 | var following = await GetFollowing(); 49 | rolls.AddRange(following); 50 | foreach (var x in following) 51 | await UpdateUserInformation(x, BlogRollType.Following); 52 | } 53 | 54 | rolls = rolls.Distinct().ToList(); 55 | var delete = DB.BlogRolls.Where(x => !rolls.Contains(x.GitHubId)).ToList(); 56 | foreach (var x in delete) 57 | DB.BlogRolls.Remove(x); 58 | DB.SaveChanges(); 59 | } 60 | catch(Exception e) 61 | { 62 | Console.WriteLine(e.ToString()); 63 | } 64 | } 65 | 66 | #region Pull Blog Rolls From GitHub 67 | 68 | private async Task GetFollowersCount() 69 | { 70 | var url = "https://github.com"; 71 | using (var client = new HttpClient()) 72 | { 73 | client.BaseAddress = new Uri(url); 74 | var result = await client.GetAsync($"/{ Config["BlogRoll:GitHub"] }/followers"); 75 | var html = await result.Content.ReadAsStringAsync(); 76 | var regex = new Regex(@"(?<=)[\s\r\n0-9]{0,}"); 77 | return Convert.ToInt64(regex.Match(html).Value.Trim()); 78 | } 79 | } 80 | 81 | private async Task> GetFollowers() 82 | { 83 | var ret = new List(); 84 | var count = await GetFollowersCount(); 85 | var pages = (count + 50) / 51; 86 | var url = "https://github.com"; 87 | for (var i = 1; i <= pages; i++) 88 | { 89 | var endpoint = $"/{ Config["BlogRoll:GitHub"] }/followers?page=" + i; 90 | using (var client = new HttpClient()) 91 | { 92 | client.BaseAddress = new Uri(url); 93 | var result = await client.GetAsync(endpoint); 94 | var html = await result.Content.ReadAsStringAsync(); 95 | var regex = new Regex(@"(?<=)"); 96 | var matches = regex.Matches(html); 97 | foreach (Match x in matches) 98 | { 99 | var regex2 = new Regex(@"(?<=)"); 100 | ret.Add(regex2.Match(x.Value).Value); 101 | } 102 | } 103 | } 104 | return ret; 105 | } 106 | 107 | private async Task GetFollowingCount() 108 | { 109 | var url = "https://github.com"; 110 | using (var client = new HttpClient()) 111 | { 112 | client.BaseAddress = new Uri(url); 113 | var result = await client.GetAsync($"/{ Config["BlogRoll:GitHub"] }/following"); 114 | var html = await result.Content.ReadAsStringAsync(); 115 | var regex = new Regex(@"(?<=)[\s\r\n0-9]{0,}"); 116 | return Convert.ToInt64(regex.Match(html).Value.Trim()); 117 | } 118 | } 119 | 120 | private async Task> GetFollowing() 121 | { 122 | var ret = new List(); 123 | var count = await GetFollowersCount(); 124 | var pages = (count + 50) / 51; 125 | var url = "https://github.com"; 126 | for (var i = 1; i <= pages; i++) 127 | { 128 | var endpoint = $"/{ Config["BlogRoll:GitHub"] }/following?page=" + i; 129 | using (var client = new HttpClient()) 130 | { 131 | client.BaseAddress = new Uri(url); 132 | var result = await client.GetAsync(endpoint); 133 | var html = await result.Content.ReadAsStringAsync(); 134 | var regex = new Regex(@"(?<=)"); 135 | var matches = regex.Matches(html); 136 | foreach (Match x in matches) 137 | { 138 | var regex2 = new Regex(@"(?<=)"); 139 | ret.Add(regex2.Match(x.Value).Value); 140 | } 141 | } 142 | } 143 | return ret; 144 | } 145 | 146 | 147 | public async Task UpdateAvatar(string URL, Guid? Id = null) 148 | { 149 | try 150 | { 151 | Blob avatar; 152 | if (Id.HasValue) 153 | avatar = DB.Blobs.Single(x => x.Id == Id.Value); 154 | else 155 | { 156 | avatar = new Blob(); 157 | avatar.Id = Guid.NewGuid(); 158 | } 159 | using (var client = new HttpClient()) 160 | { 161 | client.BaseAddress = new Uri(URL); 162 | var result = await client.GetAsync(""); 163 | avatar.ContentType = result.Content.Headers.ContentType.ToString(); 164 | avatar.FileName = "avatar"; 165 | avatar.Time = DateTime.Now; 166 | avatar.Bytes = await result.Content.ReadAsByteArrayAsync(); 167 | avatar.ContentLength = avatar.Bytes.LongCount(); 168 | } 169 | if (!Id.HasValue) 170 | DB.Blobs.Add(avatar); 171 | DB.SaveChanges(); 172 | return avatar.Id; 173 | } 174 | catch(Exception e) 175 | { 176 | Console.WriteLine(e.ToString()); 177 | return default(Guid); 178 | } 179 | } 180 | 181 | private async Task UpdateUserInformation(string Username, BlogRollType Type) 182 | { 183 | try 184 | { 185 | var needInsert = true; 186 | var br = DB.BlogRolls.SingleOrDefault(x => x.GitHubId == Username); 187 | if (br == null) 188 | br = new BlogRoll(); 189 | else 190 | { 191 | needInsert = false; 192 | if (br.Type == BlogRollType.Follower && Type == BlogRollType.Following) 193 | return; 194 | } 195 | br.Type = Type; 196 | br.GitHubId = Username; 197 | var url = $"https://github.com/{ Config["BlogRoll:GitHub"] }"; 198 | using (var client = new HttpClient()) 199 | { 200 | client.BaseAddress = new Uri(url); 201 | var result = await client.GetAsync("/" + Username); 202 | var fullnameRegex = new Regex(@"(?<=
    ).*(?=
    )"); 203 | var html = await result.Content.ReadAsStringAsync(); 204 | br.NickName = fullnameRegex.Match(html).Value; 205 | if (string.IsNullOrEmpty(br.NickName)) 206 | br.NickName = Username; 207 | var websiteRegex = new Regex(@"(?<=
    span::selection,.CodeMirror-line>span>span::selection{background:#d7d4f0}.CodeMirror-crosshair{cursor:crosshair}.CodeMirror-line::-moz-selection,.CodeMirror-line>span::-moz-selection,.CodeMirror-line>span>span::-moz-selection{background:#d7d4f0}.cm-searching{background:#ffa;background:rgba(255,255,0,.4)}.cm-force-border{padding-right:.1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:''}span.CodeMirror-selectedtext{background:0 0}.CodeMirror{height:auto;min-height:300px;border:1px solid #ddd;border-bottom-left-radius:4px;border-bottom-right-radius:4px;padding:10px;font:inherit;z-index:1}.CodeMirror-scroll{min-height:300px}.CodeMirror-fullscreen{background:#fff;position:fixed!important;top:50px;left:0;right:0;bottom:0;height:auto;z-index:9}.CodeMirror-sided{width:50%!important}.editor-toolbar{position:relative;opacity:.6;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none;padding:0 10px;border-top:1px solid #bbb;border-left:1px solid #bbb;border-right:1px solid #bbb;border-top-left-radius:4px;border-top-right-radius:4px}.editor-toolbar:after,.editor-toolbar:before{display:block;content:' ';height:1px}.editor-toolbar:before{margin-bottom:8px}.editor-toolbar:after{margin-top:8px}.editor-toolbar:hover,.editor-wrapper input.title:focus,.editor-wrapper input.title:hover{opacity:.8}.editor-toolbar.fullscreen{width:100%;height:50px;overflow-x:auto;overflow-y:hidden;white-space:nowrap;padding-top:10px;padding-bottom:10px;box-sizing:border-box;background:#fff;border:0;position:fixed;top:0;left:0;opacity:1;z-index:9}.editor-toolbar.fullscreen::before{width:20px;height:50px;background:-moz-linear-gradient(left,rgba(255,255,255,1) 0,rgba(255,255,255,0) 100%);background:-webkit-gradient(linear,left top,right top,color-stop(0,rgba(255,255,255,1)),color-stop(100%,rgba(255,255,255,0)));background:-webkit-linear-gradient(left,rgba(255,255,255,1) 0,rgba(255,255,255,0) 100%);background:-o-linear-gradient(left,rgba(255,255,255,1) 0,rgba(255,255,255,0) 100%);background:-ms-linear-gradient(left,rgba(255,255,255,1) 0,rgba(255,255,255,0) 100%);background:linear-gradient(to right,rgba(255,255,255,1) 0,rgba(255,255,255,0) 100%);position:fixed;top:0;left:0;margin:0;padding:0}.editor-toolbar.fullscreen::after{width:20px;height:50px;background:-moz-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,1) 100%);background:-webkit-gradient(linear,left top,right top,color-stop(0,rgba(255,255,255,0)),color-stop(100%,rgba(255,255,255,1)));background:-webkit-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,1) 100%);background:-o-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,1) 100%);background:-ms-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,1) 100%);background:linear-gradient(to right,rgba(255,255,255,0) 0,rgba(255,255,255,1) 100%);position:fixed;top:0;right:0;margin:0;padding:0}.editor-toolbar a{display:inline-block;text-align:center;text-decoration:none!important;color:#2c3e50!important;width:30px;height:30px;margin:0;border:1px solid transparent;border-radius:3px;cursor:pointer}.editor-toolbar a.active,.editor-toolbar a:hover{background:#fcfcfc;border-color:#95a5a6}.editor-toolbar a:before{line-height:30px}.editor-toolbar i.separator{display:inline-block;width:0;border-left:1px solid #d9d9d9;border-right:1px solid #fff;color:transparent;text-indent:-10px;margin:0 6px}.editor-toolbar a.fa-header-x:after{font-family:Arial,"Helvetica Neue",Helvetica,sans-serif;font-size:65%;vertical-align:text-bottom;position:relative;top:2px}.editor-toolbar a.fa-header-1:after{content:"1"}.editor-toolbar a.fa-header-2:after{content:"2"}.editor-toolbar a.fa-header-3:after{content:"3"}.editor-toolbar a.fa-header-bigger:after{content:"▲"}.editor-toolbar a.fa-header-smaller:after{content:"▼"}.editor-toolbar.disabled-for-preview a:not(.no-disable){pointer-events:none;background:#fff;border-color:transparent;text-shadow:inherit}@media only screen and (max-width:700px){.editor-toolbar a.no-mobile{display:none}}.editor-statusbar{padding:8px 10px;font-size:12px;color:#959694;text-align:right}.editor-statusbar span{display:inline-block;min-width:4em;margin-left:1em}.editor-preview,.editor-preview-side{padding:10px;background:#fafafa;overflow:auto;display:none;box-sizing:border-box}.editor-statusbar .lines:before{content:'lines: '}.editor-statusbar .words:before{content:'words: '}.editor-statusbar .characters:before{content:'characters: '}.editor-preview{position:absolute;width:100%;height:100%;top:0;left:0;z-index:7}.editor-preview-side{position:fixed;bottom:0;width:50%;top:50px;right:0;z-index:9;border:1px solid #ddd}.editor-preview-active,.editor-preview-active-side{display:block}.editor-preview-side>p,.editor-preview>p{margin-top:0}.editor-preview pre,.editor-preview-side pre{background:#eee;margin-bottom:10px}.editor-preview table td,.editor-preview table th,.editor-preview-side table td,.editor-preview-side table th{border:1px solid #ddd;padding:5px}.CodeMirror .CodeMirror-code .cm-tag{color:#63a35c}.CodeMirror .CodeMirror-code .cm-attribute{color:#795da3}.CodeMirror .CodeMirror-code .cm-string{color:#183691}.CodeMirror .CodeMirror-selected{background:#d9d9d9}.CodeMirror .CodeMirror-code .cm-header-1{font-size:200%;line-height:200%}.CodeMirror .CodeMirror-code .cm-header-2{font-size:160%;line-height:160%}.CodeMirror .CodeMirror-code .cm-header-3{font-size:125%;line-height:125%}.CodeMirror .CodeMirror-code .cm-header-4{font-size:110%;line-height:110%}.CodeMirror .CodeMirror-code .cm-comment{background:rgba(0,0,0,.05);border-radius:2px}.CodeMirror .CodeMirror-code .cm-link{color:#7f8c8d}.CodeMirror .CodeMirror-code .cm-url{color:#aab2b3}.CodeMirror .CodeMirror-code .cm-strikethrough{text-decoration:line-through}.CodeMirror .CodeMirror-placeholder{opacity:.5}.CodeMirror .cm-spell-error:not(.cm-url):not(.cm-comment):not(.cm-tag):not(.cm-word){background:rgba(255,0,0,.15)} -------------------------------------------------------------------------------- /src/YuukoBlog/Views/Hexo/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | @ViewBag.Title - @ViewBag.Description 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 |
    16 |
    17 |
    18 | 142 |
    143 | 144 |
    145 |
    146 | 182 | 183 |
    184 | @RenderBody() 185 |
    186 | 187 |
    188 |
    189 | 203 |
    204 |
    205 |
    206 | 207 | 208 | 209 | 210 | 211 | 212 | 225 |
    226 | 227 | --------------------------------------------------------------------------------