using DNTPersianUtils.Core; using GanjooRazor.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.Ganjoor; using RMuseum.Models.Ganjoor.ViewModels; using RMuseum.Models.GanjoorAudio; using RMuseum.Models.GanjoorAudio.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 GanjooRazor.Pages { /// /// Renders a single Ganjoor 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 / _GanjoorPagePartial.cshtml, and /// Startup.cs's AddPageRoute("/GanjoorPage", "{*url}") which now owns the site's catch-all route /// (Index.cshtml keeps its own automatic "/" route unchanged, so home is unaffected). /// [IgnoreAntiforgeryToken(Order = 1001)] [OutputCache(PolicyName = "GanjoorPublicPage")] public partial class GanjoorPageModel : LoginPartialEnabledPageModel { /// /// Persian stop words excluded from category word-count listings when remStopWords is /// requested. /// private static readonly HashSet _persianStopWords = new HashSet { "و", "از", "که", "به", "در", "را", "ز", "است", "می", "این", "چون", "بود", "ای", "تا", "چو", "هر", "با", "چه", "شد", "بی", "خود", "گفت", "نیست", "نه", "گر", "کند", "اگر", "کرد", "باشد", "هم", "روی", "شود", "یک", "دو", "وی", "اندر", "پیش", "آمد", "دارد", "کن", "یا", "همی", "آید", "کرده", "نمی", "کز", "هست", "ام", "کی", "بهر", "فی", "چنین", "پای", "ها", "اند", "ی", "گردد", "داد", "چنان", "کنم", "نبود", "گشت", "دیگر", "باید", "دگر", "چند", "همچو", "شده", "بد", "زان", "پی", "مگر", "آنکه", "رفت", "کنی", "برد", "بدان", "ست", "ازین", "دید", "وز", "گوید", "کجا", "دهد", "گه", "درین", "آخر", "دارم", "خواهد", "نیز", "های", "چرا", "راست", "کان", "رو", "نباشد", "بر", "من", "آن", "تو", "او", "ما", "شما", "مرا", "ار", "داری", "بیا", "همه", "گو", "مکن", "زد", "گفتم", }; private readonly PoetCacheService _poetCache; public bool OfflineMode => GetConfigFlag("OfflineMode"); public bool ReadOnlyMode => GetConfigFlag("ReadOnlyMode"); public const int GanjoorSlashVaznPageId = 39401; /// /// constructor /// public GanjoorPageModel(IConfiguration configuration, HttpClient httpClient, PoetCacheService poetCache ) : base(httpClient, configuration) { _poetCache = poetCache; } /// /// last error /// public string LastError { get; set; } /// /// banner /// public GanjoorSiteBannerViewModel Banner { get; set; } /// /// If the response failed, stores the API's error message in and /// returns true so the caller can short-circuit (the established pattern here is /// `if (await CaptureErrorIfFailedAsync(response)) return Page();`). /// private async Task CaptureErrorIfFailedAsync(HttpResponseMessage response) { if (response.IsSuccessStatusCode) { return false; } LastError = await ReadErrorMessageAsync(response); return true; } public _CommentPartialModel GetCommentModel(GanjoorCommentSummaryViewModel 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(GanjoorQuotedPoemViewModel quotedPoem, GanjoorPageCompleteViewModel page, bool canEdit) { return new _QuotedPoemPartialModel() { GanjoorQuotedPoemViewModel = quotedPoem, PoetImageUrl = page.PoetOrCat.Poet.ImageUrl, PoetNickName = page.PoetOrCat.Poet.Nickname, CanEdit = canEdit, }; } /// /// re-fetch a poem's comments sorted by rating, oldest, or newest (AJAX, called from sortComments() in bk.js) /// public async Task OnGetCommentsPartialAsync(int poemId, GanjoorCommentSortOrder sortOrder = GanjoorCommentSortOrder.TopRated) { var response = await _httpClient.GetAsync($"{APIRoot.Url}/api/ganjoor/poem/{poemId}/comments?sortOrder={sortOrder}"); if (!response.IsSuccessStatusCode) { return BadRequest(await ReadErrorMessageAsync(response)); } var comments = JArray.Parse(await response.Content.ReadAsStringAsync()).ToObject(); 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 GanjoorReloginHandler(Request, Response))) { if (await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response)) { GanjoorUserBookmarkViewModel[] bookmarks = Array.Empty(); HttpResponseMessage bookmarksResponse = await secureClient.GetAsync($"{APIRoot.Url}/api/ganjoor/bookmark/{poemId}"); if (bookmarksResponse.IsSuccessStatusCode) { bookmarks = JsonConvert.DeserializeObject(await bookmarksResponse.Content.ReadAsStringAsync()); } foreach (GanjoorCommentSummaryViewModel 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/ganjoor/poem/{poemId}/comments/myratings"); if (ratingsResponse.IsSuccessStatusCode) { var ratings = JsonConvert.DeserializeObject(await ratingsResponse.Content.ReadAsStringAsync()); if (ratings.Length > 0) { var ratingsDic = ratings.ToDictionary(r => r.CommentId, r => r.Value); foreach (GanjoorCommentSummaryViewModel comment in comments) { _applyCommentRatings(comment, ratingsDic); } } } } } } return Partial("~/Pages/Partials/GanjoorPage/_CommentsListPartial.cshtml", new _CommentsListPartialModel() { Comments = comments, PoemId = poemId, LoggedIn = LoggedIn, }); } public Task OnPostReply(string replyCommentText, int refPoemId, int refCommentId) { return OnPostComment(replyCommentText, refPoemId, refCommentId, -1); } /// /// comment /// public Task OnPostComment(string comment, int poemId, int inReplytoId, int coupletIndex) { var unauthorized = Partial("~/Pages/Partials/GanjoorPage/_CommentPartial.cshtml", new _CommentPartialModel() { Comment = null, Error = NotLoggedInMessage, InReplyTo = null }); return WithSecureClientAsync(async secureClient => { var stringContent = new StringContent( JsonConvert.SerializeObject(new GanjoorCommentPostViewModel() { 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/ganjoor/comment", stringContent); if (response.StatusCode == HttpStatusCode.OK) { var resComment = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync()); resComment.MyComment = true; return Partial("~/Pages/Partials/GanjoorPage/_CommentPartial.cshtml", new _CommentPartialModel() { Comment = resComment, Error = "", InReplyTo = inReplytoId == 0 ? null : new GanjoorCommentSummaryViewModel(), LoggedIn = !string.IsNullOrEmpty(Request.Cookies["Token"]), PoemId = poemId, }); } string rawError = await ReadErrorMessageAsync(response); var sanitizerInfo = TryParseSanitizerTextDroppedError(rawError); return Partial("~/Pages/Partials/GanjoorPage/_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 OnDeleteMyComment(int id) { return WithSecureClientAsync(async secureClient => { var response = await secureClient.DeleteAsync($"{APIRoot.Url}/api/ganjoor/comment?id={id}"); if (!response.IsSuccessStatusCode) { return new BadRequestObjectResult(await ReadErrorMessageAsync(response)); } return new JsonResult(true); }); } public Task OnPutMyComment(int id, string comment) { return WithSecureClientAsync(async secureClient => { var response = await secureClient.PutAsync($"{APIRoot.Url}/api/ganjoor/comment/{id}", new StringContent(JsonConvert.SerializeObject(comment), Encoding.UTF8, "application/json")); if (!response.IsSuccessStatusCode) { return await BadRequestFromApiErrorAsync(response); } return new JsonResult(true); }); } /// /// like / dislike / clear rating for a comment (AJAX, called from rateComment() in bk.js) /// public Task OnPostRateCommentAsync(int id, short value) { return WithSecureClientAsync(async secureClient => { var response = await secureClient.PostAsync($"{APIRoot.Url}/api/ganjoor/poem/comment/{id}/rate?value={value}", null); if (!response.IsSuccessStatusCode) { return new BadRequestObjectResult(await ReadErrorMessageAsync(response)); } var result = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync()); return new JsonResult(result); }); } /// /// Poets (for this page's own #hdr2 author dropdown - same pattern as Search/Simi/etc.) /// public List Poets { get; set; } public bool IsPoetPage { get; set; } public bool IsCatPage { get; set; } public bool IsPoemPage { get; set; } public string CanonicalUrl => $"{Configuration["SiteUrl"]}{GanjoorPage.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(GanjoorPoemSummaryViewModel 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 += "..."; } } } /// /// specify which comments belong to current user /// private async Task _markMyCommentsAndBringUpMyRecitations() { if (GanjoorPage == null) { return; } if (GanjoorPage.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 (GanjoorCommentSummaryViewModel comment in GanjoorPage.Poem.Comments) { comment.MyComment = comment.UserId == userId; _markMyReplies(comment, userId, null); } if (GanjoorPage.Poem.Recitations.Length > 0) { using (HttpClient secureClient = new HttpClient(new GanjoorReloginHandler(Request, Response))) { if (await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response)) { HttpResponseMessage response = await secureClient.GetAsync($"{APIRoot.Url}/api/ganjoor/poem/{GanjoorPage.Poem.Id}/recitations/upvotes"); if (response.IsSuccessStatusCode) { var upVotedIds = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync()); if (upVotedIds.Length > 0) { foreach (var recitation in GanjoorPage.Poem.Recitations) { recitation.UpVotedByUser = upVotedIds.Contains(recitation.Id); } Array.Sort(GanjoorPage.Poem.Recitations, (a, b) => b.UpVotedByUser == a.UpVotedByUser ? a.AudioOrder.CompareTo(b.AudioOrder) : b.UpVotedByUser.CompareTo(a.UpVotedByUser)); } } } } } if (GanjoorPage.Poem.Comments.Length > 0) { using (HttpClient secureClient = new HttpClient(new GanjoorReloginHandler(Request, Response))) { if (await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response)) { HttpResponseMessage response = await secureClient.GetAsync($"{APIRoot.Url}/api/ganjoor/poem/{GanjoorPage.Poem.Id}/comments/myratings"); if (response.IsSuccessStatusCode) { var ratings = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync()); if (ratings.Length > 0) { var ratingsDic = ratings.ToDictionary(r => r.CommentId, r => r.Value); foreach (GanjoorCommentSummaryViewModel comment in GanjoorPage.Poem.Comments) { _applyCommentRatings(comment, ratingsDic); } } } } } } } private void _applyCommentRatings(GanjoorCommentSummaryViewModel comment, Dictionary ratings) { if (ratings.TryGetValue(comment.Id, out short value)) { comment.CurrentUserRatingValue = value; } foreach (var reply in comment.Replies) { _applyCommentRatings(reply, ratings); } } private void _markMyReplies(GanjoorCommentSummaryViewModel parent, Guid userId, GanjoorUserBookmarkViewModel[] 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); } } /// /// get audio description /// public string getAudioDesc(PublicRecitationViewModel recitation, bool contributionLink = false) { string audiodesc = "به خوانش "; if (!string.IsNullOrEmpty(recitation.AudioArtistUrl)) { audiodesc += $"{recitation.AudioArtist}"; } else { audiodesc += $"{recitation.AudioArtist}"; } if (!string.IsNullOrEmpty(recitation.AudioSrc)) { if (!string.IsNullOrEmpty(recitation.AudioSrcUrl)) { audiodesc += $" {recitation.AudioSrc}"; } else { audiodesc += $" {recitation.AudioSrc}"; } } audiodesc += $" (دریافت)"; if (contributionLink) { audiodesc += "
می‌خواهید شما بخوانید؟ اینجا را ببینید."; } return audiodesc; } private async Task preparePoets() { var (success, poets, error) = await _poetCache.GetPoetsAsync(EditorCacheBypass); if (!success) { LastError = error; return false; } Poets = poets; return true; } public async Task 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; } = "fa-IR"; private void _prepareNextPre() { switch (GanjoorPage.GanjoorPageType) { case GanjoorPageType.PoemPage: { if (GanjoorPage.Poem.Next != null) { NextUrl = GanjoorPage.PoetOrCat.Cat.FullUrl + "/" + GanjoorPage.Poem.Next.UrlSlug; NextTitle = GanjoorPage.Poem.Next.Title + ": " + GanjoorPage.Poem.Next.Excerpt; } else if (GanjoorPage.Poem.MixedModeOrder > 0 && GanjoorPage.Poem.Category.Cat.Children.Where(c => c.MixedModeOrder == 0 || c.MixedModeOrder > GanjoorPage.Poem.MixedModeOrder).Any()) { var nextCat = GanjoorPage.Poem.Category.Cat.Children.Where(c => c.MixedModeOrder == 0 || c.MixedModeOrder > GanjoorPage.Poem.MixedModeOrder).OrderBy(c => c.MixedModeOrder).First(); NextUrl = nextCat.FullUrl; NextTitle = nextCat.Title; } else if (GanjoorPage.Poem.Category.Cat.Next != null) { NextUrl = GanjoorPage.Poem.Category.Cat.Next.FullUrl; NextTitle = GanjoorPage.Poem.Category.Cat.Next.Title; } if (GanjoorPage.Poem.Previous != null) { PreviousUrl = GanjoorPage.PoetOrCat.Cat.FullUrl + "/" + GanjoorPage.Poem.Previous.UrlSlug; PreviousTitle = GanjoorPage.Poem.Previous.Title + ": " + GanjoorPage.Poem.Previous.Excerpt; } else if (GanjoorPage.Poem.MixedModeOrder > 0 && GanjoorPage.Poem.Category.Cat.Children.Where(c => c.MixedModeOrder != 0 && c.MixedModeOrder < GanjoorPage.Poem.MixedModeOrder).Any()) { var prevCat = GanjoorPage.Poem.Category.Cat.Children.Where(c => c.MixedModeOrder != 0 && c.MixedModeOrder < GanjoorPage.Poem.MixedModeOrder).OrderByDescending(c => c.MixedModeOrder).First(); PreviousUrl = prevCat.FullUrl; PreviousTitle = prevCat.Title; } else if (GanjoorPage.Poem.Category.Cat.Previous != null) { PreviousUrl = GanjoorPage.Poem.Category.Cat.Previous.FullUrl; PreviousTitle = GanjoorPage.Poem.Category.Cat.Previous.Title; } } break; case GanjoorPageType.CatPage: { if (GanjoorPage.PoetOrCat.Cat.Next != null) { NextUrl = GanjoorPage.PoetOrCat.Cat.Next.FullUrl; NextTitle = GanjoorPage.PoetOrCat.Cat.Next.Title; } if (GanjoorPage.PoetOrCat.Cat.Previous != null) { PreviousUrl = GanjoorPage.PoetOrCat.Cat.Previous.FullUrl; PreviousTitle = GanjoorPage.PoetOrCat.Cat.Previous.Title; } } break; } } public bool MutiPartPoemPage { get; set; } private void _prepareRelatedSecions() { SectionsWithRelated = new List(); SectionsWithMetreAndRhymes = new List(); MutiPartPoemPage = GanjoorPage.Poem.Verses.Where(v => v.VersePosition == VersePosition.Paragraph || v.VersePosition == VersePosition.Single).Any() || GanjoorPage.Poem.Sections.Count(s => s.SectionType == PoemSectionType.WholePoem && s.VerseType == VersePoemSectionType.First) != 1; if ( !MutiPartPoemPage ) { foreach (var section in GanjoorPage.Poem.Sections) { if (section.Top6RelatedSections.Length > 0) { SectionsWithRelated.Add(section); } if (section.SectionType == PoemSectionType.WholePoem && section.GanjoorMetre != null && !string.IsNullOrEmpty(section.RhymeLetters)) { SectionsWithMetreAndRhymes.Add(section); } } } } public GanjoorPoetSuggestedPictureViewModel Photo { get; set; } /// /// 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 "/". /// public async Task 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 LoadGanjoorPageAsync(); 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(); } /// /// A couple of legacy URL shapes get redirected to their modern equivalents. /// Returns null if the current request doesn't match either pattern. /// 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; } } /// /// Handles the legacy "?p=<id>" 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. /// private async Task RedirectByPageIdAsync(string pageId) { var pageUrlResponse = await _httpClient.GetAsync($"{APIRoot.Url}/api/ganjoor/pageurl?id={pageId}"); if (await CaptureErrorIfFailedAsync(pageUrlResponse)) { return Page(); } var pageUrl = JsonConvert.DeserializeObject(await pageUrlResponse.Content.ReadAsStringAsync()); return Redirect(pageUrl); } /// /// Fetches the GanjoorPage 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. /// private async Task LoadGanjoorPageAsync() { var pageQuery = await _httpClient.GetAsync($"{APIRoot.Url}/api/ganjoor/page?url={Request.Path}"); if (!pageQuery.IsSuccessStatusCode) { if (pageQuery.StatusCode == HttpStatusCode.NotFound) { var redirectQuery = await _httpClient.GetAsync($"{APIRoot.Url}/api/ganjoor/redirecturl?url={Request.Path}"); if (redirectQuery.IsSuccessStatusCode) { var redirectUrl = JsonConvert.DeserializeObject(await redirectQuery.Content.ReadAsStringAsync()); return Redirect(redirectUrl); } return NotFound(); } LastError = await ReadErrorMessageAsync(pageQuery); return Page(); } GanjoorPage = JObject.Parse(await pageQuery.Content.ReadAsStringAsync()).ToObject(); if (!string.IsNullOrEmpty(GanjoorPage.HtmlText)) { GanjoorPage.HtmlText = GanjoorPage.HtmlText.Replace("https://ganjoor.net/", "/").Replace("http://ganjoor.net/", "/"); } switch (GanjoorPage.GanjoorPageType) { case GanjoorPageType.PoemPage: await _markMyCommentsAndBringUpMyRecitations(); _preparePoemExcerpt(GanjoorPage.Poem.Next); _preparePoemExcerpt(GanjoorPage.Poem.Previous); GanjoorPage.PoetOrCat = GanjoorPage.Poem.Category; _prepareNextPre(); _prepareRelatedSecions(); IsPoemPage = true; break; case GanjoorPageType.PoetPage: IsPoetPage = true; break; case GanjoorPageType.CatPage: _prepareNextPre(); IsCatPage = true; break; } return null; } /// /// 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. /// private async Task LoadPageTypeSpecificDataAsync() { if (IsPoetPage || IsCatPage) { var catHasAnyRecitationQuery = await _httpClient.GetAsync($"{APIRoot.Url}/api/audio/catany/{GanjoorPage.PoetOrCat.Cat.Id}"); if (await CaptureErrorIfFailedAsync(catHasAnyRecitationQuery)) { return Page(); } CategoryHasRecitations = JsonConvert.DeserializeObject(await catHasAnyRecitationQuery.Content.ReadAsStringAsync()); } if (IsPoetPage) { var responsePhotos = await _httpClient.GetAsync($"{APIRoot.Url}/api/poetphotos/poet/{GanjoorPage.PoetOrCat.Poet.Id}"); if (await CaptureErrorIfFailedAsync(responsePhotos)) { return Page(); } var photos = JArray.Parse(await responsePhotos.Content.ReadAsStringAsync()).ToObject>(); if (photos.Any()) { Photo = photos.First(); } } if (IsPoemPage) { HtmlLanguage = string.IsNullOrEmpty(GanjoorPage.Poem.Language) ? "fa-IR" : GanjoorPage.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(); } } } return null; } /// /// 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). /// 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"] = $"گنجور » {GanjoorPage.PoetOrCat.Poet.Nickname}"; breadCrumbList.AddItem(GanjoorPage.PoetOrCat.Poet.Nickname, GanjoorPage.PoetOrCat.Cat.FullUrl, $"{APIRoot.InternetUrl + GanjoorPage.PoetOrCat.Poet.ImageUrl}"); } private void BuildCatPageTitleAndBreadCrumbs(GoogleBreadCrumbList breadCrumbList) { string title = $"گنجور » "; bool poetCat = true; foreach (var gran in GanjoorPage.PoetOrCat.Cat.Ancestors) { title += $"{gran.Title} » "; breadCrumbList.AddItem(gran.Title, gran.FullUrl, poetCat ? $"{APIRoot.InternetUrl + GanjoorPage.PoetOrCat.Poet.ImageUrl}" : "https://i.ganjoor.net/cat.png"); poetCat = false; } breadCrumbList.AddItem(GanjoorPage.PoetOrCat.Cat.Title, GanjoorPage.PoetOrCat.Cat.FullUrl, "https://i.ganjoor.net/cat.png"); title += GanjoorPage.PoetOrCat.Cat.Title; ViewData["Title"] = title; } private void BuildPoemPageTitleAndBreadCrumbs(GoogleBreadCrumbList breadCrumbList) { ViewData["Title"] = $"گنجور » {GanjoorPage.Poem.FullTitle}"; bool poetCat = true; foreach (var gran in GanjoorPage.Poem.Category.Cat.Ancestors) { breadCrumbList.AddItem(gran.Title, gran.FullUrl, poetCat ? $"{APIRoot.InternetUrl + GanjoorPage.PoetOrCat.Poet.ImageUrl}" : "https://i.ganjoor.net/cat.png"); poetCat = false; } breadCrumbList.AddItem(GanjoorPage.PoetOrCat.Cat.Title, GanjoorPage.PoetOrCat.Cat.FullUrl, "https://i.ganjoor.net/cat.png"); var aiMuseumImage = GanjoorPage.Poem.Images.FirstOrDefault(i => i.TargetPageUrl.StartsWith("https://museum.ganjoor.net/items/ai")); if (aiMuseumImage != null) { breadCrumbList.AddItem(GanjoorPage.Poem.Title, GanjoorPage.Poem.FullUrl, aiMuseumImage.ThumbnailImageUrl.Replace("/thumb/", "/orig/")); } else if (GanjoorPage.Poem.Images.Any()) { breadCrumbList.AddItem(GanjoorPage.Poem.Title, GanjoorPage.Poem.FullUrl, GanjoorPage.Poem.Images.First().ThumbnailImageUrl.Replace("/thumb/", "/orig/")); } else { breadCrumbList.AddItem(GanjoorPage.Poem.Title, GanjoorPage.Poem.FullUrl, "https://i.ganjoor.net/poem.png"); } } private void BuildGenericPageTitleAndBreadCrumbs(GoogleBreadCrumbList breadCrumbList) { if (GanjoorPage.PoetOrCat != null) { bool poetCat = true; string fullTitle = "گنجور » "; if (GanjoorPage.PoetOrCat.Cat.Ancestors.Count == 0) { fullTitle += $"{GanjoorPage.PoetOrCat.Poet.Nickname} » "; } else { foreach (var gran in GanjoorPage.PoetOrCat.Cat.Ancestors) { breadCrumbList.AddItem(gran.Title, gran.FullUrl, poetCat ? $"{APIRoot.InternetUrl + GanjoorPage.PoetOrCat.Poet.ImageUrl}" : "https://i.ganjoor.net/cat.png"); poetCat = false; fullTitle += $"{gran.Title} » "; } } ViewData["Title"] = $"{fullTitle}{GanjoorPage.Title}"; breadCrumbList.AddItem(GanjoorPage.PoetOrCat.Poet.Nickname, GanjoorPage.PoetOrCat.Cat.FullUrl, $"{APIRoot.InternetUrl + GanjoorPage.PoetOrCat.Poet.ImageUrl}"); } else { ViewData["Title"] = $"گنجور » {GanjoorPage.FullTitle}"; } breadCrumbList.AddItem(GanjoorPage.Title, GanjoorPage.FullUrl, "https://i.ganjoor.net/cat.png"); } private async Task LoadCategoryGeoTagsAsync() { var tagsResponse = await _httpClient.GetAsync($"{APIRoot.Url}/api/ganjoor/cat/{GanjoorPage.PoetOrCat.Cat.Id}/geotag"); if (await CaptureErrorIfFailedAsync(tagsResponse)) { return Page(); } CategoryPoemGeoDateTags = JsonConvert.DeserializeObject(await tagsResponse.Content.ReadAsStringAsync()); return null; } public async Task OnGetBNumPartialAsync(int poemId, int coupletIndex) { var responseCoupletComments = await _httpClient.GetAsync($"{APIRoot.Url}/api/ganjoor/poem/{poemId}/comments?coupletIndex={coupletIndex}"); if (!responseCoupletComments.IsSuccessStatusCode) { return BadRequest(await ReadErrorMessageAsync(responseCoupletComments)); } var comments = JArray.Parse(await responseCoupletComments.Content.ReadAsStringAsync()).ToObject>(); 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>(); var responseCoupletSections = await _httpClient.GetAsync($"{APIRoot.Url}/api/ganjoor/couplet/{poemId}/{coupletIndex}/sections"); if (!responseCoupletSections.IsSuccessStatusCode) { return BadRequest(await ReadErrorMessageAsync(responseCoupletSections)); } var responseVerses = await _httpClient.GetAsync($"{APIRoot.Url}/api/ganjoor/poem/{poemId}/verses?coupletIndex={coupletIndex}"); if (!responseVerses.IsSuccessStatusCode) { return BadRequest(await ReadErrorMessageAsync(responseVerses)); } var verses = JArray.Parse(await responseVerses.Content.ReadAsStringAsync()).ToObject>(); var sections = JArray.Parse(await responseCoupletSections.Content.ReadAsStringAsync()).ToObject>(); var sectionsWithMetreAndRhymes = new List(); foreach (var section in sections) { if (section.GanjoorMetreId != null && !string.IsNullOrEmpty(section.RhymeLetters)) { if (!sectionsWithMetreAndRhymes.Any(s => s.GanjoorMetreId == section.GanjoorMetreId && 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 GanjoorReloginHandler(Request, Response))) { if (await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response)) { HttpResponseMessage responseIsBookmarked = await secureClient.GetAsync($"{APIRoot.Url}/api/ganjoor/bookmark/{poemId}"); if (!responseIsBookmarked.IsSuccessStatusCode) { return BadRequest(await ReadErrorMessageAsync(responseIsBookmarked)); } var bookmarks = JsonConvert.DeserializeObject(await responseIsBookmarked.Content.ReadAsStringAsync()); if (bookmarks.Where(b => b.CoupletIndex == coupletIndex).Any()) isBookmarked = true; foreach (GanjoorCommentSummaryViewModel 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/ganjoor/poem/{poemId}/comments/myratings"); if (responseRatings.IsSuccessStatusCode) { var ratings = JsonConvert.DeserializeObject(await responseRatings.Content.ReadAsStringAsync()); if (ratings.Length > 0) { var ratingsDic = ratings.ToDictionary(r => r.CommentId, r => r.Value); foreach (GanjoorCommentSummaryViewModel comment in comments) { _applyCommentRatings(comment, ratingsDic); } } } } } } } return Partial("~/Pages/Partials/GanjoorPage/_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 OnPostSwitchBookmarkAsync(int poemId, int coupletIndex) { return WithSecureClientAsync(async secureClient => { var response = await secureClient.PostAsync($"{APIRoot.Url}/api/ganjoor/bookmark/switch/ret/{poemId}/{coupletIndex}", null); if (!response.IsSuccessStatusCode) { return new BadRequestObjectResult(await ReadErrorMessageAsync(response)); } var res = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync()); return new OkObjectResult(res); }); } public Task OnGetIsCoupletBookmarkedAsync(int poemId, int coupletIndex) { return WithSecureClientAsync(async secureClient => { var response = await secureClient.GetAsync($"{APIRoot.Url}/api/ganjoor/bookmark/{poemId}/{coupletIndex}"); if (!response.IsSuccessStatusCode) { return new BadRequestObjectResult(await ReadErrorMessageAsync(response)); } var res = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync()); return new OkObjectResult(res); }); } public Task OnGetPoemBookmarksAsync(int poemId) { return WithSecureClientAsync(async secureClient => { var response = await secureClient.GetAsync($"{APIRoot.Url}/api/ganjoor/bookmark/{poemId}"); if (!response.IsSuccessStatusCode) { return new BadRequestObjectResult(await ReadErrorMessageAsync(response)); } var res = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync()); return new OkObjectResult(res); }); } /// /// unused now, to be removed /// public Task OnGetUserUpvotedRecitationsAsync(int poemId) { return WithSecureClientAsync(async secureClient => { var response = await secureClient.GetAsync($"{APIRoot.Url}/api/ganjoor/poem/{poemId}/recitations/upvotes"); if (!response.IsSuccessStatusCode) { return new BadRequestObjectResult(await ReadErrorMessageAsync(response)); } var res = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync()); return new OkObjectResult(res); }); } public Task 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(await response.Content.ReadAsStringAsync()); return new OkObjectResult(res); }); } public Task 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(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 OnPutBookmarkNote(Guid id, string note) { return WithSecureClientAsync(async secureClient => { var response = await secureClient.PutAsync($"{APIRoot.Url}/api/ganjoor/bookmark/{id}", new StringContent(JsonConvert.SerializeObject(note), Encoding.UTF8, "application/json")); if (!response.IsSuccessStatusCode) { return await BadRequestFromApiErrorAsync(response); } return new JsonResult(true); }); } public Task 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 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 OnGetMoreQuotedPoemsForRelatedPoemPartialAsync(int poemId, int relatedPoemId, string poetImageUrl, string poetNickName, bool canEdit) { string url = $"{APIRoot.Url}/api/ganjoor/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>(); 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/GanjoorPage/_MultipleQuotedPoemsPartial.cshtml", new _MultipleQuotedPoemsPartialModel() { GanjoorQuotedPoems = quoteds.ToArray(), PoetImageUrl = poetImageUrl, PoetNickName = poetNickName, CanEdit = canEdit }); } public async Task OnGetMoreQuotedPoemsPartialAsync(int poemId, int skip, string poetImageUrl, string poetNickName, bool canEdit) { string url = $"{APIRoot.Url}/api/ganjoor/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>(); return Partial("~/Pages/Partials/GanjoorPage/_MultipleQuotedPoemsPartial.cshtml", new _MultipleQuotedPoemsPartialModel() { GanjoorQuotedPoems = quoteds.ToArray(), PoetImageUrl = poetImageUrl, PoetNickName = poetNickName, CanEdit = canEdit }); } public string PoemBlockClass => GanjoorPage != null && GanjoorPage.Poem != null && GanjoorPage.Poem.ClaimedByMultiplePoets ? "poem ribbon-parent" : "poem"; public Task OnPostMarkAsTextOriginalAsync(int bookId, int categoryId) { return WithSecureClientAsync(async secureClient => { var response = await secureClient.PutAsync($"{APIRoot.Url}/api/ganjoor/naskban/textoriginal/{bookId}/{categoryId}/true", null); if (!response.IsSuccessStatusCode) { return new BadRequestObjectResult(await ReadErrorMessageAsync(response)); } return new OkObjectResult(true); }); } public Task OnDeleteRelatedImageLinkAsync(PoemRelatedImageType relatedImageType, Guid linkId, bool removeItemLink) { return WithSecureClientAsync(async secureClient => { var response = await secureClient.DeleteAsync( relatedImageType == PoemRelatedImageType.MuseumLink ? $"{APIRoot.Url}/api/artifacts/ganjoor?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 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(await catTop1RecitationsQuery.Content.ReadAsStringAsync()); return Partial("~/Pages/Partials/GanjoorPage/_AudioPlayerPartial.cshtml", new _AudioPlayerPartialModel() { LoggedIn = !string.IsNullOrEmpty(Request.Cookies["Token"]), Recitations = categoryTop1Recitations, ShowAllRecitaions = true, CategoryMode = true, }); } private async Task<_CategoryWordsCountPartialModel> _GetCategoryWordCountsAsync(int catId, int poetId, bool remStopWords = false) { var wordSumsResponse = await _httpClient.GetAsync($"{APIRoot.Url}/api/ganjoor/wordsums/{catId}"); if (!wordSumsResponse.IsSuccessStatusCode) { return null; } var wordSums = JsonConvert.DeserializeObject(await wordSumsResponse.Content.ReadAsStringAsync()); if (wordSums.TotalWordCount == 0) { return null; } int pageSize = remStopWords ? 200 : 100; var wordCountsResponse = await _httpClient.GetAsync($"{APIRoot.Url}/api/ganjoor/wordcounts/{catId}?PageNumber=1&PageSize={pageSize}"); if (!wordCountsResponse.IsSuccessStatusCode) { return null; } var wordCounts = JsonConvert.DeserializeObject(await wordCountsResponse.Content.ReadAsStringAsync()); if (remStopWords) { wordCounts = wordCounts.Where(w => !_persianStopWords.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 OnGetCategoryWordCountsAsync(int catId, int poetId, bool remStopWords = false) { var res = await _GetCategoryWordCountsAsync(catId, poetId, remStopWords); if (res == null) { return BadRequest("خطا در دسترسی به شمارش واژگان"); } return Partial("~/Pages/Partials/GanjoorPage/_CategoryWordsCountPartial.cshtml", res); } public async Task OnGetSearchCategoryWordCountsAsync(int catId, int poetId, string term, int totalWordCount) { if (!string.IsNullOrEmpty(term)) { term = term.Trim(); } var wordCountsResponse = await _httpClient.GetAsync($"{APIRoot.Url}/api/ganjoor/wordcounts/{catId}?PageNumber=1&PageSize=100&term={term}"); if (!wordCountsResponse.IsSuccessStatusCode) { return BadRequest(await ReadErrorMessageAsync(wordCountsResponse)); } var wordCounts = JsonConvert.DeserializeObject(await wordCountsResponse.Content.ReadAsStringAsync()); return Partial("~/Pages/Partials/GanjoorPage/_CategoryWordsCountTablePartial.cshtml", new _CategoryWordsCountTablePartialModel() { CatId = catId, PoetId = poetId, WordCounts = wordCounts, TotalWordCount = totalWordCount, }); } public async Task OnGetPoemWordCountsAsync(int poemId) { var poemQuery = await _httpClient.GetAsync($"{APIRoot.Url}/api/ganjoor/poem/{poemId}"); if (!poemQuery.IsSuccessStatusCode) { return BadRequest(await ReadErrorMessageAsync(poemQuery)); } var poem = JObject.Parse(await poemQuery.Content.ReadAsStringAsync()).ToObject(); List counts = new List(); 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/GanjoorPage/_CategoryWordsCountTablePartial.cshtml", new _CategoryWordsCountTablePartialModel() { CatId = -1, PoetId = -1, TotalWordCount = counts.Where(c => c.RowNmbrInCat > 0).Sum(c => c.Count), WordCounts = counts.ToArray() }); } public async Task OnGetTopVisitsAsync(string url) { url = $"https://ganjoor.net/{url}/"; var apiUrl = $"https://track.kntr.ir/api/reporting/toppages/1/ganjoor.net?parentUrl={WebUtility.UrlEncode(url)}&count=20"; var topVisitsResponse = await _httpClient.GetAsync(apiUrl); if (!topVisitsResponse.IsSuccessStatusCode) { return Partial("~/Pages/Partials/GanjoorPage/_TopVisitsPartial.cshtml", new _TopVisitsPartialModel() { Visits = null }); } var topVisits = JsonConvert.DeserializeObject(await topVisitsResponse.Content.ReadAsStringAsync()); return Partial("~/Pages/Partials/GanjoorPage/_TopVisitsPartial.cshtml", new _TopVisitsPartialModel() { Visits = topVisits }); } public async Task OnGetSevenDaysVisitsAsync(string url) { url = $"https://ganjoor.net/{url}/"; var apiUrl = $"https://track.kntr.ir/api/reporting/dailypagevisits/1/ganjoor.net/for/{WebUtility.UrlEncode(url)}?start={DateTime.Now.Date.AddDays(-6).ToString("yyyy-MM-dd")}"; var s7ndaysVisitsResponse = await _httpClient.GetAsync(apiUrl); if (!s7ndaysVisitsResponse.IsSuccessStatusCode) { return Partial("~/Pages/Partials/GanjoorPage/_7DaysVisitsPartial.cshtml", new _7DaysVisitsPartialModel() { SevenDaysVisits = null }); } var s7ndaysVisits = JsonConvert.DeserializeObject(await s7ndaysVisitsResponse.Content.ReadAsStringAsync()); return Partial("~/Pages/Partials/GanjoorPage/_7DaysVisitsPartial.cshtml", new _7DaysVisitsPartialModel() { SevenDaysVisits = s7ndaysVisits }); } // --- merged from the old _SimiPage.cshtml.cs (was a partial-class extension of IndexModel) --- public async Task OnGetSimilarPoemsPartialAsync(int poemId, int skip, string prosodyMetre, string rhymeLetters, string poemFullUrl, int sectionIndex) { string url = $"{APIRoot.Url}/api/ganjoor/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>(); return Partial("~/Pages/Partials/GanjoorPage/_SimiPartialView.cshtml", new _SimiPartialViewModel() { RelatedSections = relatedSections.ToArray(), Rhythm = prosodyMetre, RhymeLetters = rhymeLetters, Skip = skip, PoemId = poemId, PoemFullUrl = poemFullUrl, SectionIndex = sectionIndex }); } public async Task OnGetSimilarPoemsFromPoetPartialAsync(int poetId, string prosodyMetre, string rhymeLetters, string skipPoemFullUrl1, string skipPoemFullUrl2) { string url = $"{APIRoot.Url}/api/ganjoor/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>(); 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 = GanjoorPoemTools.GetPoemHtmlExcerpt(poem.HtmlText); } return Partial("~/Pages/Partials/GanjoorPage/_SimiPartialFromPoetView.cshtml", new _SimiPartialFromPoetViewModel() { Poems = poems, }); } } }