divan/DivanRazor/Pages/DivanPage.cshtml.cs
Anas Rashid e3e3819c78 Urdu public pages, batch 2 (#2): auth, errors, map, FAQ, similar poems, people pages, page titles
- translate remaining public views and page-model strings (titles, messages, relation names)
- Urdu stopword list for word-frequency stats (replaces Persian list)
- error pages: drop list of alternative Ganjoor services

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:22:09 +02:00

1478 lines
66 KiB
C#
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using DNTPersianUtils.Core;
using DivanRazor.Utils;
using KontorService.Models.Reporting.ViewModels;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.OutputCaching;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using RMuseum.Models.Divan;
using RMuseum.Models.Divan.ViewModels;
using RMuseum.Models.DivanAudio;
using RMuseum.Models.DivanAudio.ViewModels;
using RMuseum.Services.Implementation;
using RMuseum.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace DivanRazor.Pages
{
/// <summary>
/// Renders a single Divan content page (poem/poet/category). Split out of IndexModel, which
/// used to serve both this and the home page from one class dispatching on
/// Request.Path == "/" - see _HomePagePartial.cshtml / _DivanPagePartial.cshtml, and
/// Startup.cs's AddPageRoute("/DivanPage", "{*url}") which now owns the site's catch-all route
/// (Index.cshtml keeps its own automatic "/" route unchanged, so home is unaffected).
/// </summary>
[OutputCache(PolicyName = "DivanPublicPage")]
public partial class DivanPageModel : LoginPartialEnabledPageModel
{
/// <summary>
/// Urdu stop words excluded from category word-count listings when remStopWords is
/// requested (divan: replaces upstream's Persian list).
/// </summary>
private static readonly HashSet<string> _urduStopWords = new HashSet<string>
{
"و", "اور", "کا", "کی", "کے", "کو", "نے", "سے", "میں", "پر", "تک", "ہے", "ہیں", "تھا", "تھی",
"تھے", "ہو", "ہوا", "ہوئی", "ہوئے", "گا", "گی", "گے", "یہ", "وہ", "اس", "ان", "جو", "جس", "جن",
"کہ", "کیا", "کیوں", "کب", "جب", "تب", "اب", "یاں", "واں", "یہاں", "وہاں", "نہ", "نہیں", "مت", "بھی",
"ہی", "تو", "اک", "ایک", "کچھ", "سب", "ہر", "کوئی", "کسی", "کر", "کرے", "کرتا", "دیا", "لیا", "رہا",
"رہی", "رہے", "مجھ", "مجھے", "ہم", "تم", "تجھ", "تجھے", "آپ", "میرا", "میری", "میرے", "مرا", "مری", "مرے",
"تیرا", "تیری", "تیرے", "ترا", "تری", "ترے", "اپنا", "اپنی", "اپنے", "ہمارا", "ہماری", "ہمارے", "تمہارا", "تمہاری", "تمہارے",
"پہ", "سا", "سی", "جیسے", "ایسا", "ایسی", "ایسے", "یا", "مگر", "لیکن", "پھر", "اگر", "گر", "جوں", "کہیں",
"یوں", "ہائے", "اے",
};
private readonly PoetCacheService _poetCache;
public bool OfflineMode => GetConfigFlag("OfflineMode");
public bool ReadOnlyMode => GetConfigFlag("ReadOnlyMode");
public const int DivanSlashVaznPageId = 39401;
/// <summary>
/// constructor
/// </summary>
public DivanPageModel(IConfiguration configuration,
HttpClient httpClient,
PoetCacheService poetCache
) : base(httpClient, configuration)
{
_poetCache = poetCache;
}
/// <summary>
/// last error
/// </summary>
public string LastError { get; set; }
/// <summary>
/// banner
/// </summary>
public DivanSiteBannerViewModel Banner { get; set; }
/// <summary>
/// If the response failed, stores the API's error message in <see cref="LastError"/> and
/// returns true so the caller can short-circuit (the established pattern here is
/// `if (await CaptureErrorIfFailedAsync(response)) return Page();`).
/// </summary>
private async Task<bool> CaptureErrorIfFailedAsync(HttpResponseMessage response)
{
if (response.IsSuccessStatusCode)
{
return false;
}
LastError = await ReadErrorMessageAsync(response);
return true;
}
public _CommentPartialModel GetCommentModel(DivanCommentSummaryViewModel comment, int poemId)
{
return new _CommentPartialModel()
{
Comment = comment,
Error = "",
InReplyTo = null,
LoggedIn = LoggedIn,
DivSuffix = "",
PoemId = poemId,
};
}
public _AudioPlayerPartialModel GetRecitationsModel(PublicRecitationViewModel[] recitations, bool minimumControls, RecitationType recitationType, bool isAdmin)
{
return new _AudioPlayerPartialModel()
{
LoggedIn = LoggedIn,
Recitations = recitations.Where(a => a.RecitationType == recitationType).ToArray(),
ShowAllRecitaions = minimumControls ? true : ShowAllRecitaions,
CategoryMode = minimumControls,
RecitationType = recitationType,
IsAdmin = isAdmin,
};
}
public _QuotedPoemPartialModel GetQuotedPoemModel(DivanQuotedPoemViewModel quotedPoem, DivanPageCompleteViewModel page, bool canEdit)
{
return new _QuotedPoemPartialModel()
{
DivanQuotedPoemViewModel = quotedPoem,
PoetImageUrl = page.PoetOrCat.Poet.ImageUrl,
PoetNickName = page.PoetOrCat.Poet.Nickname,
CanEdit = canEdit,
};
}
/// <summary>
/// re-fetch a poem's comments sorted by rating, oldest, or newest (AJAX, called from sortComments() in bk.js)
/// </summary>
public async Task<IActionResult> OnGetCommentsPartialAsync(int poemId, DivanCommentSortOrder sortOrder = DivanCommentSortOrder.TopRated)
{
var response = await _httpClient.GetAsync($"{APIRoot.Url}/api/divan/poem/{poemId}/comments?sortOrder={sortOrder}");
if (!response.IsSuccessStatusCode)
{
return BadRequest(await ReadErrorMessageAsync(response));
}
var comments = JArray.Parse(await response.Content.ReadAsStringAsync()).ToObject<DivanCommentSummaryViewModel[]>();
LoggedIn = !string.IsNullOrEmpty(Request.Cookies["Token"]);
if (!string.IsNullOrEmpty(Request.Cookies["UserId"])
&& Guid.TryParse(Request.Cookies["UserId"], out Guid userId)
&& userId != Guid.Empty)
{
using (HttpClient secureClient = new HttpClient(new DivanReloginHandler(Request, Response)))
{
if (await DivanSessionChecker.PrepareClient(secureClient, Request, Response))
{
DivanUserBookmarkViewModel[] bookmarks = Array.Empty<DivanUserBookmarkViewModel>();
HttpResponseMessage bookmarksResponse = await secureClient.GetAsync($"{APIRoot.Url}/api/divan/bookmark/{poemId}");
if (bookmarksResponse.IsSuccessStatusCode)
{
bookmarks = JsonConvert.DeserializeObject<DivanUserBookmarkViewModel[]>(await bookmarksResponse.Content.ReadAsStringAsync());
}
foreach (DivanCommentSummaryViewModel comment in comments)
{
comment.MyComment = comment.UserId == userId;
comment.IsBookmarked = bookmarks.Where(b => b.CoupletIndex == -comment.Id).Any();
_markMyReplies(comment, userId, bookmarks);
}
HttpResponseMessage ratingsResponse = await secureClient.GetAsync($"{APIRoot.Url}/api/divan/poem/{poemId}/comments/myratings");
if (ratingsResponse.IsSuccessStatusCode)
{
var ratings = JsonConvert.DeserializeObject<DivanCommentUserRatingViewModel[]>(await ratingsResponse.Content.ReadAsStringAsync());
if (ratings.Length > 0)
{
var ratingsDic = ratings.ToDictionary(r => r.CommentId, r => r.Value);
foreach (DivanCommentSummaryViewModel comment in comments)
{
_applyCommentRatings(comment, ratingsDic);
}
}
}
}
}
}
return Partial("~/Pages/Partials/DivanPage/_CommentsListPartial.cshtml", new _CommentsListPartialModel()
{
Comments = comments,
PoemId = poemId,
LoggedIn = LoggedIn,
});
}
public Task<IActionResult> OnPostReply(string replyCommentText, int refPoemId, int refCommentId)
{
return OnPostComment(replyCommentText, refPoemId, refCommentId, -1);
}
/// <summary>
/// comment
/// </summary>
public Task<IActionResult> OnPostComment(string comment, int poemId, int inReplytoId, int coupletIndex)
{
var unauthorized = Partial("~/Pages/Partials/DivanPage/_CommentPartial.cshtml", new _CommentPartialModel()
{
Comment = null,
Error = NotLoggedInMessage,
InReplyTo = null
});
return WithSecureClientAsync(async secureClient =>
{
var stringContent = new StringContent(
JsonConvert.SerializeObject(new DivanCommentPostViewModel()
{
HtmlComment = comment,
InReplyToId = inReplytoId == 0 ? null : inReplytoId,
PoemId = poemId,
CoupletIndex = coupletIndex == -1 ? null : coupletIndex
}),
Encoding.UTF8, "application/json");
var response = await secureClient.PostAsync($"{APIRoot.Url}/api/divan/comment", stringContent);
if (response.StatusCode == HttpStatusCode.OK)
{
var resComment = JsonConvert.DeserializeObject<DivanCommentSummaryViewModel>(await response.Content.ReadAsStringAsync());
resComment.MyComment = true;
return Partial("~/Pages/Partials/DivanPage/_CommentPartial.cshtml", new _CommentPartialModel()
{
Comment = resComment,
Error = "",
InReplyTo = inReplytoId == 0 ? null : new DivanCommentSummaryViewModel(),
LoggedIn = !string.IsNullOrEmpty(Request.Cookies["Token"]),
PoemId = poemId,
});
}
string rawError = await ReadErrorMessageAsync(response);
var sanitizerInfo = TryParseSanitizerTextDroppedError(rawError);
return Partial("~/Pages/Partials/DivanPage/_CommentPartial.cshtml", new _CommentPartialModel()
{
Comment = null,
Error = sanitizerInfo != null ? sanitizerInfo.Message : rawError,
SanitizerRemainingText = sanitizerInfo?.RemainingText,
InReplyTo = null,
LoggedIn = !string.IsNullOrEmpty(Request.Cookies["Token"]),
PoemId = poemId,
});
}, unauthorized);
}
public Task<IActionResult> OnDeleteMyComment(int id)
{
return WithSecureClientAsync(async secureClient =>
{
var response = await secureClient.DeleteAsync($"{APIRoot.Url}/api/divan/comment?id={id}");
if (!response.IsSuccessStatusCode)
{
return new BadRequestObjectResult(await ReadErrorMessageAsync(response));
}
return new JsonResult(true);
});
}
public Task<IActionResult> OnPutMyComment(int id, string comment)
{
return WithSecureClientAsync(async secureClient =>
{
var response = await secureClient.PutAsync($"{APIRoot.Url}/api/divan/comment/{id}", new StringContent(JsonConvert.SerializeObject(comment), Encoding.UTF8, "application/json"));
if (!response.IsSuccessStatusCode)
{
return await BadRequestFromApiErrorAsync(response);
}
return new JsonResult(true);
});
}
/// <summary>
/// like / dislike / clear rating for a comment (AJAX, called from rateComment() in bk.js)
/// </summary>
public Task<IActionResult> OnPostRateCommentAsync(int id, short value)
{
return WithSecureClientAsync(async secureClient =>
{
var response = await secureClient.PostAsync($"{APIRoot.Url}/api/divan/poem/comment/{id}/rate?value={value}", null);
if (!response.IsSuccessStatusCode)
{
return new BadRequestObjectResult(await ReadErrorMessageAsync(response));
}
var result = JsonConvert.DeserializeObject<DivanCommentRatingResultViewModel>(await response.Content.ReadAsStringAsync());
return new JsonResult(result);
});
}
/// <summary>
/// Poets (for this page's own #hdr2 author dropdown - same pattern as Search/Simi/etc.)
/// </summary>
public List<DivanPoetViewModel> Poets { get; set; }
public bool IsPoetPage { get; set; }
public bool IsCatPage { get; set; }
public bool IsPoemPage { get; set; }
public string CanonicalUrl => $"{Configuration["SiteUrl"]}{DivanPage.FullUrl}";
public bool CanEdit { get; set; }
public bool KeepHistory { get; set; }
public string PinterestUrl { get; set; }
public bool ShowAllRecitaions { get; set; }
public string ActiveTab { get; set; }
private void _preparePoemExcerpt(DivanPoemSummaryViewModel poem)
{
if (poem == null || poem.Excerpt == null)
{
return;
}
if (poem.Excerpt.Length > 100)
{
poem.Excerpt = poem.Excerpt.Substring(0, 50);
int n = poem.Excerpt.LastIndexOf(' ');
if (n >= 0)
{
poem.Excerpt = poem.Excerpt.Substring(0, n) + " ...";
}
else
{
poem.Excerpt += "...";
}
}
}
/// <summary>
/// specify which comments belong to current user
/// </summary>
private async Task _markMyCommentsAndBringUpMyRecitations()
{
if (DivanPage == null)
{
return;
}
if (DivanPage.Poem == null)
{
return;
}
if (string.IsNullOrEmpty(Request.Cookies["UserId"]))
{
return;
}
if (!Guid.TryParse(Request.Cookies["UserId"], out Guid userId))
{
return;
}
if (userId == Guid.Empty)
{
return;
}
foreach (DivanCommentSummaryViewModel comment in DivanPage.Poem.Comments)
{
comment.MyComment = comment.UserId == userId;
_markMyReplies(comment, userId, null);
}
if (DivanPage.Poem.Recitations.Length > 0)
{
using (HttpClient secureClient = new HttpClient(new DivanReloginHandler(Request, Response)))
{
if (await DivanSessionChecker.PrepareClient(secureClient, Request, Response))
{
HttpResponseMessage response = await secureClient.GetAsync($"{APIRoot.Url}/api/divan/poem/{DivanPage.Poem.Id}/recitations/upvotes");
if (response.IsSuccessStatusCode)
{
var upVotedIds = JsonConvert.DeserializeObject<int[]>(await response.Content.ReadAsStringAsync());
if (upVotedIds.Length > 0)
{
foreach (var recitation in DivanPage.Poem.Recitations)
{
recitation.UpVotedByUser = upVotedIds.Contains(recitation.Id);
}
Array.Sort(DivanPage.Poem.Recitations, (a, b) => b.UpVotedByUser == a.UpVotedByUser ? a.AudioOrder.CompareTo(b.AudioOrder) : b.UpVotedByUser.CompareTo(a.UpVotedByUser));
}
}
}
}
}
if (DivanPage.Poem.Comments.Length > 0)
{
using (HttpClient secureClient = new HttpClient(new DivanReloginHandler(Request, Response)))
{
if (await DivanSessionChecker.PrepareClient(secureClient, Request, Response))
{
HttpResponseMessage response = await secureClient.GetAsync($"{APIRoot.Url}/api/divan/poem/{DivanPage.Poem.Id}/comments/myratings");
if (response.IsSuccessStatusCode)
{
var ratings = JsonConvert.DeserializeObject<DivanCommentUserRatingViewModel[]>(await response.Content.ReadAsStringAsync());
if (ratings.Length > 0)
{
var ratingsDic = ratings.ToDictionary(r => r.CommentId, r => r.Value);
foreach (DivanCommentSummaryViewModel comment in DivanPage.Poem.Comments)
{
_applyCommentRatings(comment, ratingsDic);
}
}
}
}
}
}
}
private void _applyCommentRatings(DivanCommentSummaryViewModel comment, Dictionary<int, short> ratings)
{
if (ratings.TryGetValue(comment.Id, out short value))
{
comment.CurrentUserRatingValue = value;
}
foreach (var reply in comment.Replies)
{
_applyCommentRatings(reply, ratings);
}
}
private void _markMyReplies(DivanCommentSummaryViewModel parent, Guid userId, DivanUserBookmarkViewModel[] bookmarks)
{
foreach (var reply in parent.Replies)
{
reply.MyComment = reply.UserId == userId;
if (bookmarks != null)
reply.IsBookmarked = bookmarks.Where(b => b.CoupletIndex == -reply.Id).Any();
_markMyReplies(reply, userId, bookmarks);
}
}
/// <summary>
/// get audio description
/// </summary>
public string getAudioDesc(PublicRecitationViewModel recitation, bool contributionLink = false)
{
string audiodesc = "قرأت ";
if (!string.IsNullOrEmpty(recitation.AudioArtistUrl))
{
audiodesc += $"<a href='{recitation.AudioArtistUrl}'>{recitation.AudioArtist}</a>";
}
else
{
audiodesc += $"{recitation.AudioArtist}";
}
if (!string.IsNullOrEmpty(recitation.AudioSrc))
{
if (!string.IsNullOrEmpty(recitation.AudioSrcUrl))
{
audiodesc += $" <a href='{recitation.AudioSrcUrl}'>{recitation.AudioSrc}</a>";
}
else
{
audiodesc += $" {recitation.AudioSrc}";
}
}
audiodesc += $" <small><a href='/AudioClip/?a={recitation.Id}' onclick='wpopen(this.href); return false' class='comments-link' title='دریافت'>(دریافت)</a></small>";
if (contributionLink)
{
audiodesc += "<br /> <small>کیا آپ پڑھنا چاہیں گے؟ <a href='http://ava.ganjoor.net/about/'>یہاں</a> دیکھیں.</small>";
}
return audiodesc;
}
private async Task<bool> preparePoets()
{
var (success, poets, error) = await _poetCache.GetPoetsAsync(EditorCacheBypass);
if (!success)
{
LastError = error;
return false;
}
Poets = poets;
return true;
}
public async Task<IActionResult> OnGetPoetInformationAsync(int id)
{
if (id == 0)
{
return new OkObjectResult(null);
}
var (success, poet, error) = await _poetCache.GetPoetAsync(id, EditorCacheBypass);
if (!success)
{
return BadRequest(error);
}
return new OkObjectResult(poet);
}
public string HtmlLanguage { get; set; } = "ur-PK";
private void _prepareNextPre()
{
switch (DivanPage.DivanPageType)
{
case DivanPageType.PoemPage:
{
if (DivanPage.Poem.Next != null)
{
NextUrl = DivanPage.PoetOrCat.Cat.FullUrl + "/" + DivanPage.Poem.Next.UrlSlug;
NextTitle = DivanPage.Poem.Next.Title + ": " + DivanPage.Poem.Next.Excerpt;
}
else
if (DivanPage.Poem.MixedModeOrder > 0
&& DivanPage.Poem.Category.Cat.Children.Where(c => c.MixedModeOrder == 0 || c.MixedModeOrder > DivanPage.Poem.MixedModeOrder).Any())
{
var nextCat = DivanPage.Poem.Category.Cat.Children.Where(c => c.MixedModeOrder == 0 || c.MixedModeOrder > DivanPage.Poem.MixedModeOrder).OrderBy(c => c.MixedModeOrder).First();
NextUrl = nextCat.FullUrl;
NextTitle = nextCat.Title;
}
else
if (DivanPage.Poem.Category.Cat.Next != null)
{
NextUrl = DivanPage.Poem.Category.Cat.Next.FullUrl;
NextTitle = DivanPage.Poem.Category.Cat.Next.Title;
}
if (DivanPage.Poem.Previous != null)
{
PreviousUrl = DivanPage.PoetOrCat.Cat.FullUrl + "/" + DivanPage.Poem.Previous.UrlSlug;
PreviousTitle = DivanPage.Poem.Previous.Title + ": " + DivanPage.Poem.Previous.Excerpt;
}
else
if (DivanPage.Poem.MixedModeOrder > 0
&& DivanPage.Poem.Category.Cat.Children.Where(c => c.MixedModeOrder != 0 && c.MixedModeOrder < DivanPage.Poem.MixedModeOrder).Any())
{
var prevCat = DivanPage.Poem.Category.Cat.Children.Where(c => c.MixedModeOrder != 0 && c.MixedModeOrder < DivanPage.Poem.MixedModeOrder).OrderByDescending(c => c.MixedModeOrder).First();
PreviousUrl = prevCat.FullUrl;
PreviousTitle = prevCat.Title;
}
else
if (DivanPage.Poem.Category.Cat.Previous != null)
{
PreviousUrl = DivanPage.Poem.Category.Cat.Previous.FullUrl;
PreviousTitle = DivanPage.Poem.Category.Cat.Previous.Title;
}
}
break;
case DivanPageType.CatPage:
{
if (DivanPage.PoetOrCat.Cat.Next != null)
{
NextUrl = DivanPage.PoetOrCat.Cat.Next.FullUrl;
NextTitle = DivanPage.PoetOrCat.Cat.Next.Title;
}
if (DivanPage.PoetOrCat.Cat.Previous != null)
{
PreviousUrl = DivanPage.PoetOrCat.Cat.Previous.FullUrl;
PreviousTitle = DivanPage.PoetOrCat.Cat.Previous.Title;
}
}
break;
}
}
public bool MutiPartPoemPage { get; set; }
private void _prepareRelatedSecions()
{
SectionsWithRelated = new List<DivanPoemSection>();
SectionsWithMetreAndRhymes = new List<DivanPoemSection>();
MutiPartPoemPage = DivanPage.Poem.Verses.Where(v => v.VersePosition == VersePosition.Paragraph || v.VersePosition == VersePosition.Single).Any()
||
DivanPage.Poem.Sections.Count(s => s.SectionType == PoemSectionType.WholePoem && s.VerseType == VersePoemSectionType.First) != 1;
if (
!MutiPartPoemPage
)
{
foreach (var section in DivanPage.Poem.Sections)
{
if (section.Top6RelatedSections.Length > 0)
{
SectionsWithRelated.Add(section);
}
if (section.SectionType == PoemSectionType.WholePoem && section.DivanMetre != null && !string.IsNullOrEmpty(section.RhymeLetters))
{
SectionsWithMetreAndRhymes.Add(section);
}
}
}
}
public DivanPoetSuggestedPictureViewModel Photo { get; set; }
/// <summary>
/// Get. Unlike the old IndexModel.OnGetAsync, this never needs to check
/// Request.Path == "/" - routing guarantees this page never receives that request, since
/// Index.cshtml's own automatic "/" route always takes precedence over this page's
/// catch-all for a request to "/".
/// </summary>
public async Task<IActionResult> OnGetAsync()
{
var maintenanceResult = TryGetMaintenanceModeResult();
if (maintenanceResult != null)
{
return maintenanceResult;
}
var legacyRedirect = TryHandleLegacyUrlRedirect();
if (legacyRedirect != null)
{
return legacyRedirect;
}
InitializeRequestState();
if (!string.IsNullOrEmpty(Request.Query["p"]))
{
return await RedirectByPageIdAsync(Request.Query["p"]);
}
await preparePoets();
var breadCrumbList = new GoogleBreadCrumbList();
Banner = null;
var pageLoadResult = await LoadDivanPageAsync();
if (pageLoadResult != null)
{
return pageLoadResult;
}
var pageDataResult = await LoadPageTypeSpecificDataAsync();
if (pageDataResult != null)
{
return pageDataResult;
}
BuildTitleAndBreadCrumbs(breadCrumbList);
ViewData["BrearCrumpList"] = breadCrumbList.ToString();
if (IsCatPage || IsPoetPage)
{
var geoTagsResult = await LoadCategoryGeoTagsAsync();
if (geoTagsResult != null)
{
return geoTagsResult;
}
}
return Page();
}
/// <summary>
/// A couple of legacy URL shapes get redirected to their modern equivalents.
/// Returns null if the current request doesn't match either pattern.
/// </summary>
private IActionResult TryHandleLegacyUrlRedirect()
{
var path = Request.Path.ToString();
if (path.IndexOf("index.php") != -1)
{
return Redirect($"{path.Replace("index.php", "search")}{Request.QueryString}");
}
if (path.IndexOf("vazn") != -1 && Request.QueryString.ToString().IndexOf("v") != -1)
{
return Redirect($"{path.Replace("vazn", "simi")}{Request.QueryString}");
}
return null;
}
private void InitializeRequestState()
{
LastError = "";
InitializeCommonPageState();
CanEdit = Request.Cookies["CanEdit"] == "True";
KeepHistory = Request.Cookies["KeepHistory"] == "True";
CanTranslate = Request.Cookies["CanTranslate"] == "True";
IsPoetPage = false;
IsCatPage = false;
IsPoemPage = false;
PinterestUrl = Request.Query["pinterest_url"];
ShowAllRecitaions = Request.Query["allaudio"] == "1";
ActiveTab = Request.Query["tab"];
if (ShowAllRecitaions && string.IsNullOrEmpty(ActiveTab))
{
ActiveTab = "recitations";
}
else if (ActiveTab == "recitations" || ActiveTab == "commentaries")
{
ShowAllRecitaions = true;
}
}
/// <summary>
/// Handles the legacy "?p=&lt;id&gt;" query-string form by resolving it to the page's real
/// URL and redirecting there. Also kept on IndexModel (home page): since query strings don't
/// affect ASP.NET Core route matching, a link like "/?p=123" always has Path == "/" and is
/// always routed to the home page regardless of this split, so the check needs to exist on
/// both classes to keep old "?p=" links working from either entry point.
/// </summary>
private async Task<IActionResult> RedirectByPageIdAsync(string pageId)
{
var pageUrlResponse = await _httpClient.GetAsync($"{APIRoot.Url}/api/divan/pageurl?id={pageId}");
if (await CaptureErrorIfFailedAsync(pageUrlResponse))
{
return Page();
}
var pageUrl = JsonConvert.DeserializeObject<string>(await pageUrlResponse.Content.ReadAsStringAsync());
return Redirect(pageUrl);
}
/// <summary>
/// Fetches the DivanPage for the current URL and applies the page-type-specific preparation
/// that depends only on the page payload itself (excerpts, related sections, next/previous
/// links). Returns null to let OnGetAsync continue, or a terminal IActionResult
/// (redirect/404/error page) to short-circuit.
/// </summary>
private async Task<IActionResult> LoadDivanPageAsync()
{
var pageQuery = await _httpClient.GetAsync($"{APIRoot.Url}/api/divan/page?url={Request.Path}");
if (!pageQuery.IsSuccessStatusCode)
{
if (pageQuery.StatusCode == HttpStatusCode.NotFound)
{
var redirectQuery = await _httpClient.GetAsync($"{APIRoot.Url}/api/divan/redirecturl?url={Request.Path}");
if (redirectQuery.IsSuccessStatusCode)
{
var redirectUrl = JsonConvert.DeserializeObject<string>(await redirectQuery.Content.ReadAsStringAsync());
return Redirect(redirectUrl);
}
return NotFound();
}
LastError = await ReadErrorMessageAsync(pageQuery);
return Page();
}
DivanPage = JObject.Parse(await pageQuery.Content.ReadAsStringAsync()).ToObject<DivanPageCompleteViewModel>();
if (!string.IsNullOrEmpty(DivanPage.HtmlText))
{
DivanPage.HtmlText = DivanPage.HtmlText.Replace("https://ganjoor.net/", "/").Replace("http://ganjoor.net/", "/");
}
switch (DivanPage.DivanPageType)
{
case DivanPageType.PoemPage:
await _markMyCommentsAndBringUpMyRecitations();
_preparePoemExcerpt(DivanPage.Poem.Next);
_preparePoemExcerpt(DivanPage.Poem.Previous);
DivanPage.PoetOrCat = DivanPage.Poem.Category;
_prepareNextPre();
_prepareRelatedSecions();
IsPoemPage = true;
break;
case DivanPageType.PoetPage:
IsPoetPage = true;
break;
case DivanPageType.CatPage:
_prepareNextPre();
IsCatPage = true;
break;
}
return null;
}
/// <summary>
/// The extra API calls that only apply to specific page types: recitation availability for
/// poet/cat pages, poet photo for poet pages, and the random banner for poem pages.
/// </summary>
private async Task<IActionResult> LoadPageTypeSpecificDataAsync()
{
if (IsPoetPage || IsCatPage)
{
var catHasAnyRecitationQuery = await _httpClient.GetAsync($"{APIRoot.Url}/api/audio/catany/{DivanPage.PoetOrCat.Cat.Id}");
if (await CaptureErrorIfFailedAsync(catHasAnyRecitationQuery))
{
return Page();
}
CategoryHasRecitations = JsonConvert.DeserializeObject<bool>(await catHasAnyRecitationQuery.Content.ReadAsStringAsync());
}
if (IsPoetPage)
{
var responsePhotos = await _httpClient.GetAsync($"{APIRoot.Url}/api/poetphotos/poet/{DivanPage.PoetOrCat.Poet.Id}");
if (await CaptureErrorIfFailedAsync(responsePhotos))
{
return Page();
}
var photos = JArray.Parse(await responsePhotos.Content.ReadAsStringAsync()).ToObject<List<DivanPoetSuggestedPictureViewModel>>();
if (photos.Any())
{
Photo = photos.First();
}
}
if (IsPoemPage)
{
HtmlLanguage = string.IsNullOrEmpty(DivanPage.Poem.Language) ? "ur-PK" : DivanPage.Poem.Language;
if (GetConfigFlag("BannersEnabled"))
{
var bannerQuery = await _httpClient.GetAsync($"{APIRoot.Url}/api/banners/random");
if (await CaptureErrorIfFailedAsync(bannerQuery))
{
return Page();
}
var bannerResponse = await bannerQuery.Content.ReadAsStringAsync();
if (!string.IsNullOrEmpty(bannerResponse))
{
Banner = JObject.Parse(bannerResponse).ToObject<DivanSiteBannerViewModel>();
}
}
}
return null;
}
/// <summary>
/// Sets ViewData["Title"] and populates the breadcrumb list. Unlike the old
/// BuildTitleAndBreadCrumbsAsync, has no IsHomePage branch - this class never serves the home
/// page (see the note on OnGetAsync above).
/// </summary>
private void BuildTitleAndBreadCrumbs(GoogleBreadCrumbList breadCrumbList)
{
if (IsPoetPage)
{
BuildPoetPageTitleAndBreadCrumbs(breadCrumbList);
}
else if (IsCatPage)
{
BuildCatPageTitleAndBreadCrumbs(breadCrumbList);
}
else if (IsPoemPage)
{
BuildPoemPageTitleAndBreadCrumbs(breadCrumbList);
}
else
{
BuildGenericPageTitleAndBreadCrumbs(breadCrumbList);
}
}
private void BuildPoetPageTitleAndBreadCrumbs(GoogleBreadCrumbList breadCrumbList)
{
ViewData["Title"] = $"دیوان » {DivanPage.PoetOrCat.Poet.Nickname}";
breadCrumbList.AddItem(DivanPage.PoetOrCat.Poet.Nickname, DivanPage.PoetOrCat.Cat.FullUrl, $"{APIRoot.InternetUrl + DivanPage.PoetOrCat.Poet.ImageUrl}");
}
private void BuildCatPageTitleAndBreadCrumbs(GoogleBreadCrumbList breadCrumbList)
{
string title = $"دیوان » ";
bool poetCat = true;
foreach (var gran in DivanPage.PoetOrCat.Cat.Ancestors)
{
title += $"{gran.Title} » ";
breadCrumbList.AddItem(gran.Title, gran.FullUrl, poetCat ? $"{APIRoot.InternetUrl + DivanPage.PoetOrCat.Poet.ImageUrl}" : "https://i.ganjoor.net/cat.png");
poetCat = false;
}
breadCrumbList.AddItem(DivanPage.PoetOrCat.Cat.Title, DivanPage.PoetOrCat.Cat.FullUrl, "https://i.ganjoor.net/cat.png");
title += DivanPage.PoetOrCat.Cat.Title;
ViewData["Title"] = title;
}
private void BuildPoemPageTitleAndBreadCrumbs(GoogleBreadCrumbList breadCrumbList)
{
ViewData["Title"] = $"دیوان » {DivanPage.Poem.FullTitle}";
bool poetCat = true;
foreach (var gran in DivanPage.Poem.Category.Cat.Ancestors)
{
breadCrumbList.AddItem(gran.Title, gran.FullUrl, poetCat ? $"{APIRoot.InternetUrl + DivanPage.PoetOrCat.Poet.ImageUrl}" : "https://i.ganjoor.net/cat.png");
poetCat = false;
}
breadCrumbList.AddItem(DivanPage.PoetOrCat.Cat.Title, DivanPage.PoetOrCat.Cat.FullUrl, "https://i.ganjoor.net/cat.png");
var aiMuseumImage = DivanPage.Poem.Images.FirstOrDefault(i => i.TargetPageUrl.StartsWith("https://museum.ganjoor.net/items/ai"));
if (aiMuseumImage != null)
{
breadCrumbList.AddItem(DivanPage.Poem.Title, DivanPage.Poem.FullUrl, aiMuseumImage.ThumbnailImageUrl.Replace("/thumb/", "/orig/"));
}
else if (DivanPage.Poem.Images.Any())
{
breadCrumbList.AddItem(DivanPage.Poem.Title, DivanPage.Poem.FullUrl, DivanPage.Poem.Images.First().ThumbnailImageUrl.Replace("/thumb/", "/orig/"));
}
else
{
breadCrumbList.AddItem(DivanPage.Poem.Title, DivanPage.Poem.FullUrl, "https://i.ganjoor.net/poem.png");
}
}
private void BuildGenericPageTitleAndBreadCrumbs(GoogleBreadCrumbList breadCrumbList)
{
if (DivanPage.PoetOrCat != null)
{
bool poetCat = true;
string fullTitle = "دیوان » ";
if (DivanPage.PoetOrCat.Cat.Ancestors.Count == 0)
{
fullTitle += $"{DivanPage.PoetOrCat.Poet.Nickname} » ";
}
else
{
foreach (var gran in DivanPage.PoetOrCat.Cat.Ancestors)
{
breadCrumbList.AddItem(gran.Title, gran.FullUrl, poetCat ? $"{APIRoot.InternetUrl + DivanPage.PoetOrCat.Poet.ImageUrl}" : "https://i.ganjoor.net/cat.png");
poetCat = false;
fullTitle += $"{gran.Title} » ";
}
}
ViewData["Title"] = $"{fullTitle}{DivanPage.Title}";
breadCrumbList.AddItem(DivanPage.PoetOrCat.Poet.Nickname, DivanPage.PoetOrCat.Cat.FullUrl, $"{APIRoot.InternetUrl + DivanPage.PoetOrCat.Poet.ImageUrl}");
}
else
{
ViewData["Title"] = $"دیوان » {DivanPage.FullTitle}";
}
breadCrumbList.AddItem(DivanPage.Title, DivanPage.FullUrl, "https://i.ganjoor.net/cat.png");
}
private async Task<IActionResult> LoadCategoryGeoTagsAsync()
{
var tagsResponse = await _httpClient.GetAsync($"{APIRoot.Url}/api/divan/cat/{DivanPage.PoetOrCat.Cat.Id}/geotag");
if (await CaptureErrorIfFailedAsync(tagsResponse))
{
return Page();
}
CategoryPoemGeoDateTags = JsonConvert.DeserializeObject<PoemGeoDateTag[]>(await tagsResponse.Content.ReadAsStringAsync());
return null;
}
public async Task<IActionResult> OnGetBNumPartialAsync(int poemId, int coupletIndex)
{
var responseCoupletComments = await _httpClient.GetAsync($"{APIRoot.Url}/api/divan/poem/{poemId}/comments?coupletIndex={coupletIndex}");
if (!responseCoupletComments.IsSuccessStatusCode)
{
return BadRequest(await ReadErrorMessageAsync(responseCoupletComments));
}
var comments = JArray.Parse(await responseCoupletComments.Content.ReadAsStringAsync()).ToObject<List<DivanCommentSummaryViewModel>>();
var responseCoupletNumbers = await _httpClient.GetAsync($"{APIRoot.Url}/api/numberings/couplet/{poemId}/{coupletIndex}");
if (!responseCoupletNumbers.IsSuccessStatusCode)
{
return BadRequest(await ReadErrorMessageAsync(responseCoupletNumbers));
}
var numbers = JArray.Parse(await responseCoupletNumbers.Content.ReadAsStringAsync()).ToObject<List<DivanCoupletNumberViewModel>>();
var responseCoupletSections = await _httpClient.GetAsync($"{APIRoot.Url}/api/divan/couplet/{poemId}/{coupletIndex}/sections");
if (!responseCoupletSections.IsSuccessStatusCode)
{
return BadRequest(await ReadErrorMessageAsync(responseCoupletSections));
}
var responseVerses = await _httpClient.GetAsync($"{APIRoot.Url}/api/divan/poem/{poemId}/verses?coupletIndex={coupletIndex}");
if (!responseVerses.IsSuccessStatusCode)
{
return BadRequest(await ReadErrorMessageAsync(responseVerses));
}
var verses = JArray.Parse(await responseVerses.Content.ReadAsStringAsync()).ToObject<List<DivanVerseViewModel>>();
var sections = JArray.Parse(await responseCoupletSections.Content.ReadAsStringAsync()).ToObject<List<DivanPoemSection>>();
var sectionsWithMetreAndRhymes = new List<DivanPoemSection>();
foreach (var section in sections)
{
if (section.DivanMetreId != null && !string.IsNullOrEmpty(section.RhymeLetters))
{
if (!sectionsWithMetreAndRhymes.Any(s => s.DivanMetreId == section.DivanMetreId && s.RhymeLetters == section.RhymeLetters))
{
sectionsWithMetreAndRhymes.Add(section);
}
}
}
bool isBookmarked = false;
if (!string.IsNullOrEmpty(Request.Cookies["UserId"]))
{
if (Guid.TryParse(Request.Cookies["UserId"], out Guid userId))
if (userId != Guid.Empty)
{
using (HttpClient secureClient = new HttpClient(new DivanReloginHandler(Request, Response)))
{
if (await DivanSessionChecker.PrepareClient(secureClient, Request, Response))
{
HttpResponseMessage responseIsBookmarked = await secureClient.GetAsync($"{APIRoot.Url}/api/divan/bookmark/{poemId}");
if (!responseIsBookmarked.IsSuccessStatusCode)
{
return BadRequest(await ReadErrorMessageAsync(responseIsBookmarked));
}
var bookmarks = JsonConvert.DeserializeObject<DivanUserBookmarkViewModel[]>(await responseIsBookmarked.Content.ReadAsStringAsync());
if (bookmarks.Where(b => b.CoupletIndex == coupletIndex).Any())
isBookmarked = true;
foreach (DivanCommentSummaryViewModel comment in comments)
{
comment.MyComment = comment.UserId == userId;
comment.IsBookmarked = bookmarks.Where(b => b.CoupletIndex == -comment.Id).Any();
_markMyReplies(comment, userId, bookmarks);
}
HttpResponseMessage responseRatings = await secureClient.GetAsync($"{APIRoot.Url}/api/divan/poem/{poemId}/comments/myratings");
if (responseRatings.IsSuccessStatusCode)
{
var ratings = JsonConvert.DeserializeObject<DivanCommentUserRatingViewModel[]>(await responseRatings.Content.ReadAsStringAsync());
if (ratings.Length > 0)
{
var ratingsDic = ratings.ToDictionary(r => r.CommentId, r => r.Value);
foreach (DivanCommentSummaryViewModel comment in comments)
{
_applyCommentRatings(comment, ratingsDic);
}
}
}
}
}
}
}
return Partial("~/Pages/Partials/DivanPage/_BNumPartial.cshtml", new _BNumPartialModel()
{
PoemId = poemId,
CoupletIndex = coupletIndex,
LoggedIn = !string.IsNullOrEmpty(Request.Cookies["Token"]),
Comments = comments,
IsBookmarked = isBookmarked,
Numbers = numbers,
Sections = sections,
SectionsWithMetreAndRhymes = sectionsWithMetreAndRhymes,
Verses = verses,
});
}
public Task<IActionResult> OnPostSwitchBookmarkAsync(int poemId, int coupletIndex)
{
return WithSecureClientAsync(async secureClient =>
{
var response = await secureClient.PostAsync($"{APIRoot.Url}/api/divan/bookmark/switch/ret/{poemId}/{coupletIndex}", null);
if (!response.IsSuccessStatusCode)
{
return new BadRequestObjectResult(await ReadErrorMessageAsync(response));
}
var res = JsonConvert.DeserializeObject<string>(await response.Content.ReadAsStringAsync());
return new OkObjectResult(res);
});
}
public Task<IActionResult> OnGetIsCoupletBookmarkedAsync(int poemId, int coupletIndex)
{
return WithSecureClientAsync(async secureClient =>
{
var response = await secureClient.GetAsync($"{APIRoot.Url}/api/divan/bookmark/{poemId}/{coupletIndex}");
if (!response.IsSuccessStatusCode)
{
return new BadRequestObjectResult(await ReadErrorMessageAsync(response));
}
var res = JsonConvert.DeserializeObject<bool>(await response.Content.ReadAsStringAsync());
return new OkObjectResult(res);
});
}
public Task<IActionResult> OnGetPoemBookmarksAsync(int poemId)
{
return WithSecureClientAsync(async secureClient =>
{
var response = await secureClient.GetAsync($"{APIRoot.Url}/api/divan/bookmark/{poemId}");
if (!response.IsSuccessStatusCode)
{
return new BadRequestObjectResult(await ReadErrorMessageAsync(response));
}
var res = JsonConvert.DeserializeObject<DivanUserBookmarkViewModel[]>(await response.Content.ReadAsStringAsync());
return new OkObjectResult(res);
});
}
/// <summary>
/// unused now, to be removed
/// </summary>
public Task<IActionResult> OnGetUserUpvotedRecitationsAsync(int poemId)
{
return WithSecureClientAsync(async secureClient =>
{
var response = await secureClient.GetAsync($"{APIRoot.Url}/api/divan/poem/{poemId}/recitations/upvotes");
if (!response.IsSuccessStatusCode)
{
return new BadRequestObjectResult(await ReadErrorMessageAsync(response));
}
var res = JsonConvert.DeserializeObject<int[]>(await response.Content.ReadAsStringAsync());
return new OkObjectResult(res);
});
}
public Task<IActionResult> OnPostSwitchRecitationUpVoteAsync(int id)
{
return WithSecureClientAsync(async secureClient =>
{
var response = await secureClient.PutAsync($"{APIRoot.Url}/api/audio/vote/switch/{id}", null);
if (!response.IsSuccessStatusCode)
{
return new BadRequestObjectResult(await ReadErrorMessageAsync(response));
}
var res = JsonConvert.DeserializeObject<bool>(await response.Content.ReadAsStringAsync());
return new OkObjectResult(res);
});
}
public Task<IActionResult> OnPostAddToMyHistoryAsync(int poemId)
{
return WithSecureClientAsync(async secureClient =>
{
var response = await secureClient.PostAsync($"{APIRoot.Url}/api/tracking", new StringContent(JsonConvert.SerializeObject(poemId), Encoding.UTF8, "application/json"));
if (!response.IsSuccessStatusCode)
{
return new BadRequestObjectResult(await ReadErrorMessageAsync(response));
}
var res = JsonConvert.DeserializeObject<DivanUserPrePoemVisitViewModel>(await response.Content.ReadAsStringAsync());
if (res.KeepTrack == false)
{
if (Request.Cookies["KeepHistory"] != null)
{
Response.Cookies.Delete("KeepHistory");
}
var cookieOption = new CookieOptions()
{
Expires = DateTime.Now.AddDays(365),
HttpOnly = true,
Secure = true,
SameSite = SameSiteMode.Lax,
};
Response.Cookies.Append("KeepHistory", $"{false}", cookieOption);
}
return new OkObjectResult(res.KeepTrack && res.LastVisit != null ? $"اس صفحے کو آخری بار {res.LastVisit.ToFriendlyPersianDateTextify()} اور مجموعی طور پر {res.TotalVisits.ToPersianNumbers()} بار دیکھا." : "");
});
}
public Task<IActionResult> OnPutBookmarkNote(Guid id, string note)
{
return WithSecureClientAsync(async secureClient =>
{
var response = await secureClient.PutAsync($"{APIRoot.Url}/api/divan/bookmark/{id}", new StringContent(JsonConvert.SerializeObject(note), Encoding.UTF8, "application/json"));
if (!response.IsSuccessStatusCode)
{
return await BadRequestFromApiErrorAsync(response);
}
return new JsonResult(true);
});
}
public Task<IActionResult> OnDeleteMistakeAsync(int id)
{
return WithSecureClientAsync(async secureClient =>
{
var response = await secureClient.DeleteAsync($"{APIRoot.Url}/api/audio/errors/approved/{id}");
if (!response.IsSuccessStatusCode)
{
return new BadRequestObjectResult(await ReadErrorMessageAsync(response));
}
return new JsonResult(true);
});
}
public Task<IActionResult> OnPutMistakeAsync(int id, string reasonText)
{
return WithSecureClientAsync(async secureClient =>
{
var stringContent = new StringContent(
JsonConvert.SerializeObject(new RecitationErrorReportViewModel()
{
Id = id,
ReasonText = reasonText,
NumberOfLinesAffected = 1,
CoupletIndex = -1,
}),
Encoding.UTF8, "application/json");
var response = await secureClient.PutAsync($"{APIRoot.Url}/api/audio/errors/report/edit", stringContent);
if (!response.IsSuccessStatusCode)
{
return new BadRequestObjectResult(await ReadErrorMessageAsync(response));
}
return new JsonResult(true);
});
}
public async Task<IActionResult> OnGetMoreQuotedPoemsForRelatedPoemPartialAsync(int poemId, int relatedPoemId, string poetImageUrl, string poetNickName, bool canEdit)
{
string url = $"{APIRoot.Url}/api/divan/poem/{poemId}/quoteds/{relatedPoemId}?published=true";
var response = await _httpClient.GetAsync(url);
if (!response.IsSuccessStatusCode)
return BadRequest(await ReadErrorMessageAsync(response));
var quoteds = JArray.Parse(await response.Content.ReadAsStringAsync()).ToObject<List<DivanQuotedPoemViewModel>>();
if (!quoteds.Any())
{
return BadRequest("مزید کچھ نہیں ملا.");
}
if (quoteds.Any(q => q.ChosenForMainList))
{
var mainList = quoteds.Where(q => q.ChosenForMainList).First();
quoteds.Remove(mainList);
}
return Partial("~/Pages/Partials/DivanPage/_MultipleQuotedPoemsPartial.cshtml", new _MultipleQuotedPoemsPartialModel()
{
DivanQuotedPoems = quoteds.ToArray(),
PoetImageUrl = poetImageUrl,
PoetNickName = poetNickName,
CanEdit = canEdit
});
}
public async Task<IActionResult> OnGetMoreQuotedPoemsPartialAsync(int poemId, int skip, string poetImageUrl, string poetNickName, bool canEdit)
{
string url = $"{APIRoot.Url}/api/divan/poem/{poemId}/quoteds?skip={skip}&itemsCount=1000";
var response = await _httpClient.GetAsync(url);
if (!response.IsSuccessStatusCode)
return BadRequest(await ReadErrorMessageAsync(response));
var quoteds = JArray.Parse(await response.Content.ReadAsStringAsync()).ToObject<List<DivanQuotedPoemViewModel>>();
return Partial("~/Pages/Partials/DivanPage/_MultipleQuotedPoemsPartial.cshtml", new _MultipleQuotedPoemsPartialModel()
{
DivanQuotedPoems = quoteds.ToArray(),
PoetImageUrl = poetImageUrl,
PoetNickName = poetNickName,
CanEdit = canEdit
});
}
public string PoemBlockClass => DivanPage != null && DivanPage.Poem != null && DivanPage.Poem.ClaimedByMultiplePoets ? "poem ribbon-parent" : "poem";
public Task<IActionResult> OnPostMarkAsTextOriginalAsync(int bookId, int categoryId)
{
return WithSecureClientAsync(async secureClient =>
{
var response = await secureClient.PutAsync($"{APIRoot.Url}/api/divan/naskban/textoriginal/{bookId}/{categoryId}/true", null);
if (!response.IsSuccessStatusCode)
{
return new BadRequestObjectResult(await ReadErrorMessageAsync(response));
}
return new OkObjectResult(true);
});
}
public Task<IActionResult> OnDeleteRelatedImageLinkAsync(PoemRelatedImageType relatedImageType, Guid linkId, bool removeItemLink)
{
return WithSecureClientAsync(async secureClient =>
{
var response = await secureClient.DeleteAsync(
relatedImageType == PoemRelatedImageType.MuseumLink
? $"{APIRoot.Url}/api/artifacts/divan?linkId={linkId}&removeItemLink={removeItemLink}"
: $"{APIRoot.Url}/api/artifacts/pinterest?linkId={linkId}");
if (!response.IsSuccessStatusCode)
{
return new BadRequestObjectResult(await ReadErrorMessageAsync(response));
}
return new OkObjectResult(true);
});
}
public async Task<IActionResult> OnGetCategoryRecitationsAsync(int catId)
{
var catTop1RecitationsQuery = await _httpClient.GetAsync($"{APIRoot.Url}/api/audio/cattop1/{catId}?includePoemText=false");
if (!catTop1RecitationsQuery.IsSuccessStatusCode)
{
return BadRequest(await ReadErrorMessageAsync(catTop1RecitationsQuery));
}
var categoryTop1Recitations = JsonConvert.DeserializeObject<PublicRecitationViewModel[]>(await catTop1RecitationsQuery.Content.ReadAsStringAsync());
return Partial("~/Pages/Partials/DivanPage/_AudioPlayerPartial.cshtml", new _AudioPlayerPartialModel()
{
LoggedIn = !string.IsNullOrEmpty(Request.Cookies["Token"]),
Recitations = categoryTop1Recitations,
ShowAllRecitaions = true,
CategoryMode = true,
});
}
/// <summary>
/// on-demand "شخصیت‌ها" (characters) tab on a poet/category page - fetches the
/// category-scoped person/relation graph (see DivanRelatedPersonService.GetCatPersonGraphAsync)
/// and renders it with the same peoplegraph.js force-directed viewer used by /PeopleGraph
/// </summary>
/// <param name="catId"></param>
/// <returns></returns>
public async Task<IActionResult> OnGetPersonGraphAsync(int catId)
{
var response = await _httpClient.GetAsync($"{APIRoot.Url}/api/divan/cat/{catId}/persongraph");
if (!response.IsSuccessStatusCode)
{
return BadRequest(await ReadErrorMessageAsync(response));
}
var graph = JsonConvert.DeserializeObject<DivanPersonGraphViewModel>(await response.Content.ReadAsStringAsync());
return Partial("~/Pages/Partials/DivanPage/_PersonGraphPartial.cshtml", new _PersonGraphPartialModel()
{
CatId = catId,
Graph = graph,
});
}
private async Task<_CategoryWordsCountPartialModel> _GetCategoryWordCountsAsync(int catId, int poetId, bool remStopWords = false)
{
var wordSumsResponse = await _httpClient.GetAsync($"{APIRoot.Url}/api/divan/wordsums/{catId}");
if (!wordSumsResponse.IsSuccessStatusCode)
{
return null;
}
var wordSums = JsonConvert.DeserializeObject<CategoryWordCountSummary>(await wordSumsResponse.Content.ReadAsStringAsync());
if (wordSums.TotalWordCount == 0)
{
return null;
}
int pageSize = remStopWords ? 200 : 100;
var wordCountsResponse = await _httpClient.GetAsync($"{APIRoot.Url}/api/divan/wordcounts/{catId}?PageNumber=1&PageSize={pageSize}");
if (!wordCountsResponse.IsSuccessStatusCode)
{
return null;
}
var wordCounts = JsonConvert.DeserializeObject<CategoryWordCount[]>(await wordCountsResponse.Content.ReadAsStringAsync());
if (remStopWords)
{
wordCounts = wordCounts.Where(w => !_urduStopWords.Contains(w.Word)).Take(100).ToArray();
}
return new _CategoryWordsCountPartialModel()
{
CatId = catId,
PoetId = poetId,
WordCounts = wordCounts,
UniqueWordCount = wordSums.UniqueWordCount,
TotalWordCount = wordSums.TotalWordCount,
RemStopWords = remStopWords,
};
}
public async Task<IActionResult> OnGetCategoryWordCountsAsync(int catId, int poetId, bool remStopWords = false)
{
var res = await _GetCategoryWordCountsAsync(catId, poetId, remStopWords);
if (res == null)
{
return BadRequest("الفاظ کی گنتی تک رسائی میں خرابی");
}
return Partial("~/Pages/Partials/DivanPage/_CategoryWordsCountPartial.cshtml", res);
}
public async Task<IActionResult> OnGetSearchCategoryWordCountsAsync(int catId, int poetId, string term, int totalWordCount)
{
if (!string.IsNullOrEmpty(term))
{
term = term.Trim();
}
var wordCountsResponse = await _httpClient.GetAsync($"{APIRoot.Url}/api/divan/wordcounts/{catId}?PageNumber=1&PageSize=100&term={term}");
if (!wordCountsResponse.IsSuccessStatusCode)
{
return BadRequest(await ReadErrorMessageAsync(wordCountsResponse));
}
var wordCounts = JsonConvert.DeserializeObject<CategoryWordCount[]>(await wordCountsResponse.Content.ReadAsStringAsync());
return Partial("~/Pages/Partials/DivanPage/_CategoryWordsCountTablePartial.cshtml", new _CategoryWordsCountTablePartialModel()
{
CatId = catId,
PoetId = poetId,
WordCounts = wordCounts,
TotalWordCount = totalWordCount,
});
}
public async Task<IActionResult> OnGetPoemWordCountsAsync(int poemId)
{
var poemQuery = await _httpClient.GetAsync($"{APIRoot.Url}/api/divan/poem/{poemId}");
if (!poemQuery.IsSuccessStatusCode)
{
return BadRequest(await ReadErrorMessageAsync(poemQuery));
}
var poem = JObject.Parse(await poemQuery.Content.ReadAsStringAsync()).ToObject<DivanPoemCompleteViewModel>();
List<CategoryWordCount> counts = new List<CategoryWordCount>();
foreach (var verse in poem.Verses.Where(v => v.VersePosition != VersePosition.Comment))
{
string[] words = LanguageUtils.MakeTextSearchable(verse.Text).Split([' ', '‌']);
foreach (var word in words)
{
if (string.IsNullOrEmpty(word)) continue;
var wordCount = counts.Where(c => c.Word == word).SingleOrDefault();
if (wordCount != null)
{
wordCount.Count++;
}
else
{
counts.Add(new CategoryWordCount { CatId = 0, Word = word, Count = 1 });
}
}
}
counts.Sort((a, b) => b.Count.CompareTo(a.Count));
for (int i = 0; i < counts.Count; i++)
{
counts[i].RowNmbrInCat = i + 1;
}
counts.Insert(0, new CategoryWordCount()
{
Word = "* کل تعداد",
Count = counts.Sum(c => c.Count),
RowNmbrInCat = 0,
});
return Partial("~/Pages/Partials/DivanPage/_CategoryWordsCountTablePartial.cshtml", new _CategoryWordsCountTablePartialModel()
{
CatId = -1,
PoetId = -1,
TotalWordCount = counts.Where(c => c.RowNmbrInCat > 0).Sum(c => c.Count),
WordCounts = counts.ToArray()
});
}
// --- merged from the old _SimiPage.cshtml.cs (was a partial-class extension of IndexModel) ---
public async Task<IActionResult> OnGetSimilarPoemsPartialAsync(int poemId, int skip, string prosodyMetre, string rhymeLetters, string poemFullUrl, int sectionIndex)
{
string url = $"{APIRoot.Url}/api/divan/section/{poemId}/{sectionIndex}/related?skip={skip}&itemsCount=21";
var response = await _httpClient.GetAsync(url);
if (!response.IsSuccessStatusCode)
return BadRequest(await ReadErrorMessageAsync(response));
var relatedSections = JArray.Parse(await response.Content.ReadAsStringAsync()).ToObject<List<DivanCachedRelatedSection>>();
return Partial("~/Pages/Partials/DivanPage/_SimiPartialView.cshtml", new _SimiPartialViewModel()
{
RelatedSections = relatedSections.ToArray(),
Rhythm = prosodyMetre,
RhymeLetters = rhymeLetters,
Skip = skip,
PoemId = poemId,
PoemFullUrl = poemFullUrl,
SectionIndex = sectionIndex
});
}
public async Task<IActionResult> OnGetSimilarPoemsFromPoetPartialAsync(int poetId, string prosodyMetre, string rhymeLetters, string skipPoemFullUrl1, string skipPoemFullUrl2)
{
string url = $"{APIRoot.Url}/api/divan/poems/similar?metre={prosodyMetre}&rhyme={rhymeLetters}&poetId={poetId}";
var response = await _httpClient.GetAsync(url);
if (!response.IsSuccessStatusCode)
return BadRequest(await ReadErrorMessageAsync(response));
var poems = JArray.Parse(await response.Content.ReadAsStringAsync()).ToObject<List<DivanPoemCompleteViewModel>>();
if (poems.Any(p => p.FullUrl == skipPoemFullUrl1))
poems.RemoveAll(p => p.FullUrl == skipPoemFullUrl1); //TODO: fix errors here
if (poems.Any(p => p.FullUrl == skipPoemFullUrl2))
poems.RemoveAll(p => p.FullUrl == skipPoemFullUrl2);
foreach (var poem in poems)
{
poem.HtmlText = DivanPoemTools.GetPoemHtmlExcerpt(poem.HtmlText);
}
return Partial("~/Pages/Partials/DivanPage/_SimiPartialFromPoetView.cshtml", new _SimiPartialFromPoetViewModel()
{
Poems = poems,
});
}
}
}