1
0

Added blog

This commit is contained in:
Michael Gordeev
2020-05-24 02:26:07 +03:00
parent 4c79f053a6
commit fc2b2eee37
15 changed files with 756 additions and 13 deletions
@@ -53,6 +53,7 @@
<h1>Useufl links</h1> <h1>Useufl links</h1>
<ul> <ul>
<li><a asp-area="" asp-controller="Blog" asp-action="Tags" asp-route-id="FoxTube">Dev Notes</a></li>
<li><a asp-action="Privacy">Privacy policy</a></li> <li><a asp-action="Privacy">Privacy policy</a></li>
<li><a target="_blank" href="//youtube.com/t/privacy">YouTube privacy policy</a></li> <li><a target="_blank" href="//youtube.com/t/privacy">YouTube privacy policy</a></li>
<li><a target="_blank" href="//youtube.com/t/terms">YouTube terms of use</a></li> <li><a target="_blank" href="//youtube.com/t/terms">YouTube terms of use</a></li>
@@ -0,0 +1,73 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using MyWebsite.Models.Databases;
using MyWebsite.ViewModels;
using Newtonsoft.Json;
using Google.Apis.Blogger.v3.Data;
namespace MyWebsite.Controllers
{
public class BlogController : ExtendedController
{
private string apiKey = Startup.BlogspotAPI;
private const string blogId = "8566398713922921363";
public BlogController(DatabaseContext context) : base(context) { }
public async Task<IActionResult> Index(int pageNumber = 0)
{
using HttpClient client = new HttpClient();
string query = $"https://blogger.googleapis.com/v3/blogs/{blogId}/posts?fetchBodies=false&fetchImages=true&maxResults=10&orderBy=PUBLISHED&view=READER&key={apiKey}";
string response;
PostList list;
for (int k = 0; k < pageNumber; k++)
{
response = await client.GetStringAsync(new Uri(query)).ConfigureAwait(false);
list = JsonConvert.DeserializeObject<PostList>(response);
if (string.IsNullOrWhiteSpace(list.NextPageToken))
return RedirectToAction("Index", routeValues: "pageNumber=" + k);
if (query.IndexOf("&pageToken", StringComparison.InvariantCulture) > -1)
query = query.Remove(query.IndexOf("&pageToken=", StringComparison.InvariantCulture));
query += $"&pageToken={list.NextPageToken}";
}
response = await client.GetStringAsync(new Uri(query)).ConfigureAwait(false);
list = JsonConvert.DeserializeObject<PostList>(response);
BlogListViewModel viewModel = new BlogListViewModel(Database, list) { PageNumber = pageNumber };
return View(viewModel);
}
public async Task<IActionResult> Tags(string id)
{
using HttpClient client = new HttpClient();
string query = $"https://blogger.googleapis.com/v3/blogs/{blogId}/posts?fetchBodies=false&fetchImages=true&maxResults=500&orderBy=PUBLISHED&view=READER&key={apiKey}&labels={id}";
string response = await client.GetStringAsync(new Uri(query)).ConfigureAwait(false);
PostList list = JsonConvert.DeserializeObject<PostList>(response);
BlogListViewModel viewModel = new BlogListViewModel(Database, list) { SearchTerm = "#" + id };
return View(viewName: "~/Views/Blog/Index.cshtml", viewModel);
}
public async Task<IActionResult> Post(string id)
{
using HttpClient client = new HttpClient();
string query = $"https://www.googleapis.com/blogger/v3/blogs/{blogId}/posts/{id}?fetchBody=true&fetchImages=true&maxComments=500&view=READER&key={apiKey}";
string response = await client.GetStringAsync(new Uri(query)).ConfigureAwait(false);
Post post = JsonConvert.DeserializeObject<Post>(response);
BlogPostViewModel viewModel = new BlogPostViewModel(Database, post);
return View(viewModel);
}
}
}
+7 -6
View File
@@ -13,15 +13,16 @@
<RepositoryUrl>https://github.com/xfox111/cvwebsite</RepositoryUrl> <RepositoryUrl>https://github.com/xfox111/cvwebsite</RepositoryUrl>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8"> <PackageReference Include="Google.Apis.Blogger.v3" Version="1.45.0.1966" />
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.0.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.3" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.3" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.4" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.3" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.4" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.1.1" /> <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.1.3" />
<PackageReference Include="Select.HtmlToPdf.NetCore" Version="19.2.0" /> <PackageReference Include="Select.HtmlToPdf.NetCore" Version="20.1.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Resources\Areas\" /> <Folder Include="Resources\Areas\" />
@@ -147,6 +147,9 @@
<data name="en" xml:space="preserve"> <data name="en" xml:space="preserve">
<value>ru</value> <value>ru</value>
</data> </data>
<data name="Follow me on" xml:space="preserve">
<value>Подписывайтесь на</value>
</data>
<data name="Here is presented the most of projects I worked on" xml:space="preserve"> <data name="Here is presented the most of projects I worked on" xml:space="preserve">
<value>Здесь собрано большинство проектов над которыми я когда-либо работал</value> <value>Здесь собрано большинство проектов над которыми я когда-либо работал</value>
</data> </data>
@@ -186,6 +189,9 @@
<data name="socialNetworks" xml:space="preserve"> <data name="socialNetworks" xml:space="preserve">
<value>соцСети</value> <value>соцСети</value>
</data> </data>
<data name="Tags" xml:space="preserve">
<value>Теги</value>
</data>
<data name="You" xml:space="preserve"> <data name="You" xml:space="preserve">
<value>Вы</value> <value>Вы</value>
</data> </data>
@@ -0,0 +1,135 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Next" xml:space="preserve">
<value>Вперед</value>
</data>
<data name="Prev" xml:space="preserve">
<value>Назад</value>
</data>
<data name="Search" xml:space="preserve">
<value>Поиск</value>
</data>
<data name="Search results for" xml:space="preserve">
<value>Результаты поиска по тегу</value>
</data>
<data name="Visit on" xml:space="preserve">
<value>Открыть на</value>
</data>
</root>
+5 -4
View File
@@ -15,14 +15,15 @@ namespace MyWebsite
{ {
// TODO: FoxTube API admin page // TODO: FoxTube API admin page
// TODO: Complete homepage // TODO: Complete homepage
// TODO: Add localization system
// TODO: Add blog
// TODO: Rid of JavaScript (use Blazor) // TODO: Rid of JavaScript (use Blazor)
public class Startup public class Startup
{ {
public static string BlogspotAPI { get; private set; }
public Startup(IConfiguration configuration) public Startup(IConfiguration configuration)
{ {
Configuration = configuration; Configuration = configuration;
BlogspotAPI = configuration.GetConnectionString("BlogspotAPI");
} }
public IConfiguration Configuration { get; } public IConfiguration Configuration { get; }
@@ -68,12 +69,12 @@ namespace MyWebsite
CultureInfo[] supportedCultures = new[] CultureInfo[] supportedCultures = new[]
{ {
new CultureInfo("en"), new CultureInfo("en-US"),
new CultureInfo("ru"), new CultureInfo("ru"),
}; };
app.UseRequestLocalization(new RequestLocalizationOptions app.UseRequestLocalization(new RequestLocalizationOptions
{ {
DefaultRequestCulture = new RequestCulture("en"), DefaultRequestCulture = new RequestCulture("en-US"),
SupportedCultures = supportedCultures, SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures SupportedUICultures = supportedCultures
}); });
@@ -0,0 +1,14 @@
using Google.Apis.Blogger.v3.Data;
using MyWebsite.Models.Databases;
namespace MyWebsite.ViewModels
{
public class BlogListViewModel : ViewModelBase
{
public BlogListViewModel(DatabaseContext context, PostList list) : base(context) =>
Posts = list;
public PostList Posts { get; }
public int PageNumber { get; set; }
public string SearchTerm { get; set; }
}
}
@@ -0,0 +1,12 @@
using Google.Apis.Blogger.v3.Data;
using MyWebsite.Models.Databases;
namespace MyWebsite.ViewModels
{
public class BlogPostViewModel : ViewModelBase
{
public Post Post { get; set; }
public BlogPostViewModel(DatabaseContext context, Post post) : base(context) =>
Post = post;
}
}
@@ -0,0 +1,88 @@
@model BlogListViewModel
@{
ViewData["Title"] = "Fox, Coffee and Science - " + SharedLocalizer["Blog"];
}
<header>
@if (string.IsNullOrWhiteSpace(Model.SearchTerm))
{
<h1>Fox, Coffee and Science - @SharedLocalizer["Blog"]</h1>
}
else
{
<h1>@SharedLocalizer["Search results for"] @Model.SearchTerm</h1>
}
@Localizer["Visit on"] <a target="_blank" href="@Model.Links.FirstOrDefault(i => i.Name == "blogger")?.Url">Blogspot</a>
</header>
<form method="get" action="https://xfox111.blogspot.com/search" target="_blank">
<input type="text" name="q" spellcheck="false" placeholder="@Localizer["Search"]" />
<input type="submit" value="&#xE094;" />
</form>
<article>
<div>
@if (Model.Posts.Items == null || Model.Posts.Items.Count < 1)
{
<p class="comment">// @SharedLocalizer["No content available"]</p>
}
else
foreach (Google.Apis.Blogger.v3.Data.Post post in Model.Posts.Items)
{
<div class="item">
@if (post.Images != null && post.Images.Count > 0)
{
<img src="@post.Images.FirstOrDefault()?.Url" title="@post.Title" onerror="this.parentElement.removeChild(this)" />
}
<div>
<p>
@DateTime.Parse(post.Published).ToShortDateString() | <a href="@post.Author.Url" target="_blank">@post.Author.DisplayName</a><br />
@if (post.Labels != null && post.Labels.Count > 0)
{
<span class="comment">// @(Html.Raw(string.Join(", ", (post.Labels ?? new string[0]).Select(i => $"<a class=\"comment\" href=\"Blog/Tags/{i}\">{i}</a>"))))</span>
}
</p>
<h2><a asp-action="Post" asp-route-id="@post.Id">@post.Title</a></h2>
</div>
</div>
}
</div>
@if ((string.IsNullOrWhiteSpace(Model.SearchTerm) || Model.SearchTerm.StartsWith("#")) && (Model.PageNumber > 0 || !string.IsNullOrWhiteSpace(Model.Posts.NextPageToken)))
{
<div class="page-navigation">
@if (Model.PageNumber > 0)
{
<a asp-action="Index" asp-route-pageNumber="@(Model.PageNumber - 1)">&#xE00E; @Localizer["Prev"]</a>
}
<span>@(Model.PageNumber + 1)</span>
@if (!string.IsNullOrWhiteSpace(Model.Posts.NextPageToken))
{
<a asp-action="Index" asp-route-pageNumber="@(Model.PageNumber + 1)">@Localizer["Next"] &#xE00F;</a>
}
</div>
}
</article>
<aside>
<a class="twitter-timeline" data-lang="@SharedLocalizer["en"]" data-width="300" data-height="600" data-theme="light" href="https://twitter.com/xfox111?ref_src=twsrc%5Etfw">Tweets by xfox111</a>
<h3>@SharedLocalizer["Follow me on"]</h3>
<div class="follow-list">
@foreach (LinkModel link in Model.Links.Where(i => new[] { "twitter", "blogger", "github" }.Contains(i.Name)).OrderBy(i => i.Order))
{
<a class="socicon-@(link.Name)" href="@(link.Url)" target="_blank" title="@(link.Title)"></a>
}
<a class="socicon-rss" href="//xfox111.blogspot.com/feeds/posts/default?alt=rss" target="_blank" title="RSS Feed"></a>
<a href="//buymeacoff.ee/xfox111" target="_blank" title="Buy me a coffee">
<img width="24" height="25" src="https://cdn.buymeacoffee.com/buttons/bmc-new-btn-logo.svg">
</a>
</div>
</aside>
@section Imports
{
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<link rel="stylesheet" type="text/css" href="~/css/Blog.css" />
}
+114
View File
@@ -0,0 +1,114 @@
@model BlogPostViewModel
@{
ViewData["Title"] = Model.Post.Title + " - Fox, Coffee and Science - " + SharedLocalizer["Blog"];
}
<article>
<div class="post-body">
<div class="post-header">
<h2>@Model.Post.Title</h2>
<p>
@DateTime.Parse(Model.Post.Published).ToShortDateString() | <a href="@Model.Post.Author.Url" target="_blank">@Model.Post.Author.DisplayName</a><br />
</p>
<div class="share-btns">
<a class="share-btn facebook" target="_blank" href="//www.facebook.com/sharer/sharer.php?u=@Context.Request.Scheme://@Context.Request.Host@Context.Request.Path" title="Facebook">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve" class="eapps-social-share-buttons-item-icon">
<path d="M5.677,12.998V8.123h3.575V6.224C9.252,2.949,11.712,0,14.736,0h3.94v4.874h-3.94 c-0.432,0-0.934,0.524-0.934,1.308v1.942h4.874v4.874h-4.874V24H9.252V12.998H5.677z"></path>
</svg>
</a>
<a class="share-btn twitter" target="_blank" href="//twitter.com/intent/tweet?url=@Context.Request.Scheme://@Context.Request.Host@Context.Request.Path&text=@Model.Post.Title" title="Twitter">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve" class="eapps-social-share-buttons-item-icon">
<path fill="%233E68C0" d="M21.535,7.063c0.311,6.923-4.852,14.642-13.99,14.642 c-2.78,0-5.368-0.815-7.545-2.213c2.611,0.308,5.217-0.415,7.287-2.038c-2.154-0.039-3.972-1.462-4.599-3.418 c0.771,0.148,1.53,0.105,2.223-0.084c-2.367-0.475-4.002-2.608-3.948-4.888c0.664,0.369,1.423,0.59,2.229,0.615 C1.001,8.215,0.38,5.32,1.67,3.108c2.428,2.978,6.055,4.938,10.145,5.143c-0.717-3.079,1.618-6.044,4.796-6.044 c1.415,0,2.694,0.598,3.592,1.554c1.121-0.221,2.174-0.631,3.126-1.195c-0.368,1.149-1.149,2.114-2.164,2.724 c0.995-0.119,1.944-0.384,2.826-0.776C23.331,5.503,22.497,6.37,21.535,7.063z"></path>
</svg>
</a>
<a class="share-btn linkedin" target="_blank" href="https://www.linkedin.com/sharing/share-offsite/?url=@Context.Request.Scheme://@Context.Request.Host@Context.Request.Path" title="LinkedIn">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve" class="eapps-social-share-buttons-item-icon">
<path fill="%233E68C0" d="M6.52,22h-4.13V8.667h4.13V22z M4.436,6.92 c-1.349,0-2.442-1.101-2.442-2.46C1.994,3.102,3.087,2,4.436,2s2.442,1.102,2.442,2.46C6.877,5.819,5.784,6.92,4.436,6.92z M21.994,22h-4.109c0,0,0-5.079,0-6.999c0-1.919-0.73-2.991-2.249-2.991c-1.652,0-2.515,1.116-2.515,2.991c0,2.054,0,6.999,0,6.999 h-3.96V8.667h3.96v1.796c0,0,1.191-2.202,4.02-2.202c2.828,0,4.853,1.727,4.853,5.298C21.994,17.129,21.994,22,21.994,22z"></path>
</svg>
</a>
<a class="share-btn vk" target="_blank" href="//vk.com/share.php?url=@Context.Request.Scheme://@Context.Request.Host@Context.Request.Path&title=@Model.Post.Title" title="VK">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve" class="eapps-social-share-buttons-item-icon">
<path fill="%233E68C0" d="M23.876,17.52c-0.029-0.063-0.056-0.115-0.081-0.156c-0.416-0.75-1.212-1.67-2.387-2.762l-0.025-0.025 l-0.012-0.012l-0.013-0.013h-0.013c-0.533-0.508-0.871-0.85-1.012-1.025c-0.259-0.333-0.317-0.671-0.175-1.012 c0.1-0.258,0.475-0.804,1.125-1.637c0.342-0.442,0.612-0.795,0.812-1.062c1.441-1.916,2.066-3.141,1.874-3.674l-0.074-0.125 c-0.05-0.075-0.096-0.116-0.304-0.178c-0.208-0.062-0.466-0.057-1.107-0.057l-3.03,0.021c-0.155,0-0.486-0.017-0.594,0.008 s-0.163,0.038-0.163,0.038L18.633,5.88l-0.05,0.038c-0.042,0.025-0.087,0.069-0.138,0.131c-0.05,0.062-0.091,0.135-0.125,0.219 c-0.392,1.008-0.837,1.945-1.337,2.811c-0.308,0.517-0.591,0.964-0.85,1.343c-0.258,0.379-0.475,0.658-0.65,0.837 c-0.175,0.179-0.333,0.323-0.475,0.431s-0.25,0.154-0.325,0.138c-0.075-0.017-0.146-0.033-0.213-0.05 c-0.117-0.075-0.21-0.177-0.281-0.306s-0.119-0.292-0.144-0.487c-0.025-0.196-0.04-0.364-0.044-0.506 c-0.004-0.141-0.002-0.342,0.006-0.6c0.009-0.258,0.013-0.433,0.013-0.525c0-0.317,0.006-0.66,0.019-1.031s0.023-0.664,0.031-0.881 s0.012-0.446,0.012-0.687c0-0.242-0.015-0.431-0.044-0.569c-0.029-0.137-0.073-0.271-0.131-0.4s-0.144-0.229-0.256-0.3 c-0.112-0.071-0.252-0.127-0.419-0.169c-0.442-0.1-1.004-0.154-1.687-0.162C9.996,5.138,9,5.238,8.559,5.455 C8.384,5.547,8.226,5.672,8.084,5.83c-0.15,0.183-0.171,0.283-0.063,0.3c0.5,0.075,0.854,0.254,1.062,0.537l0.075,0.15 c0.058,0.108,0.117,0.3,0.175,0.575c0.058,0.275,0.096,0.579,0.112,0.912c0.042,0.608,0.042,1.129,0,1.562 s-0.081,0.771-0.119,1.012c-0.038,0.242-0.094,0.437-0.169,0.587s-0.125,0.242-0.15,0.275s-0.046,0.054-0.062,0.062 c-0.108,0.042-0.221,0.063-0.337,0.063c-0.117,0-0.258-0.058-0.425-0.175c-0.167-0.117-0.339-0.277-0.519-0.481 c-0.179-0.204-0.381-0.489-0.606-0.856c-0.225-0.366-0.458-0.8-0.7-1.299l-0.2-0.362C6.033,8.459,5.862,8.119,5.646,7.674 C5.429,7.228,5.238,6.797,5.071,6.381c-0.067-0.175-0.167-0.308-0.3-0.4L4.708,5.943C4.666,5.91,4.6,5.874,4.508,5.837 C4.416,5.799,3.576,5.766,3.219,5.766L0.831,5.78c-0.35,0-0.621,0.08-0.746,0.239l-0.05,0.075c-0.025,0.042-0.038,0.108-0.038,0.2 s0.025,0.204,0.075,0.337c0.5,1.175,1.043,2.308,1.631,3.399C2.29,11.121,2.801,12,3.234,12.666 c0.433,0.667,0.875,1.296,1.325,1.887c0.45,0.592,0.748,0.971,0.893,1.137c0.146,0.167,0.26,0.292,0.344,0.375l0.312,0.3 c0.2,0.2,0.494,0.439,0.881,0.718c0.387,0.279,0.816,0.554,1.287,0.825c0.471,0.271,1.018,0.491,1.643,0.662 s1.218,0.206,1.824,0.206h1.437c0.291-0.025,0.512-0.117,0.662-0.275l0.05-0.063c0.033-0.05,0.065-0.127,0.094-0.231 s0.044-0.219,0.044-0.344c-0.009-0.358,0.019-0.681,0.081-0.968s0.133-0.504,0.213-0.65c0.079-0.146,0.169-0.269,0.269-0.368 c0.1-0.1,0.171-0.16,0.213-0.181c0.041-0.021,0.075-0.035,0.1-0.044c0.2-0.067,0.435-0.002,0.706,0.194s0.525,0.437,0.762,0.725 s0.523,0.61,0.856,0.968s0.625,0.625,0.875,0.8l0.25,0.15c0.167,0.1,0.383,0.192,0.65,0.275c0.266,0.083,0.401,0.062,0.7,0.062 l3.262-0.003c0.316,0,0.5-0.099,0.674-0.203c0.175-0.104,0.279-0.219,0.312-0.344s0.035-0.267,0.006-0.425 C23.935,17.693,23.905,17.582,23.876,17.52z"></path>
</svg>
</a>
</div>
</div>
@Html.Raw(Model.Post.Content)
<p>
@if (Model.Post.Labels != null && Model.Post.Labels.Count > 0)
{
<span class="comment">// @SharedLocalizer["Tags"]: @(Html.Raw(string.Join(", ", (Model.Post.Labels ?? new string[0]).Select(i => $"<a class=\"comment\" href=\"/Blog/Tags/{i}\">{i}</a>"))))</span>
}
</p>
</div>
<div class="post-comments" style="display: none !important;">
<h3>Comments: @Model.Post.Replies.TotalItems</h3>
<p>
<a class="comment" href="@Model.Post.Url" target="_blank">// Add comment on Blogpost</a>
</p>
<div>
@foreach (var i in Model.Post.Replies.Items.Where(i => i.InReplyTo == null))
{
@Html.Raw(GetCommentCard(i.Id))
}
</div>
</div>
</article>
<aside>
<a class="twitter-timeline" data-lang="@SharedLocalizer["en"]" data-width="300" data-height="600" data-theme="light" href="https://twitter.com/xfox111?ref_src=twsrc%5Etfw">Tweets by xfox111</a>
<h3>@SharedLocalizer["Follow me on"]</h3>
<div class="follow-list">
@foreach (LinkModel link in Model.Links.Where(i => new[] { "twitter", "blogger", "github" }.Contains(i.Name)).OrderBy(i => i.Order))
{
<a class="socicon-@(link.Name)" href="@(link.Url)" target="_blank" title="@(link.Title)"></a>
}
<a class="socicon-rss" href="//xfox111.blogspot.com/feeds/posts/default?alt=rss" target="_blank" title="RSS Feed"></a>
<a href="//buymeacoff.ee/xfox111" target="_blank" title="Buy me a coffee">
<img width="24" height="25" src="https://cdn.buymeacoffee.com/buttons/bmc-new-btn-logo.svg">
</a>
</div>
</aside>
@section Imports
{
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<link rel="stylesheet" type="text/css" href="~/css/Blog.css" />
<style type="text/css">
main
{
padding-top: 50px;
}
</style>
}
@functions
{
string GetCommentCard(string commentId)
{
var comment = Model.Post.Replies.Items.FirstOrDefault(i => i.Id == commentId);
return $@"
<div class= ""post-comment"">
<div>
<img src=""{comment.Author.Image.Url}"" />
<div>
<span>
<a href=""{comment.Author.Url}"" target=""_blank"">{comment.Author.DisplayName}</a> | {DateTime.Parse(comment.Published).ToString()}
</span>
<p>
{comment.Content}
</p>
<div>
{(string.Join(string.Empty, Model.Post.Replies.Items.Where(i => i.InReplyTo?.Id == commentId).Select(i => GetCommentCard(i.Id))))}
</div>
</div>
</div>
</div>
";
}
}
@@ -1,5 +1,5 @@
<li><a asp-area="" asp-controller="Home" asp-action="Index">@(SharedLocalizer["AboutMe"])();</a></li> <li><a asp-area="" asp-controller="Home" asp-action="Index">@(SharedLocalizer["AboutMe"])();</a></li>
<li hidden><a href="//xfox111.blogspot.com/" target="_blank">@(SharedLocalizer["MyBlog"])();</a></li> <li><a asp-area="" asp-controller="Blog" asp-action="Index">@(SharedLocalizer["Blog"])();</a></li>
<li><a asp-area="" asp-controller="Resume" asp-action="Index">@(SharedLocalizer["MyResume"])();</a></li> <li><a asp-area="" asp-controller="Resume" asp-action="Index">@(SharedLocalizer["MyResume"])();</a></li>
<li><a asp-area="" asp-controller="Projects" asp-action="Index">@(SharedLocalizer["Projects"])();</a></li> <li><a asp-area="" asp-controller="Projects" asp-action="Index">@(SharedLocalizer["Projects"])();</a></li>
<li><a asp-area="" asp-controller="Gallery" asp-action="Index">@(SharedLocalizer["Arts"])();</a></li> <li><a asp-area="" asp-controller="Gallery" asp-action="Index">@(SharedLocalizer["Arts"])();</a></li>
@@ -0,0 +1,18 @@
<environment names="Development">
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
</environment>
<environment names="Staging,Production">
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.17.0/jquery.validate.min.js"
asp-fallback-src="~/lib/jquery-validation/dist/jquery.validate.min.js"
asp-fallback-test="window.jQuery && window.jQuery.validator"
crossorigin="anonymous"
integrity="sha384-rZfj/ogBloos6wzLGpPkkOr/gpkBNLZ6b6yLy4o+ok+t/SAKlL5mvXLr0OXNi1Hp">
</script>
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validation.unobtrusive/3.2.9/jquery.validate.unobtrusive.min.js"
asp-fallback-src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"
asp-fallback-test="window.jQuery && window.jQuery.validator && window.jQuery.validator.unobtrusive"
crossorigin="anonymous"
integrity="sha384-ifv0TYDWxBHzvAk2Z0n8R434FL1Rlv/Av18DXE43N/1rvHyOG4izKst0f2iSLdds">
</script>
</environment>
+2 -1
View File
@@ -10,6 +10,7 @@
"ConnectionStrings": { "ConnectionStrings": {
"MainDB": "Server=(localdb)\\mssqllocaldb;Database=xfox111;Trusted_Connection=True;", "MainDB": "Server=(localdb)\\mssqllocaldb;Database=xfox111;Trusted_Connection=True;",
"FoxTubeDB": "Server=(localdb)\\mssqllocaldb;Database=foxtube;Trusted_Connection=True;", "FoxTubeDB": "Server=(localdb)\\mssqllocaldb;Database=foxtube;Trusted_Connection=True;",
"GUTScheduleDB": "Server=(localdb)\\mssqllocaldb;Database=gutSchedule;Trusted_Connection=True;" "GUTScheduleDB": "Server=(localdb)\\mssqllocaldb;Database=gutSchedule;Trusted_Connection=True;",
"BlogspotAPI": "%API_Key%"
} }
} }
+279
View File
@@ -0,0 +1,279 @@
main
{
display: grid;
grid-template-columns: 1fr 300px;
grid-column-gap: 50px;
max-width: 1100px;
margin: 0px auto;
padding: 0px 30px;
}
article, header
{
margin: 0px;
}
body
{
background-color: whitesmoke;
}
.item
{
background: white;
margin-bottom: 20px;
border-radius: 5px;
}
header
{
align-self: center;
height: 100px;
}
header a:link
{
text-decoration: underline;
}
header > h1
{
margin-bottom: 0px;
}
.item > img
{
width: 100%;
border-radius: 5px 5px 0px 0px;
}
.post-body img
{
width: 100%;
}
.item > div
{
padding: 10px 25px;
}
.item > div > p
{
color: gray;
}
.item > div > h2 > a
{
color: black;
text-decoration: none;
}
.item > div > h2::after
{
content: " \E00F";
font-size: initial;
margin: 10px 0px;
}
main > form
{
align-self: center;
background-color: white;
position: relative;
border-radius: 5px;
}
main > form > input[type=text]
{
border: none;
box-sizing: border-box;
border-radius: 5px;
padding: 0px 10px;
height: 32px;
width: 100%;
}
main > form > input[type=submit]
{
font-family: "SegoeMDL2Assets";
height: 32px;
width: 32px;
background-color: transparent;
border: none;
border-radius: 5px;
position: absolute;
right: 0px;
}
.page-navigation
{
background-color: white;
border-radius: 5px;
display: table;
user-select: none;
margin: auto;
}
.page-navigation > a
{
background: #343434;
color: white !important;
text-decoration: none;
border-radius: 5px;
padding: 7px 15px 7px 15px;
line-height: 33px;
}
.page-navigation > span
{
margin: 0px 20px;
line-height: 33px;
}
.follow-list a
{
color: black;
text-decoration: none;
font-size: x-large;
}
.follow-list a:hover
{
opacity: .5;
}
.follow-list
{
display: inline-grid;
grid-auto-flow: column;
grid-column-gap: 20px;
}
.post-body
{
background: white;
margin-bottom: 20px;
border-radius: 5px;
padding: 1px 0px;
}
.post-body .post-header, .post-body > p
{
margin-right: 25px;
margin-left: 25px;
margin-bottom: 25px;
}
.share-btns
{
display: inline-grid;
grid-auto-flow: column;
grid-column-gap: 5px;
}
.share-btn
{
width: 32px;
height: 32px;
color: white;
transition-duration: .3s;
}
.share-btn:hover
{
background-color: white !important;
}
.share-btn svg
{
width: 16px;
height: 16px;
padding: 8px;
}
.share-btn path
{
fill: white
}
.share-btn.facebook
{
background-color: #3e68c0;
}
.share-btn.facebook:hover path
{
fill: #3e68c0 !important;
}
.share-btn.twitter
{
background-color: #23abff;
}
.share-btn.twitter:hover path
{
fill: #23abff !important;
}
.share-btn.linkedin
{
background-color: #15ace5;
}
.share-btn.linkedin:hover path
{
fill: #15ace5 !important;
}
.share-btn.vk
{
background-color: #3673be;
}
.share-btn.vk:hover path
{
fill: #3673be !important;
}
.post-comments
{
background-color: white;
border-radius: 5px;
padding: 5px 25px;
}
.post-comment > div
{
display: grid;
grid-template-columns: auto 1fr;
grid-column-gap: 20px;
}
.post-comment > div > div > span
{
line-height: 35px;
}
.post-comment > div > div > div
{
margin-top: 30px;
}
.post-comment > div > img
{
border-radius: 999px;
}
@media only screen and (max-width: 800px)
{
main > aside, main > form
{
display: none;
}
main
{
display: block;
}
}
+1 -1
View File
@@ -130,7 +130,7 @@ header
} }
/* Adaptive code */ /* Adaptive code */
@media only screen and (min-width: 980px) @media only screen and (min-width: 1070px)
{ {
menu menu
{ {