using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using RMuseum.DbContext; using RMuseum.Models.Ganjoor; using RMuseum.Models.Ganjoor.ViewModels; using RMuseum.Models.GanjoorAudio; using RMuseum.Models.GanjoorAudio.ViewModels; using RMuseum.Models.MusicCatalogue; using RSecurityBackend.Models.Generic; using RSecurityBackend.Services; using System; using System.Collections.Generic; using System.Data; using System.Globalization; using System.Linq; using System.Threading.Tasks; using RMuseum.Models.Artifact; using RSecurityBackend.Services.Implementation; using DNTPersianUtils.Core; namespace RMuseum.Services.Implementation { /// /// IGanjoorService implementation /// public partial class GanjoorService : IGanjoorService { /// /// Get List of poets /// /// /// /// public async Task> GetPoets(bool websitePoets, bool includeBio = true) { try { var res = await (from poet in _context.GanjoorPoets join cat in _context.GanjoorCategories.Where(c => c.ParentId == null) on poet.Id equals cat.PoetId where !websitePoets || poet.Id < 200 orderby poet.Name descending select new GanjoorPoetViewModel() { Id = poet.Id, Name = poet.Name, Description = includeBio ? poet.Description : null, FullUrl = cat.FullUrl, RootCatId = cat.Id, Nickname = poet.Nickname, Published = poet.Published, ImageUrl = poet.RImageId == null ? "" : $"/api/ganjoor/poet/image{cat.FullUrl}.png" } ) .ToListAsync(); StringComparer fa = StringComparer.Create(new CultureInfo("fa-IR"), true); res.Sort((a, b) => fa.Compare(a.Name, b.Name)); return new RServiceResult ( res.ToArray() ); ; } catch (Exception exp) { return new RServiceResult(null, exp.ToString()); } } /// /// get poet by id /// /// /// public async Task> GetPoetById(int id) { try { var poet = await _context.GanjoorPoets.Where(p => p.Id == id).FirstOrDefaultAsync(); if (poet == null) return new RServiceResult(null); var cat = await _context.GanjoorCategories.Where(c => c.ParentId == null && c.PoetId == id).FirstOrDefaultAsync(); return await GetCatById(cat.Id); } catch (Exception exp) { return new RServiceResult(null, exp.ToString()); } } /// /// get poet by url /// /// /// public async Task> GetPoetByUrl(string url) { try { // /hafez/ => /hafez : if (url.LastIndexOf('/') == url.Length - 1) { url = url.Substring(0, url.Length - 1); } var cat = await _context.GanjoorCategories.Where(c => c.FullUrl == url && c.ParentId == null).SingleOrDefaultAsync(); if (cat == null) return new RServiceResult(null); return await GetCatById(cat.Id); } catch (Exception exp) { return new RServiceResult(null, exp.ToString()); } } /// /// poet image id by url /// /// /// public async Task> GetPoetImageIdByUrl(string url) { try { // /hafez/ => /hafez : if (url.LastIndexOf('/') == url.Length - 1) { url = url.Substring(0, url.Length - 1); } var cat = await _context.GanjoorCategories.Where(c => c.FullUrl == url && c.ParentId == null).SingleOrDefaultAsync(); if (cat == null) return new RServiceResult(Guid.Empty); var poet = await _context.GanjoorPoets.Where(p => p.Id == cat.PoetId).SingleOrDefaultAsync(); return new RServiceResult((Guid)poet.RImageId); } catch (Exception exp) { return new RServiceResult(Guid.Empty, exp.ToString()); } } /// /// get cat by url /// /// /// /// public async Task> GetCatByUrl(string url, bool poems = true) { try { // /hafez/ => /hafez : if (url.LastIndexOf('/') == url.Length - 1) { url = url.Substring(0, url.Length - 1); } var cat = await _context.GanjoorCategories.Where(c => c.FullUrl == url).SingleOrDefaultAsync(); if (cat == null) return new RServiceResult(null); return await GetCatById(cat.Id); } catch (Exception exp) { return new RServiceResult(null, exp.ToString()); } } /// /// get cat by id /// /// /// /// public async Task> GetCatById(int id, bool poems = true) { try { var cat = await _context.GanjoorCategories.Include(c => c.Poet).Include(c => c.Parent).Where(c => c.Id == id).FirstOrDefaultAsync(); if (cat == null) return new RServiceResult(null); List ancetors = new List(); var parent = cat.Parent; while (parent != null) { ancetors.Insert(0, new GanjoorCatViewModel() { Id = parent.Id, Title = parent.Title, UrlSlug = parent.UrlSlug, FullUrl = parent.FullUrl }); parent = await _context.GanjoorCategories.Where(c => c.Id == parent.ParentId).FirstOrDefaultAsync(); } int nextCatId = await _context.GanjoorCategories.Where(c => c.PoetId == cat.PoetId && c.ParentId == cat.ParentId && c.Id > id).AnyAsync() ? await _context.GanjoorCategories.Where(c => c.PoetId == cat.PoetId && c.ParentId == cat.ParentId && c.Id > id).MinAsync(c => c.Id) : 0; var nextCat = nextCatId == 0 ? null : await _context .GanjoorCategories .Where(c => c.Id == nextCatId) .Select ( c => new GanjoorCatViewModel() { Id = c.Id, Title = c.Title, UrlSlug = c.UrlSlug, FullUrl = c.FullUrl //other fields null } ).SingleOrDefaultAsync(); int preCatId = await _context.GanjoorCategories.Where(c => c.PoetId == cat.PoetId && c.ParentId == cat.ParentId && c.Id < id).AnyAsync() ? await _context.GanjoorCategories.Where(c => c.PoetId == cat.PoetId && c.ParentId == cat.ParentId && c.Id < id).MaxAsync(c => c.Id) : 0; var preCat = preCatId == 0 ? null : await _context .GanjoorCategories .Where(c => c.Id == preCatId) .Select ( c => new GanjoorCatViewModel() { Id = c.Id, Title = c.Title, UrlSlug = c.UrlSlug, FullUrl = c.FullUrl //other fields null } ).SingleOrDefaultAsync(); GanjoorCatViewModel catViewModel = new GanjoorCatViewModel() { Id = cat.Id, Title = cat.Title, UrlSlug = cat.UrlSlug, FullUrl = cat.FullUrl, Next = nextCat, Previous = preCat, Ancestors = ancetors, Children = await _context.GanjoorCategories.Where(c => c.ParentId == cat.Id).OrderBy(cat => cat.Id).Select ( c => new GanjoorCatViewModel() { Id = c.Id, Title = c.Title, UrlSlug = c.UrlSlug, FullUrl = c.FullUrl } ).ToListAsync(), Poems = poems ? await _context.GanjoorPoems.Where(p => p.CatId == cat.Id).OrderBy(p => p.Id).Select ( p => new GanjoorPoemSummaryViewModel() { Id = p.Id, Title = p.Title, UrlSlug = p.UrlSlug, Excerpt = _context.GanjoorVerses.Where(v => v.PoemId == p.Id && v.VOrder == 1).FirstOrDefault().Text } ).ToListAsync() : null }; return new RServiceResult ( new GanjoorPoetCompleteViewModel() { Poet = await _context.GanjoorPoets.Where(p => p.Id == cat.PoetId) .Select(p => new GanjoorPoetViewModel() { Id = p.Id, Name = p.Name, Description = p.Description, FullUrl = _context.GanjoorCategories.Where(c => c.PoetId == p.Id && c.ParentId == null).Single().FullUrl, RootCatId = _context.GanjoorCategories.Where(c => c.PoetId == p.Id && c.ParentId == null).Single().Id, Nickname = p.Nickname, Published = p.Published, ImageUrl = p.RImageId == null ? "" : $"/api/ganjoor/poet/image{_context.GanjoorCategories.Where(c => c.PoetId == p.Id && c.ParentId == null).Single().FullUrl}.png" }).FirstOrDefaultAsync(), Cat = catViewModel } ); } catch (Exception exp) { return new RServiceResult(null, exp.ToString()); } } /// /// get page url by id /// /// /// public async Task> GetPageUrlById(int id) { try { var dbPage = await _context.GanjoorPages.Where(p => p.Id == id).SingleOrDefaultAsync(); if (dbPage == null) return new RServiceResult(null); //not found return new RServiceResult(dbPage.FullUrl); } catch (Exception exp) { return new RServiceResult(null, exp.ToString()); } } /// /// get page by url /// /// /// /// public async Task> GetPageByUrl(string url, bool catPoems = true) { try { if (url.IndexOf('?') != -1) { url = url.Substring(0, url.IndexOf('?')); } // /hafez/ => /hafez : if (url.LastIndexOf('/') == url.Length - 1) { url = url.Substring(0, url.Length - 1); } var dbPage = await _context.GanjoorPages.Where(p => p.FullUrl == url).SingleOrDefaultAsync(); if (dbPage == null) return new RServiceResult(null); //not found var secondPoet = dbPage.SecondPoetId == null ? null : await (from poet in _context.GanjoorPoets join cat in _context.GanjoorCategories.Where(c => c.ParentId == null) on poet.Id equals cat.PoetId where poet.Id == (int)dbPage.SecondPoetId orderby poet.Name descending select new GanjoorPoetViewModel() { Id = poet.Id, Name = poet.Name, FullUrl = cat.FullUrl, RootCatId = cat.Id, Nickname = poet.Nickname, Published = poet.Published, ImageUrl = poet.RImageId == null ? "" : $"/api/ganjoor/poet/image{cat.FullUrl}.png" } ) .SingleAsync(); GanjoorPageCompleteViewModel page = new GanjoorPageCompleteViewModel() { Id = dbPage.Id, GanjoorPageType = dbPage.GanjoorPageType, Title = dbPage.Title, FullTitle = dbPage.FullTitle, UrlSlug = dbPage.UrlSlug, FullUrl = dbPage.FullUrl, HtmlText = dbPage.HtmlText, SecondPoet = secondPoet }; switch (page.GanjoorPageType) { case GanjoorPageType.PoemPage: { var poemRes = await GetPoemById((int)dbPage.PoemId); if (!string.IsNullOrEmpty(poemRes.ExceptionString)) { return new RServiceResult(null, poemRes.ExceptionString); } page.Poem = poemRes.Result; } break; case GanjoorPageType.CatPage: { var catRes = await GetCatById((int)dbPage.CatId); if (!string.IsNullOrEmpty(catRes.ExceptionString)) { return new RServiceResult(null, catRes.ExceptionString); } page.PoetOrCat = catRes.Result; } break; default: { if (dbPage.PoetId != null) { var poetRes = await GetPoetById((int)dbPage.PoetId); if (!string.IsNullOrEmpty(poetRes.ExceptionString)) { return new RServiceResult(null, poetRes.ExceptionString); } page.PoetOrCat = poetRes.Result; var pre = await _context.GanjoorPages.Where(p => p.GanjoorPageType == page.GanjoorPageType && p.ParentId == dbPage.ParentId && p.PoetId == dbPage.PoetId && ((p.PageOrder < dbPage.PageOrder) || (p.PageOrder == dbPage.PageOrder && p.Id < dbPage.Id))) .OrderByDescending(p => p.PageOrder) .ThenByDescending(p => p.Id) .FirstOrDefaultAsync(); if(pre != null) { page.Previous = new GanjoorPageSummaryViewModel() { Id = pre.Id, Title = pre.Title, FullUrl = pre.FullUrl }; } var next = await _context.GanjoorPages.Where(p => p.GanjoorPageType == page.GanjoorPageType && p.ParentId == dbPage.ParentId && p.PoetId == dbPage.PoetId && ((p.PageOrder > dbPage.PageOrder) || (p.PageOrder == dbPage.PageOrder && p.Id > dbPage.Id))) .OrderBy(p => p.PageOrder) .ThenBy(p => p.Id) .FirstOrDefaultAsync(); if (next != null) { page.Next = new GanjoorPageSummaryViewModel() { Id = next.Id, Title = next.Title, FullUrl = next.FullUrl }; } } } break; } return new RServiceResult(page); } catch (Exception exp) { return new RServiceResult(null, exp.ToString()); } } /// /// get poem recitations (PlainText/HtmlText are intentionally empty) /// /// /// public async Task> GetPoemRecitations(int id) { try { var source = from audio in _context.Recitations join poem in _context.GanjoorPoems on audio.GanjoorPostId equals poem.Id where audio.ReviewStatus == AudioReviewStatus.Approved && poem.Id == id orderby audio.AudioOrder select new PublicRecitationViewModel() { Id = audio.Id, PoemId = audio.GanjoorPostId, PoemFullTitle = poem.FullTitle, PoemFullUrl = poem.FullUrl, AudioTitle = audio.AudioTitle, AudioArtist = audio.AudioArtist, AudioArtistUrl = audio.AudioArtistUrl, AudioSrc = audio.AudioSrc, AudioSrcUrl = audio.AudioSrcUrl, LegacyAudioGuid = audio.LegacyAudioGuid, Mp3FileCheckSum = audio.Mp3FileCheckSum, Mp3SizeInBytes = audio.Mp3SizeInBytes, PublishDate = audio.ReviewDate, FileLastUpdated = audio.FileLastUpdated, Mp3Url = $"https://ganjgah.ir/api/audio/file/{audio.Id}.mp3", XmlText = $"https://ganjgah.ir/api/audio/xml/{audio.Id}", PlainText = "", //poem.PlainText HtmlText = "",//poem.HtmlText }; return new RServiceResult(await source.ToArrayAsync()); } catch (Exception exp) { return new RServiceResult(null, exp.ToString()); } } /// /// get poem comments /// /// /// /// public async Task> GetPoemComments(int poemId, Guid userId) { try { var source = from comment in _context.GanjoorComments.Include(c => c.User) where (comment.Status == PublishStatus.Published || (userId != Guid.Empty && comment.Status == PublishStatus.Awaiting && comment.UserId == userId)) && comment.PoemId == poemId orderby comment.CommentDate select new GanjoorCommentSummaryViewModel() { Id = comment.Id, AuthorName = comment.User == null ? comment.AuthorName : $"{comment.User.NickName}", AuthorUrl = comment.AuthorUrl, CommentDate = comment.CommentDate, HtmlComment = comment.HtmlComment, PublishStatus = comment.Status == PublishStatus.Awaiting ? "در انتظار تأیید" : "", InReplyToId = comment.InReplyToId, UserId = comment.UserId }; GanjoorCommentSummaryViewModel[] allComments = await source.ToArrayAsync(); foreach (GanjoorCommentSummaryViewModel comment in allComments) { comment.AuthorName = comment.AuthorName.ToPersianNumbers().ApplyCorrectYeKe(); } GanjoorCommentSummaryViewModel[] rootComments = allComments.Where(c => c.InReplyToId == null).ToArray(); foreach (GanjoorCommentSummaryViewModel comment in rootComments) { _FindReplies(comment, allComments); } return new RServiceResult(rootComments); } catch (Exception exp) { return new RServiceResult(null, exp.ToString()); } } private void _FindReplies(GanjoorCommentSummaryViewModel comment, GanjoorCommentSummaryViewModel[] allComments) { comment.Replies = allComments.Where(c => c.InReplyToId == comment.Id).ToArray(); foreach (GanjoorCommentSummaryViewModel reply in comment.Replies) { _FindReplies(reply, allComments); } } /// /// new comment /// /// /// /// /// /// /// public async Task> NewComment(Guid userId, string ip, int poemId, string content, int? inReplyTo) { try { if (string.IsNullOrEmpty(content)) { return new RServiceResult(null, "متن حاشیه خالی است."); } var userRes = await _appUserService.GetUserInformation(userId); content = content.ToPersianNumbers().ApplyCorrectYeKe(); GanjoorComment comment = new GanjoorComment() { UserId = userId, AuthorIpAddress = ip, CommentDate = DateTime.Now, HtmlComment = content, InReplyToId = inReplyTo, PoemId = poemId, Status = PublishStatus.Published, }; _context.GanjoorComments.Add(comment); await _context.SaveChangesAsync(); if(inReplyTo != null) { GanjoorComment refComment = await _context.GanjoorComments.Where(c => c.Id == (int)inReplyTo).SingleAsync(); if(refComment.UserId != null) { var poem = await _context.GanjoorPoems.Where(p => p.Id == comment.PoemId).SingleAsync(); await _notificationService.PushNotification((Guid)refComment.UserId, "پاسخ به حاشیهٔ شما", $"{userRes.Result.NickName} به حاشیهٔ شما روی {poem.FullTitle} این پاسخ را نوشته است {Environment.NewLine}" + $"{content}" + $"این متن حاشیهٔ خود شماست: {Environment.NewLine}" + $"{refComment.HtmlComment}" ); } } return new RServiceResult ( new GanjoorCommentSummaryViewModel() { Id = comment.Id, AuthorName = $"{userRes.Result.NickName}", AuthorUrl = comment.AuthorUrl, CommentDate = comment.CommentDate, HtmlComment = comment.HtmlComment, PublishStatus = comment.Status == PublishStatus.Awaiting ? "در انتظار تأیید" : "", InReplyToId = comment.InReplyToId, UserId = comment.UserId, Replies = new GanjoorCommentSummaryViewModel[] { } } ); } catch (Exception exp) { return new RServiceResult(null, exp.ToString()); } } /// /// update user's own comment /// /// /// /// /// public async Task> EditMyComment(Guid userId, int commentId, string htmlComment) { try { GanjoorComment comment = await _context.GanjoorComments.Where(c => c.Id == commentId && c.UserId == userId).SingleOrDefaultAsync();//userId is not part of key but it helps making call secure if (comment == null) { return new RServiceResult(false); //not found } comment.HtmlComment = htmlComment; _context.GanjoorComments.Update(comment); await _context.SaveChangesAsync(); return new RServiceResult(true); } catch (Exception exp) { return new RServiceResult(false, exp.ToString()); } } /// /// delete a reported or abusive comment /// /// /// /// public async Task> DeleteModerateComment(int commentId, string reason) { try { GanjoorComment comment = await _context.GanjoorComments.Where(c => c.Id == commentId).SingleOrDefaultAsync(); if (comment == null) { return new RServiceResult(false); //not found } if (comment.UserId != null) { reason = string.IsNullOrEmpty(reason) ? "" : $"علت ارائه شده برای حذف یا متن گزارش کاربر شاکی: {Environment.NewLine}" + $"{reason} {Environment.NewLine}"; await _notificationService.PushNotification((Guid)comment.UserId, "حذف حاشیهٔ شما", $"حاشیهٔ شما به دلیل ناسازگاری با قوانین حاشیه‌گذاری گنجور و طبق گزارشات دیگر کاربران حذف شده است..{Environment.NewLine}" + $"{reason}" + $"این متن حاشیهٔ حذف شدهٔ شماست: {Environment.NewLine}" + $"{comment.HtmlComment}" ); } //if user has got replies, delete them and notify their owners of what happened var replies = await _FindReplies(comment); for (int i = replies.Count - 1; i >= 0; i--) { if (replies[i].UserId != null) { await _notificationService.PushNotification((Guid)replies[i].UserId, "حذف پاسخ شما به حاشیه", $"پاسخ شما به یکی از حاشیه‌های گنجور به دلیل حذف زنجیرهٔ حاشیه توسط یکی از حاشیه‌گذاران حذف شده است.{Environment.NewLine}" + $"این متن حاشیهٔ حذف شدهٔ شماست: {Environment.NewLine}" + $"{replies[i].HtmlComment}" ); } _context.GanjoorComments.Remove(replies[i]); } _context.GanjoorComments.Remove(comment); await _context.SaveChangesAsync(); return new RServiceResult(true); } catch (Exception exp) { return new RServiceResult(false, exp.ToString()); } } /// /// delete user own comment /// /// /// /// public async Task> DeleteMyComment(Guid userId, int commentId) { try { GanjoorComment comment = await _context.GanjoorComments.Where(c => c.Id == commentId && c.UserId == userId).SingleOrDefaultAsync();//userId is not part of key but it helps making call secure if (comment == null) { return new RServiceResult(false); //not found } //if user has got replies, delete them and notify their owners of what happened var replies = await _FindReplies(comment); for (int i = replies.Count - 1; i >= 0; i--) { if (replies[i].UserId != null && replies[i].UserId != userId) { await _notificationService.PushNotification((Guid)replies[i].UserId, "حذف پاسخ شما به حاشیه", $"پاسخ شما به یکی از حاشیه‌های گنجور به دلیل حذف زنجیرهٔ حاشیه توسط یکی از حاشیه‌گذاران حذف شده است.{Environment.NewLine}" + $"این متن حاشیهٔ حذف شدهٔ شماست: {Environment.NewLine}" + $"{replies[i].HtmlComment}" ); } _context.GanjoorComments.Remove(replies[i]); } _context.GanjoorComments.Remove(comment); await _context.SaveChangesAsync(); return new RServiceResult(true); } catch (Exception exp) { return new RServiceResult(false, exp.ToString()); } } private async Task> _FindReplies(GanjoorComment comment) { List replies = await _context.GanjoorComments.Where(c => c.InReplyToId == comment.Id).ToListAsync(); List replyToReplies = new List(); foreach (GanjoorComment reply in replies) { replyToReplies.AddRange(await _FindReplies(reply)); } if(replyToReplies.Count > 0) { replies.AddRange(replyToReplies); } return replies; } /// /// get recent comments /// /// /// /// /// public async Task> GetRecentComments(PagingParameterModel paging, Guid filterUserId, bool onlyPublished) { try { var source = from comment in _context.GanjoorComments.Include(c => c.Poem).Include(c => c.User).Include(c => c.InReplyTo).ThenInclude(r => r.User) where ((comment.Status == PublishStatus.Published) || !onlyPublished) && ((filterUserId == Guid.Empty) || (filterUserId != Guid.Empty && comment.UserId == filterUserId)) orderby comment.CommentDate descending select new GanjoorCommentFullViewModel() { Id = comment.Id, AuthorName = comment.User == null ? comment.AuthorName : $"{comment.User.NickName}", AuthorUrl = comment.AuthorUrl, CommentDate = comment.CommentDate, HtmlComment = comment.HtmlComment, InReplayTo = comment.InReplyTo == null ? null : new GanjoorCommentSummaryViewModel() { Id = comment.InReplyTo.Id, AuthorName = comment.InReplyTo.User == null ? comment.InReplyTo.AuthorName : $"{comment.InReplyTo.User.NickName}", AuthorUrl = comment.InReplyTo.AuthorUrl, CommentDate = comment.InReplyTo.CommentDate, HtmlComment = comment.InReplyTo.HtmlComment, PublishStatus = "" }, Poem = new GanjoorPoemSummaryViewModel() { Id = comment.Poem.Id, Title = comment.Poem.FullTitle, UrlSlug = comment.Poem.FullUrl, Excerpt = "" } }; (PaginationMetadata PagingMeta, GanjoorCommentFullViewModel[] Items) paginatedResult = await QueryablePaginator.Paginate(source, paging); foreach (GanjoorCommentFullViewModel comment in paginatedResult.Items) { comment.AuthorName = comment.AuthorName.ToPersianNumbers().ApplyCorrectYeKe(); } return new RServiceResult<(PaginationMetadata PagingMeta, GanjoorCommentFullViewModel[] Items)>(paginatedResult); } catch (Exception exp) { return new RServiceResult<(PaginationMetadata PagingMeta, GanjoorCommentFullViewModel[] Items)>((PagingMeta: null, Items: null), exp.ToString()); } } /// /// report a comment /// /// /// /// id of report record public async Task> ReportComment(Guid userId, GanjoorPostReportCommentViewModel report) { try { GanjoorCommentAbuseReport r = new GanjoorCommentAbuseReport() { GanjoorCommentId = report.CommentId, ReportedById = userId, ReasonCode = report.ReasonCode, ReasonText = report.ReasonText, }; _context.GanjoorReportedComments.Add(r); await _context.SaveChangesAsync(); return new RServiceResult(r.GanjoorCommentId); } catch (Exception exp) { return new RServiceResult(0, exp.ToString()); } } /// /// delete a report /// /// /// public async Task> DeleteReport(int id) { try { GanjoorCommentAbuseReport report = await _context.GanjoorReportedComments.Where(r => r.Id == id).SingleOrDefaultAsync(); if(report == null) { return new RServiceResult(false); } _context.GanjoorReportedComments.Remove(report); await _context.SaveChangesAsync(); return new RServiceResult(true); } catch (Exception exp) { return new RServiceResult(false, exp.ToString()); } } /// /// Get list of reported comments /// /// /// public async Task> GetReportedComments(PagingParameterModel paging) { try { var source = from report in _context.GanjoorReportedComments join comment in _context.GanjoorComments.Include(c => c.Poem).Include(c => c.User).Include(c => c.InReplyTo).ThenInclude(r => r.User) on report.GanjoorCommentId equals comment.Id orderby report.Id descending select new GanjoorCommentAbuseReportViewModel() { Id = report.Id, ReasonCode = report.ReasonCode, ReasonText = report.ReasonText, Comment = new GanjoorCommentFullViewModel() { Id = comment.Id, AuthorName = comment.User == null ? comment.AuthorName : $"{comment.User.NickName}", AuthorUrl = comment.AuthorUrl, CommentDate = comment.CommentDate, HtmlComment = comment.HtmlComment, InReplayTo = comment.InReplyTo == null ? null : new GanjoorCommentSummaryViewModel() { Id = comment.InReplyTo.Id, AuthorName = comment.InReplyTo.User == null ? comment.InReplyTo.AuthorName : $"{comment.InReplyTo.User.NickName}", AuthorUrl = comment.InReplyTo.AuthorUrl, CommentDate = comment.InReplyTo.CommentDate, HtmlComment = comment.InReplyTo.HtmlComment, PublishStatus = "" }, Poem = new GanjoorPoemSummaryViewModel() { Id = comment.Poem.Id, Title = comment.Poem.FullTitle, UrlSlug = comment.Poem.FullUrl, Excerpt = "" } } }; (PaginationMetadata PagingMeta, GanjoorCommentAbuseReportViewModel[] Items) paginatedResult = await QueryablePaginator.Paginate(source, paging); foreach (GanjoorCommentAbuseReportViewModel report in paginatedResult.Items) { report.Comment.AuthorName = report.Comment.AuthorName.ToPersianNumbers().ApplyCorrectYeKe(); } return new RServiceResult<(PaginationMetadata PagingMeta, GanjoorCommentAbuseReportViewModel[] Items)>(paginatedResult); } catch (Exception exp) { return new RServiceResult<(PaginationMetadata PagingMeta, GanjoorCommentAbuseReportViewModel[] Items)>((PagingMeta: null, Items: null), exp.ToString()); } } /// /// get poem images by id (some fields are intentionally field with blank or null), /// EntityImageId : the most important data field, image url is https://ganjgah.ir/api/images/thumb/{EntityImageId}.jpg or https://ganjgah.ir/api/images/norm/{EntityImageId}.jpg /// /// /// public async Task> GetPoemImages(int id) { try { var museumSrc = from link in _context.GanjoorLinks.Include(l => l.Artifact).Include(l => l.Item).ThenInclude(i => i.Images) join poem in _context.GanjoorPoems on link.GanjoorPostId equals poem.Id where link.DisplayOnPage == true && link.ReviewResult == Models.GanjoorIntegration.ReviewResult.Approved && poem.Id == id orderby link.ReviewDate select new PoemRelatedImage() { PoemRelatedImageType = PoemRelatedImageType.MuseumLink, ThumbnailImageUrl = $"https://ganjgah.ir/api/images/thumb/{link.Item.Images.First().Id}.jpg", TargetPageUrl = $"https://museum.ganjoor.net/items/{link.Artifact.FriendlyUrl}/{link.Item.FriendlyUrl}", AltText = $"{link.Artifact.Name} » {link.Item.Name}", }; List museumImages = await museumSrc.ToListAsync(); var externalSrc = from link in _context.PinterestLinks join poem in _context.GanjoorPoems on link.GanjoorPostId equals poem.Id where link.ReviewResult == Models.GanjoorIntegration.ReviewResult.Approved && poem.Id == id orderby link.ReviewDate select new PoemRelatedImage() { PoemRelatedImageType = PoemRelatedImageType.ExternalLink, ThumbnailImageUrl = $"https://ganjgah.ir/api/images/thumb/{link.Item.Images.First().Id}.jpg", TargetPageUrl = link.PinterestUrl, AltText = link.AltText, }; museumImages.AddRange(await externalSrc.ToListAsync()); for (int i = 0; i < museumImages.Count; i++) { museumImages[i].ImageOrder = 0; } return new RServiceResult(museumImages.ToArray()); } catch (Exception exp) { return new RServiceResult(null, exp.ToString()); } } private int _GetRandomPoemId() { //this is magic number based method! int startPoemId = 2130; int endPoemId = 2624 + 1; //one is added for مژده ای دل که مسیحا نفسی می‌آید Random r = new Random(DateTime.Now.Millisecond); int poemId = r.Next(startPoemId, endPoemId); if (poemId == endPoemId) { poemId = 33179;//مژده ای دل که مسیحا نفسی می‌آید } return poemId; } /// /// Get Poem By Url /// /// /// /// /// /// /// /// /// /// /// /// public async Task> GetPoemByUrl(string url, bool catInfo = true, bool catPoems = false, bool rhymes = true, bool recitations = true, bool images = true, bool songs = true, bool comments = true, bool verseDetails = true, bool navigation = true) { try { // /hafez/ => /hafez : if (url.LastIndexOf('/') == url.Length - 1) { url = url.Substring(0, url.Length - 1); } var poem = await _context.GanjoorPoems.Where(p => p.FullUrl == url).SingleOrDefaultAsync(); if (poem == null) { return new RServiceResult(null); //not found } return await GetPoemById(poem.Id, catInfo, catPoems, rhymes, recitations, images, songs, comments, verseDetails, navigation); } catch (Exception exp) { return new RServiceResult(null, exp.ToString()); } } /// /// Get Poem By Id /// /// /// /// /// /// /// /// /// /// /// /// public async Task> GetPoemById(int id, bool catInfo = true, bool catPoems = false, bool rhymes = true, bool recitations = true, bool images = true, bool songs = true, bool comments = true, bool verseDetails = true, bool navigation = true) { try { var poem = await _context.GanjoorPoems.Include(p => p.GanjoorMetre).Where(p => p.Id == id).SingleOrDefaultAsync(); if (poem == null) { return new RServiceResult(null); //not found } GanjoorPoetCompleteViewModel cat = null; if (catInfo) { var catRes = await GetCatById(poem.CatId, catPoems); if (!string.IsNullOrEmpty(catRes.ExceptionString)) { return new RServiceResult(null, catRes.ExceptionString); } cat = catRes.Result; } GanjoorPoemSummaryViewModel next = null; if (navigation) { int nextId = await _context.GanjoorPoems .Where(p => p.CatId == poem.CatId && p.Id > poem.Id) .AnyAsync() ? await _context.GanjoorPoems .Where(p => p.CatId == poem.CatId && p.Id > poem.Id) .MinAsync(p => p.Id) : 0; if (nextId != 0) { next = await _context.GanjoorPoems.Where(p => p.Id == nextId).Select ( p => new GanjoorPoemSummaryViewModel() { Id = p.Id, Title = p.Title, UrlSlug = p.UrlSlug, Excerpt = _context.GanjoorVerses.Where(v => v.PoemId == p.Id && v.VOrder == 1).FirstOrDefault().Text } ).SingleAsync(); } } GanjoorPoemSummaryViewModel previous = null; if (navigation) { int preId = await _context.GanjoorPoems .Where(p => p.CatId == poem.CatId && p.Id < poem.Id) .AnyAsync() ? await _context.GanjoorPoems .Where(p => p.CatId == poem.CatId && p.Id < poem.Id) .MaxAsync(p => p.Id) : 0; if (preId != 0) { previous = await _context.GanjoorPoems.Where(p => p.Id == preId).Select ( p => new GanjoorPoemSummaryViewModel() { Id = p.Id, Title = p.Title, UrlSlug = p.UrlSlug, Excerpt = _context.GanjoorVerses.Where(v => v.PoemId == p.Id && v.VOrder == 1).FirstOrDefault().Text } ).SingleAsync(); } } PublicRecitationViewModel[] rc = null; if (recitations) { var rcRes = await GetPoemRecitations(id); if (!string.IsNullOrEmpty(rcRes.ExceptionString)) return new RServiceResult(null, rcRes.ExceptionString); rc = rcRes.Result; } PoemRelatedImage[] imgs = null; if (images) { var imgsRes = await GetPoemImages(id); if (!string.IsNullOrEmpty(imgsRes.ExceptionString)) return new RServiceResult(null, imgsRes.ExceptionString); imgs = imgsRes.Result; } GanjoorVerseViewModel[] verses = null; if (verseDetails) { verses = await _context.GanjoorVerses .Where(v => v.PoemId == id) .OrderBy(v => v.VOrder) .Select ( v => new GanjoorVerseViewModel() { Id = v.Id, VOrder = v.VOrder, VersePosition = v.VersePosition, Text = v.Text } ).ToArrayAsync(); }; PoemMusicTrackViewModel[] tracks = null; if (songs) { var songsRes = await GetPoemSongs(id, true, PoemMusicTrackType.All); if (!string.IsNullOrEmpty(songsRes.ExceptionString)) return new RServiceResult(null, songsRes.ExceptionString); tracks = songsRes.Result; } GanjoorCommentSummaryViewModel[] poemComments = null; if (comments) { var commentsRes = await GetPoemComments(id, Guid.Empty); if (!string.IsNullOrEmpty(commentsRes.ExceptionString)) return new RServiceResult(null, commentsRes.ExceptionString); poemComments = commentsRes.Result; } return new RServiceResult ( new GanjoorPoemCompleteViewModel() { Id = poem.Id, Title = poem.Title, FullTitle = poem.FullTitle, FullUrl = poem.FullUrl, UrlSlug = poem.UrlSlug, HtmlText = poem.HtmlText, PlainText = poem.PlainText, GanjoorMetre = poem.GanjoorMetre, RhymeLetters = poem.RhymeLetters, SourceName = poem.SourceName, SourceUrlSlug = poem.SourceUrlSlug, OldTag = poem.OldTag, OldTagPageUrl = poem.OldTagPageUrl, Category = cat, Next = next, Previous = previous, Recitations = rc, Images = imgs, Verses = verses, Songs = tracks, Comments = poemComments } ); } catch (Exception exp) { return new RServiceResult(null, exp.ToString()); } } /// /// get poem related songs /// /// /// /// /// public async Task> GetPoemSongs(int id, bool approved, PoemMusicTrackType trackType) { try { return new RServiceResult ( await _context.GanjoorPoemMusicTracks .Where ( t => t.PoemId == id && t.Approved == approved && t.Rejected == false && (trackType == PoemMusicTrackType.All || t.TrackType == trackType) ) .OrderBy(t => t.SongOrder) .Select ( t => new PoemMusicTrackViewModel() { Id = t.Id, PoemId = t.PoemId, TrackType = t.TrackType, ArtistName = t.ArtistName, ArtistUrl = t.ArtistUrl, AlbumName = t.AlbumName, AlbumUrl = t.AlbumUrl, TrackName = t.TrackName, TrackUrl = t.TrackUrl, Description = t.Description, BrokenLink = t.BrokenLink, GolhaTrackId = t.GolhaTrackId == null ? 0 : (int)t.GolhaTrackId, Approved = t.Approved, Rejected = t.Rejected, RejectionCause = t.RejectionCause } ).ToArrayAsync() ); } catch (Exception exp) { return new RServiceResult(null, exp.ToString()); } } /// /// suggest song /// /// /// /// public async Task> SuggestSong(Guid userId, PoemMusicTrackViewModel song) { try { song.Approved = false; song.Rejected = false; song.RejectionCause = ""; song.BrokenLink = false; if (song.TrackType == PoemMusicTrackType.Golha) { var golhaTrack = await _context.GolhaTracks.Include(g => g.GolhaProgram).ThenInclude(p => p.GolhaCollection).Where(g => g.Id == song.GolhaTrackId).FirstOrDefaultAsync(); if (golhaTrack == null) { return new RServiceResult(null, "مشخصات قطعهٔ گلها درست نیست."); } var alreadySuggestedSong = await _context.GanjoorPoemMusicTracks.Where(t => t.PoemId == song.PoemId && t.TrackType == song.TrackType && t.GolhaTrackId == song.GolhaTrackId).FirstOrDefaultAsync(); if (alreadySuggestedSong != null) { return new RServiceResult(null, "این آهنگ پیشتر برای این شعر پیشنهاد داده شده است."); } song.ArtistName = ""; song.ArtistUrl = ""; song.AlbumName = $"{golhaTrack.GolhaProgram.GolhaCollection.Name} » شمارهٔ {golhaTrack.GolhaProgram.Title.ToPersianNumbers().ApplyCorrectYeKe()}"; song.AlbumUrl = ""; song.TrackName = $"{golhaTrack.Timing.ToPersianNumbers().ApplyCorrectYeKe()} {golhaTrack.Title}"; song.TrackUrl = golhaTrack.GolhaProgram.Url; } else { var alreadySuggestedSong = await _context.GanjoorPoemMusicTracks.Where(t => t.PoemId == song.PoemId && t.TrackType == song.TrackType && t.TrackUrl == song.TrackUrl).FirstOrDefaultAsync(); if (alreadySuggestedSong != null) { return new RServiceResult(null, "این آهنگ پیشتر برای این شعر پیشنهاد داده شده است."); } } var sug = new PoemMusicTrack() { TrackType = song.TrackType, PoemId = song.PoemId, ArtistName = song.ArtistName, ArtistUrl = song.ArtistUrl, AlbumName = song.AlbumName, AlbumUrl = song.AlbumUrl, TrackName = song.TrackName, TrackUrl = song.TrackUrl, SuggestedById = userId, Description = song.Description, GolhaTrackId = song.TrackType == PoemMusicTrackType.Golha ? song.GolhaTrackId : (int?)null, Approved = false, Rejected = false, RejectionCause = "" }; GanjoorSinger singer = await _context.GanjoorSingers.Where(s => s.Url == song.ArtistUrl).FirstOrDefaultAsync(); if (singer != null) { sug.SingerId = singer.Id; } _context.GanjoorPoemMusicTracks.Add ( sug ); await _context.SaveChangesAsync(); sug.SongOrder = sug.Id; _context.GanjoorPoemMusicTracks.Update(sug); await _context.SaveChangesAsync(); song.Id = sug.Id; return new RServiceResult(song); } catch (Exception exp) { return new RServiceResult(null, exp.ToString()); } } /// /// get unreviewed count /// /// /// public async Task> GetUnreviewedSongsCount(Guid suggestedById) { try { return new RServiceResult(await _context.GanjoorPoemMusicTracks .Where(p => p.Approved == false && p.Rejected == false && (suggestedById == Guid.Empty || p.SuggestedById == suggestedById)) .CountAsync()); } catch (Exception exp) { return new RServiceResult(0, exp.ToString()); } } /// /// next unreviewed track /// /// /// /// public async Task> GetNextUnreviewedSong(int skip, Guid suggestedById) { try { var song = await _context.GanjoorPoemMusicTracks .Where(p => p.Approved == false && p.Rejected == false && (suggestedById == Guid.Empty || p.SuggestedById == suggestedById)) .OrderBy(p => p.Id).Skip(skip).FirstOrDefaultAsync(); if (song != null) { return new RServiceResult ( new PoemMusicTrackViewModel() { Id = song.Id, TrackType = song.TrackType, PoemId = song.PoemId, ArtistName = song.ArtistName, ArtistUrl = song.ArtistUrl, AlbumName = song.AlbumName, AlbumUrl = song.AlbumUrl, TrackName = song.TrackName, TrackUrl = song.TrackUrl, Description = song.Description, GolhaTrackId = song.TrackType == PoemMusicTrackType.Golha ? (int)song.GolhaTrackId : 0, BrokenLink = song.BrokenLink, Approved = song.Approved, Rejected = song.Rejected, RejectionCause = song.RejectionCause } ); } return new RServiceResult(null); //not found } catch (Exception exp) { return new RServiceResult(null, exp.ToString()); } } /// /// review song /// /// /// public async Task> ReviewSong(PoemMusicTrackViewModel song) { try { if (song.Approved && song.Rejected) return new RServiceResult(null, "song.Approved && song.Rejected"); var track = await _context.GanjoorPoemMusicTracks.Where(t => t.Id == song.Id).SingleOrDefaultAsync(); track.TrackType = song.TrackType; track.ArtistName = song.ArtistName; track.ArtistUrl = song.ArtistUrl; track.AlbumName = song.AlbumName; track.AlbumUrl = song.AlbumUrl; track.TrackName = song.TrackName; track.TrackUrl = song.TrackUrl; if (!track.Approved && song.Approved) { track.ApprovalDate = DateTime.Now; } track.Approved = song.Approved; track.Rejected = song.Rejected; track.RejectionCause = song.RejectionCause; track.BrokenLink = song.BrokenLink; GanjoorSinger singer = await _context.GanjoorSingers.Where(s => s.Url == track.ArtistUrl).FirstOrDefaultAsync(); if (singer != null) { track.SingerId = singer.Id; } _context.GanjoorPoemMusicTracks.Update(track); await _context.SaveChangesAsync(); return new RServiceResult(song); } catch (Exception exp) { return new RServiceResult(null, exp.ToString()); } } /// /// direct insert song /// /// /// public async Task> DirectInsertSong(PoemMusicTrackViewModel song) { try { var poem = await _context.GanjoorPoems.Where(p => p.Id == song.PoemId).SingleOrDefaultAsync(); if (poem == null) return new RServiceResult(null, "poem == null"); if ( string.IsNullOrEmpty(song.ArtistName) || string.IsNullOrEmpty(song.ArtistUrl) || string.IsNullOrEmpty(song.AlbumName) || string.IsNullOrEmpty(song.AlbumUrl) || string.IsNullOrEmpty(song.TrackName) || string.IsNullOrEmpty(song.TrackUrl) || song.TrackType != PoemMusicTrackType.BeepTunesOrKhosousi ) { return new RServiceResult(null, "data validation err"); } var duplicated = await _context.GanjoorPoemMusicTracks.Where(m => m.PoemId == song.PoemId && m.TrackUrl == song.TrackUrl).FirstOrDefaultAsync(); if (duplicated != null) { return new RServiceResult(null, "duplicated song url for this poem"); } PoemMusicTrack track = new PoemMusicTrack(); track.PoemId = song.PoemId; track.TrackType = song.TrackType; track.ArtistName = song.ArtistName; track.ArtistUrl = song.ArtistUrl; track.AlbumName = song.AlbumName; track.AlbumUrl = song.AlbumUrl; track.TrackName = song.TrackName; track.TrackUrl = song.TrackUrl; track.ApprovalDate = DateTime.Now; track.Approved = true; track.Rejected = false; track.BrokenLink = song.BrokenLink; GanjoorSinger singer = await _context.GanjoorSingers.Where(s => s.Url == track.ArtistUrl).FirstOrDefaultAsync(); if (singer != null) { track.SingerId = singer.Id; } _context.GanjoorPoemMusicTracks.Add(track); await _context.SaveChangesAsync(); track.SongOrder = track.Id; song.Id = track.Id; _context.GanjoorPoemMusicTracks.Update(track); await _context.SaveChangesAsync(); return new RServiceResult(song); } catch (Exception exp) { return new RServiceResult(null, exp.ToString()); } } /// /// get a random poem from hafez /// /// public async Task> Faal() { try { int poemId = _GetRandomPoemId(); var poem = await _context.GanjoorPoems.Where(p => p.Id == poemId).SingleOrDefaultAsync(); PublicRecitationViewModel[] recitations = poem == null ? new PublicRecitationViewModel[] { } : (await GetPoemRecitations(poemId)).Result; int loopPreventer = 0; while (poem == null || recitations.Length == 0) { poem = await _context.GanjoorPoems.Where(p => p.Id == poemId).SingleOrDefaultAsync(); recitations = poem == null ? new PublicRecitationViewModel[] { } : (await GetPoemRecitations(poemId)).Result; loopPreventer++; if (loopPreventer > 5) break; } return await GetPoemById(poemId, false, false, false, true, false, false, false, false, false); } catch (Exception exp) { return new RServiceResult(null, exp.ToString()); } } public async Task> GetVersesByQuery(string query, int poetId, PagingParameterModel paging) { try { var versesQ = _context.GanjoorVerses.AsNoTracking().Include(v => v.Poem).ThenInclude(p => p.Cat) .Where(v => v.Text.Contains(query)); if (poetId != 0) { versesQ = versesQ.Where(v => v.Poem.Cat.PoetId == poetId); } var source = versesQ.Select( v => new GanjoorSearchVerseViewModel { Id = v.Id, VOrder = v.VOrder, VersePosition = v.VersePosition, Text = v.Text, PoemId = v.PoemId, PoetId = v.Poem.Cat.PoetId, PoemFullTitle = v.Poem.FullTitle, PoemFullUrl = v.Poem.FullUrl, } ); (PaginationMetadata PagingMeta, GanjoorSearchVerseViewModel[] Items) paginatedResult = await QueryablePaginator.Paginate(source, paging); return new RServiceResult<(PaginationMetadata PagingMeta, GanjoorSearchVerseViewModel[])>(paginatedResult); } catch (Exception exp) { return new RServiceResult<(PaginationMetadata PagingMeta, GanjoorSearchVerseViewModel[] Items)>((PagingMeta: null, Items: null), exp.ToString()); } } /// /// Get Similar Poems accroding to prosody and rhyme informations /// /// /// /// /// /// public async Task> GetSimilarPoems(PagingParameterModel paging, string metre, string rhyme, int? poetId) { try { if (string.IsNullOrEmpty(rhyme)) rhyme = ""; var source = _context.GanjoorPoems.Include(p => p.Cat).ThenInclude(c => c.Poet).Include(p => p.GanjoorMetre) .Where(p => (poetId == null || p.Cat.PoetId == poetId) && (p.GanjoorMetre.Rhythm == metre) && (rhyme == "" || p.RhymeLetters == rhyme) ) .OrderBy(p => p.CatId).ThenBy(p => p.Id) .Select ( poem => new GanjoorPoemCompleteViewModel() { Id = poem.Id, Title = poem.Title, FullTitle = poem.FullTitle, FullUrl = poem.FullUrl, UrlSlug = poem.UrlSlug, HtmlText = poem.HtmlText, PlainText = poem.PlainText, GanjoorMetre = poem.GanjoorMetre, RhymeLetters = poem.RhymeLetters, Category = new GanjoorPoetCompleteViewModel() { Poet = new GanjoorPoetViewModel() { Id = poem.Cat.Poet.Id, } }, } ); (PaginationMetadata PagingMeta, GanjoorPoemCompleteViewModel[] Items) paginatedResult = await QueryablePaginator.Paginate(source, paging); Dictionary cachedPoets = new Dictionary(); foreach (var item in paginatedResult.Items) { if(cachedPoets.TryGetValue(item.Category.Poet.Id, out GanjoorPoetCompleteViewModel poet)) { item.Category = poet; } else { poet = (await GetPoetById(item.Category.Poet.Id)).Result; cachedPoets.Add(item.Category.Poet.Id, poet); item.Category = poet; } } return new RServiceResult<(PaginationMetadata PagingMeta, GanjoorPoemCompleteViewModel[] Items)>(paginatedResult); } catch(Exception exp) { return new RServiceResult<(PaginationMetadata PagingMeta, GanjoorPoemCompleteViewModel[] Items)>((null, null), exp.ToString()); } } /// /// Database Context /// protected readonly RMuseumDbContext _context; /// /// Configuration /// protected IConfiguration Configuration { get; } /// /// Background Task Queue Instance /// protected readonly IBackgroundTaskQueue _backgroundTaskQueue; /// /// IAppUserService instance /// protected IAppUserService _appUserService; /// /// Messaging service /// protected readonly IRNotificationService _notificationService; /// /// constructor /// /// /// /// /// /// public GanjoorService(RMuseumDbContext context, IConfiguration configuration, IBackgroundTaskQueue backgroundTaskQueue, IAppUserService appUserService, IRNotificationService notificationService) { _context = context; _backgroundTaskQueue = backgroundTaskQueue; _appUserService = appUserService; _notificationService = notificationService; Configuration = configuration; } } }