docs: #315 extracting Hashieha page to make it searchable

This commit is contained in:
Hamid Reza Mohammadi 2022-05-04 16:57:07 +04:30
parent b1b3854503
commit 647982f364
3 changed files with 501 additions and 3 deletions

View File

@ -0,0 +1,220 @@
@page
@using DNTPersianUtils.Core;
@model GanjooRazor.Pages.HashiehaModel
@{
Layout = null;
}
<!DOCTYPE html>
<html lang="fa-IR" dir="rtl">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>
@ViewData["Title"]
</title>
<link href="/css/p8.css?version=41" rel="stylesheet" type="text/css" />
<link href="/css/fab.css" rel="stylesheet" type="text/css" />
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
<link rel="search" type="application/opensearchdescription+xml" title="گنجور" href="https://ganjoor.net/search.xml" />
</head>
<body>
@if (!string.IsNullOrEmpty(Model.LastError))
{
@Html.Raw(Model.LastError)
}
else
{
<div id="hdr2" class="clearfix">
<h1><a href="/" title="گنجور"><img src="/image/gmgk.png" alt="گنجور" /></a></h1>
<div class="actionbox">
<form action="/search" method="get" id="searchform">
<input type="text" name="s" id="s" class="inputText" value="" placeholder="جستجو" />
<select name="author" id="author" onchange="onSelectedPoetChanged()">
@{
if (Model.PoetId != 0)
{
<option value="0">در آثار همهٔ سخنوران</option>
}
else
{
<option value="0" selected>در آثار همهٔ سخنوران</option>
}
if (Model.Poets != null)
{
foreach (RMuseum.Models.Ganjoor.ViewModels.GanjoorPoetViewModel poet in Model.Poets)
{
if (poet.Id == Model.PoetId)
{
<option value="@poet.Id" selected>@poet.Name</option>
}
else
{
<option value="@poet.Id">@poet.Name</option>
}
}
}
}
</select>
@{
if (Model.Poet != null)
{
<select name="cat" id="cat" style="width:150px">
<option value="0" selected>در همهٔ بخشها</option>
@{
foreach (var gran in Model.Poet.Cat.Ancestors)
{
<option value="@gran.Id">@gran.Title</option>
}
<option value="@Model.Poet.Cat.Id">@Model.Poet.Cat.Title</option>
foreach (var child in Model.Poet.Cat.Children)
{
<option value="@child.Id">@child.Title</option>
}
}
</select>
}
}
<input type="submit" value="بیاب" />
</form>
</div><!-- .actionbox -->
</div>
<!-- #hdr2 -->
<div id="fa">
<div id="maincnt">
<h2>
@Model.Title
</h2>
@if (Model.Profile != null)
{
<div id="profile">
<p>
@Model.Profile.NickName
@if (!string.IsNullOrEmpty(Model.Profile.Website))
{
<a href="@Model.Profile.Website">🌐</a>
}
@if (Model.CanAdministerUsers)
{
<a href="/Admin/ExamineUser?id=@Model.Profile.Id">#</a>
}
</p>
@if (!string.IsNullOrEmpty(Model.Profile.Bio))
{
<p>
@Html.Raw(Model.Profile.Bio)
</p>
}
</div>
<hr />
}
<div class="sitem actionbox">
<div class="related-images-frame">
<form method="get" id="searchcommentsform">
<input type="text" name="s" id="s" class="inputText" value="" placeholder="جستجو" />
<input type="submit" class="width-100per" value="جستجو در حاشیه‌ها" />
</form>
</div><!-- related-images-frame -->
</div> <!-- comments-like-div -->
<div class="poem">
@if (Model.Comments.Count == 0)
{
<p>حاشیه‌ای با مشخصات مورد نظر شما پیدا نشد.</p>
}
else
foreach (var comment in Model.Comments)
{
<article>
<p>
@if (comment.UserId == null)
{
@comment.AuthorName
}
else
{
<a href="/hashieha/?userid=@comment.UserId">@comment.AuthorName</a>
}
<small>
در @comment.CommentDate.ToFriendlyPersianDateTextify()
@if (comment.InReplyTo != null)
{
<text>
@(" ")در پاسخ به @comment.InReplyTo.AuthorName
</text>
}
</small>
@(" ")دربارهٔ <a href="@comment.Poem.UrlSlug#comment-@comment.Id">@comment.Poem.Title</a>:
</p>
@if (comment.CoupletIndex != -1)
{
<div class="commentquote">
@comment.CoupletSummary
</div>
}
<blockquote>
@Html.Raw(comment.HtmlComment)
</blockquote>
<div class="spacer">&nbsp;</div>
<hr />
</article>
}
</div>
@Html.Raw(Model.PagingToolsHtml)
</div>
<partial name="_InlineLoginPartial" model="Model" />
<partial name="_FooterPartial" model="null" />
</div>
<script src="/lib/jquery/dist/jquery.min.js"></script>
<script src="/js/bk.js?version=26"></script>
<script src="/js/vaabd.js"></script>
@if (!string.IsNullOrEmpty(ViewData["GoogleAnalyticsCode"].ToString()))
{
<script>
(function (i, s, o, g, r, a, m) {
i['GoogleAnalyticsObject'] = r; i[r] = i[r] || function () {
(i[r].q = i[r].q || []).push(arguments)
}, i[r].l = 1 * new Date(); a = s.createElement(o),
m = s.getElementsByTagName(o)[0]; a.async = 1; a.src = g; m.parentNode.insertBefore(a, m)
})(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');
ga('create', '@ViewData["GoogleAnalyticsCode"]', 'auto');
ga('send', 'pageview');
</script>
}
}
</body>
</html>

View File

@ -0,0 +1,278 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using DNTPersianUtils.Core;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using RMuseum.Models.Auth.ViewModel;
using RMuseum.Models.Ganjoor.ViewModels;
using RSecurityBackend.Models.Auth.Memory;
using RSecurityBackend.Models.Generic;
namespace GanjooRazor.Pages
{
[IgnoreAntiforgeryToken(Order = 1001)]
public class HashiehaModel : LoginPartialEnabledPageModel
{
/// <summary>
/// memory cache
/// </summary>
private readonly IMemoryCache _memoryCache;
/// <summary>
/// configration file reader (appsettings.json)
/// </summary>
private readonly IConfiguration Configuration;
/// <summary>
/// constructor
/// </summary>
/// <param name="httpClient"></param>
/// <param name="memoryCache"></param>
/// <param name="configuration"></param>
public HashiehaModel(HttpClient httpClient, IMemoryCache memoryCache, IConfiguration configuration) : base(httpClient)
{
_memoryCache = memoryCache;
Configuration
= configuration;
}
/// <summary>
/// aggressive cache
/// </summary>
public bool AggressiveCacheEnabled
{
get
{
try
{
return bool.Parse(Configuration["AggressiveCacheEnabled"]);
}
catch
{
return false;
}
}
}
public List<GanjoorPoetViewModel> Poets { get; set; }
public int PoetId { get; set; }
public GanjoorPoetCompleteViewModel Poet { get; set; }
public string PagingToolsHtml { get; set; }
public string LastError { get; set; }
private async Task<bool> preparePoets()
{
var cacheKey = $"/api/ganjoor/poets";
if (!_memoryCache.TryGetValue(cacheKey, out List<GanjoorPoetViewModel> poets))
{
var response = await _httpClient.GetAsync($"{APIRoot.Url}/api/ganjoor/poets");
if (!response.IsSuccessStatusCode)
{
LastError = JsonConvert.DeserializeObject<string>(await response.Content.ReadAsStringAsync());
return false;
}
poets = JArray.Parse(await response.Content.ReadAsStringAsync()).ToObject<List<GanjoorPoetViewModel>>();
if (AggressiveCacheEnabled)
{
_memoryCache.Set(cacheKey, poets);
}
}
Poets = poets;
return true;
}
private async Task<bool> preparePoet()
{
var cacheKey = $"/api/ganjoor/poet/{PoetId}";
if (!_memoryCache.TryGetValue(cacheKey, out GanjoorPoetCompleteViewModel poet))
{
var poetResponse = await _httpClient.GetAsync($"{APIRoot.Url}/api/ganjoor/poet/{PoetId}");
if (!poetResponse.IsSuccessStatusCode)
{
LastError = JsonConvert.DeserializeObject<string>(await poetResponse.Content.ReadAsStringAsync());
return false;
}
poet = JObject.Parse(await poetResponse.Content.ReadAsStringAsync()).ToObject<GanjoorPoetCompleteViewModel>();
if (AggressiveCacheEnabled)
{
_memoryCache.Set(cacheKey, poet);
}
}
Poet = poet;
return true;
}
public List<GanjoorCommentFullViewModel> Comments { get; set; }
public async Task<IActionResult> OnGetPoetInformationAsync(int id)
{
if (id == 0)
return new OkObjectResult(null);
var cacheKey = $"/api/ganjoor/poet/{id}";
if (!_memoryCache.TryGetValue(cacheKey, out GanjoorPoetCompleteViewModel poet))
{
var poetResponse = await _httpClient.GetAsync($"{APIRoot.Url}/api/ganjoor/poet/{id}");
if (!poetResponse.IsSuccessStatusCode)
{
return BadRequest(JsonConvert.DeserializeObject<string>(await poetResponse.Content.ReadAsStringAsync()));
}
poet = JObject.Parse(await poetResponse.Content.ReadAsStringAsync()).ToObject<GanjoorPoetCompleteViewModel>();
if (AggressiveCacheEnabled)
{
_memoryCache.Set(cacheKey, poet);
}
}
return new OkObjectResult(poet);
}
public GanjoorUserPublicProfile Profile { get; set; }
public string Title { get; set; }
public bool CanAdministerUsers { get; set; }
public async Task<IActionResult> OnGetAsync()
{
LoggedIn = !string.IsNullOrEmpty(Request.Cookies["Token"]);
PoetId = string.IsNullOrEmpty(Request.Query["a"]) ? 0 : int.Parse(Request.Query["a"]);
ViewData["GoogleAnalyticsCode"] = Configuration["GoogleAnalyticsCode"];
//todo: use html master layout or make it partial
// 1. poets
if (false == (await preparePoets()))
return Page();
if (PoetId != 0)
{
if (false == (await preparePoet()))
return Page();
}
// 2. search comments
int pageNumber = 1;
if (!string.IsNullOrEmpty(Request.Query["page"]))
{
pageNumber = int.Parse(Request.Query["page"]);
}
var filterUserId = Request.Query["userid"];
string url = $"{APIRoot.Url}/api/ganjoor/comments?PageNumber={pageNumber}&PageSize=20";
Title = "حاشیه‌ها";
if (!string.IsNullOrEmpty(filterUserId))
{
var responseUserProfile = await _httpClient.GetAsync($"{APIRoot.Url}/api/ganjoor/user/profile/{filterUserId}");
if (!responseUserProfile.IsSuccessStatusCode)
{
LastError = JsonConvert.DeserializeObject<string>(await responseUserProfile.Content.ReadAsStringAsync());
return Page();
}
GanjoorUserPublicProfile profile = JsonConvert.DeserializeObject<GanjoorUserPublicProfile>(await responseUserProfile.Content.ReadAsStringAsync());
ViewData["Title"] = $"گنجور » حاشیه‌های {profile.NickName}";
Title = $"حاشیه‌های {profile.NickName}";
if (!string.IsNullOrEmpty(Request.Cookies["Token"]))
{
using (HttpClient secureClient = new HttpClient())
{
secureClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Request.Cookies["Token"]);
var res = await secureClient.GetAsync($"{APIRoot.Url}/api/users/securableitems");
if (res.IsSuccessStatusCode)
{
SecurableItem[] secuarbleItems = JsonConvert.DeserializeObject<SecurableItem[]>(await res.Content.ReadAsStringAsync());
var userSecurableItem = secuarbleItems.Where(s => s.ShortName == SecurableItem.UserEntityShortName).FirstOrDefault();
if (userSecurableItem != null)
{
var administerOperation = userSecurableItem.Operations.Where(o => o.ShortName == SecurableItem.Administer).FirstOrDefault();
if (administerOperation != null)
{
CanAdministerUsers = administerOperation.Status;
}
}
}
}
}
url += $"&filterUserId={filterUserId}";
}
var response = await _httpClient.GetAsync(url);
if (!response.IsSuccessStatusCode)
{
LastError = JsonConvert.DeserializeObject<string>(await response.Content.ReadAsStringAsync());
return Page();
}
Comments = JArray.Parse(await response.Content.ReadAsStringAsync()).ToObject<List<GanjoorCommentFullViewModel>>();
string paginnationMetadata = response.Headers.GetValues("paging-headers").FirstOrDefault();
PaginationMetadata paginationMetadata = JsonConvert.DeserializeObject<PaginationMetadata>(paginnationMetadata);
string htmlText = "";
if (paginationMetadata.totalPages > 1)
{
Title += $" - صفحهٔ {pageNumber.ToPersianNumbers()}";
ViewData["Title"] += $" - صفحهٔ {pageNumber.ToPersianNumbers()}";
htmlText = $"<div>{Environment.NewLine}";
string queryFilterUserId = string.IsNullOrEmpty(filterUserId) ? "" : $"&amp;userid={filterUserId}";
if (paginationMetadata.currentPage > 3)
{
htmlText += $"<a href=\"/hashieha/?page=1{queryFilterUserId}\"><div class=\"circled-number\">۱</div></a> …";
}
for (int i = paginationMetadata.currentPage - 2; i <= (paginationMetadata.currentPage + 2); i++)
{
if (i >= 1 && i <= paginationMetadata.totalPages)
{
if (i == paginationMetadata.currentPage)
{
htmlText += $"<div class=\"circled-number-diff\">{i.ToPersianNumbers()}</div>{Environment.NewLine}";
}
else
{
htmlText += $"<a href=\"/hashieha/?page={i}{queryFilterUserId}\"><div class=\"circled-number\">{i.ToPersianNumbers()}</div></a>{Environment.NewLine}";
}
}
}
if (paginationMetadata.totalPages > (paginationMetadata.currentPage + 2))
{
htmlText += $"… <a href=\"/hashieha/?page={paginationMetadata.totalPages}{queryFilterUserId}\"><div class=\"circled-number\">{paginationMetadata.totalPages.ToPersianNumbers()}</div></a>{Environment.NewLine}";
}
htmlText += $"</div>{Environment.NewLine}";
}
ViewData["Title"] = $"گنجور » {Title}";
PagingToolsHtml = htmlText;
return Page();
}
}
}

View File

@ -764,9 +764,9 @@ namespace GanjooRazor.Pages
switch (GanjoorPage.UrlSlug)
{
case "hashieha":
await _GenerateHashiehaHtmlText();
break;
//case "hashieha":
// await _GenerateHashiehaHtmlText();
// break;
case "vazn":
await _GenerateVaznHtmlText();
break;