using Audit.WebApi; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration; using Newtonsoft.Json; using RMuseum.Models.Auth.Memory; using RMuseum.Models.Auth.ViewModel; using RMuseum.Models.Ganjoor; using RMuseum.Models.Ganjoor.PublicExport; using RMuseum.Models.Ganjoor.ViewModels; using RMuseum.Models.GanjoorAudio.ViewModels; using RMuseum.Models.GanjoorIntegration; using RMuseum.Services; using RMuseum.Services.Implementation; using RSecurityBackend.Models.Auth.Memory; using RSecurityBackend.Models.Auth.ViewModels; using RSecurityBackend.Models.Generic; using RSecurityBackend.Models.Image; using RSecurityBackend.Services; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Threading.Tasks; namespace RMuseum.Controllers { [Produces("application/json")] [Route("api/ganjoor")] public class GanjoorController : Controller { /// /// get list of published poets without their biography /// /// [HttpGet] [Route("poets")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPoetViewModel[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetPoets() { var cacheKey = $"ganjoor/poets"; if (!_memoryCache.TryGetValue(cacheKey, out GanjoorPoetViewModel[] poets)) { RServiceResult res = await _ganjoorService.GetPoets(true, false); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); poets = res.Result; if (AggressiveCacheEnabled) _memoryCache.Set(cacheKey, poets, TimeSpan.FromHours(1)); } return Ok(poets); } /// /// get list of books (GanjoorCat entries whose CatType is Book, sorted alphabetically by name), /// optionally filtered by (part of) name and/or poet id. Not to be confused with /// GetBooksAsync()/"books" above, which lists GanjoorCat entries by their (separate, /// legacy) BookName field for cover-image generation. /// /// optional, only books whose name contains this /// optional, only books belonging to this poet /// [HttpGet] [Route("book-catalog")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorBookViewModel[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetBookCatalog(string name = null, int? poetId = null) { // only the common, unfiltered call (the one the home page's book search will use to load // the full list once and filter client-side) is worth caching - a filtered call is cheap // on a table this small (well under 200 rows) and caching every name/poetId combination // would just grow the cache for no benefit. if (string.IsNullOrEmpty(name) && poetId == null) { var cacheKey = $"ganjoor/book-catalog"; if (!_memoryCache.TryGetValue(cacheKey, out GanjoorBookViewModel[] cachedBooks)) { RServiceResult cachedRes = await _ganjoorService.GetBookCatalogAsync(); if (!string.IsNullOrEmpty(cachedRes.ExceptionString)) return BadRequest(cachedRes.ExceptionString); cachedBooks = cachedRes.Result; if (AggressiveCacheEnabled) _memoryCache.Set(cacheKey, cachedBooks, TimeSpan.FromHours(1)); } return Ok(cachedBooks); } RServiceResult res = await _ganjoorService.GetBookCatalogAsync(name, poetId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// gets list of poets grouped by centuries (first one is the pinned ones) /// /// [HttpGet] [Route("centuries")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorCenturyViewModel[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetCenturiesAsync() { var cacheKey = $"ganjoor/centuries"; if (!_memoryCache.TryGetValue(cacheKey, out GanjoorCenturyViewModel[] centuries)) { RServiceResult res = await _ganjoorService.GetCenturiesAsync(); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); centuries = res.Result; if (AggressiveCacheEnabled) _memoryCache.Set(cacheKey, centuries, TimeSpan.FromHours(1)); } return Ok(centuries); } /// /// get list of all poets (including unpublished ones) with their bio /// /// [HttpGet] [Route("poets/secure")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPoetViewModel[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetAllPoets() { RServiceResult res = await _ganjoorService.GetPoets(false, true); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// poet by id /// /// /// /// [HttpGet] [Route("poet/{id}")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPoetCompleteViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetPoetById(int id, bool catPoems = false) { var cacheKey = $"poet/byid/{id}"; if (!_memoryCache.TryGetValue(cacheKey, out GanjoorPoetCompleteViewModel poet)) { RServiceResult res = await _ganjoorService.GetPoetById(id, catPoems); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (res.Result == null) return NotFound(); poet = res.Result; if (AggressiveCacheEnabled) _memoryCache.Set(cacheKey, poet, TimeSpan.FromHours(1)); } return Ok(poet); } /// /// poet by url /// /// /// [HttpGet] [Route("poet")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPoetCompleteViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetPoetByUrl(string url) { var cacheKey = $"poet/byurl/{url}"; if (!_memoryCache.TryGetValue(cacheKey, out GanjoorPoetCompleteViewModel poet)) { RServiceResult res = await _ganjoorService.GetPoetByUrl(url); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (res.Result == null) return NotFound(); poet = res.Result; if (AggressiveCacheEnabled) _memoryCache.Set(cacheKey, poet, TimeSpan.FromHours(1)); } return Ok(poet); } /// /// update poet info (except for image) /// /// /// /// [HttpPut] [Route("poet/{id}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task ModifyPoet(int id, [FromBody] GanjoorPoetViewModel poet) { if (!string.IsNullOrEmpty(poet.ImageUrl)) { return BadRequest("Please send an empty image url, if you are trying to change poet image this is not the right method to do it."); } Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); poet.Id = id; var res = await _ganjoorService.UpdatePoetAsync(poet, userId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// regenerate half centuries /// /// [HttpPost] [Route("periods")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPoetCompleteViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task RegenerateHalfCenturiesAsync() { var res = await _ganjoorService.RegenerateHalfCenturiesAsync(); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); _memoryCache.Remove($"ganjoor/centuries"); return Ok(res.Result); } /// /// create new poet /// /// /// [HttpPost] [Route("poet")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPoetCompleteViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task CreatePoet([FromBody] GanjoorPoetViewModel poet) { if (!string.IsNullOrEmpty(poet.ImageUrl)) { return BadRequest("Please send an empty image url, if you are trying to change poet image this is not the right method to do it."); } Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); var res = await _ganjoorService.AddPoetAsync(poet, userId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// starts deleting poet job /// ` /// /// [HttpDelete("poet/{id}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public IActionResult StartDeletePoet(int id) { var res = _ganjoorService.StartDeletePoet(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// get poet image with png ext /// /// sample: hafez /// [HttpGet("poet/image/{url}.png")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(FileStreamResult))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetPoetImageInPng(string url) { return await GetPoetImage(url.Replace(".png", ".gif")); } /// /// get poet image /// /// sample: hafez /// /// [HttpGet("poet/image/{url}.gif")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(FileStreamResult))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetPoetImage(string url, bool nocache = false) { var cacheKey = $"poet/image/{url}.gif"; var cacheKeyForLastModified = $"{cacheKey}/lastModified"; if (nocache && _memoryCache.TryGetValue(cacheKey, out string oldImagePath)) { _memoryCache.Remove(cacheKey); } if (!_memoryCache.TryGetValue(cacheKey, out string imagePath) || !_memoryCache.TryGetValue(cacheKeyForLastModified, out DateTime lastModified)) { RServiceResult poet = await _ganjoorService.GetPoetImageIdByUrl($"/{url}"); if (!string.IsNullOrEmpty(poet.ExceptionString)) return BadRequest(poet.ExceptionString); if (poet.Result == Guid.Empty) return NotFound(); RServiceResult img = await _imageFileService.GetImage(poet.Result); if (!string.IsNullOrEmpty(img.ExceptionString)) return BadRequest(img.ExceptionString); if (img.Result == null) return NotFound(); lastModified = img.Result.LastModified; lastModified = new DateTime(lastModified.Year, lastModified.Month, lastModified.Day, lastModified.Hour, lastModified.Minute, lastModified.Second, lastModified.Kind); RServiceResult imgPath = _imageFileService.GetImagePath(img.Result); if (!string.IsNullOrEmpty(imgPath.ExceptionString)) return BadRequest(imgPath.ExceptionString); imagePath = imgPath.Result; _memoryCache.Set(cacheKey, imagePath, TimeSpan.FromHours(1)); _memoryCache.Set(cacheKeyForLastModified, lastModified, TimeSpan.FromHours(1)); } Response.GetTypedHeaders().LastModified = lastModified; Response.Headers.CacheControl = nocache ? "no-cache" : "public,max-age=86400"; var requestHeaders = Request.GetTypedHeaders(); if (requestHeaders.IfModifiedSince.HasValue && requestHeaders.IfModifiedSince.Value >= lastModified) { return StatusCode(StatusCodes.Status304NotModified); } return new FileStreamResult(new FileStream(imagePath, FileMode.Open, FileAccess.Read), "image/gif"); } /// /// set poet image /// /// poet image /// [HttpPost("poet/image/{id}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Unauthorized)] [ProducesResponseType((int)HttpStatusCode.Forbidden, Type = typeof(string))] public async Task UploadPoetImage(int id) { try { var poet = await _ganjoorService.GetPoetById(id, false); IFormFile file = Request.Form.Files[0]; RServiceResult image = await _imageFileService.Add(file, null, file.FileName, Path.Combine(Configuration.GetSection("PictureFileService")["StoragePath"], "PoetImages")); if (!string.IsNullOrEmpty(image.ExceptionString)) { return BadRequest(image.ExceptionString); } image = await _imageFileService.Store(image.Result); if (!string.IsNullOrEmpty(image.ExceptionString)) { return BadRequest(image.ExceptionString); } var res = await _ganjoorService.ChangePoetImageAsync(id, image.Result.Id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (res.Result) { var cacheKey = $"poet/image/{poet.Result.Cat.UrlSlug}.gif"; var cacheKeyForLastModified = $"{cacheKey}/lastModified"; if (_memoryCache.TryGetValue(cacheKey, out string imagePath)) { _memoryCache.Remove(cacheKey); } if (_memoryCache.TryGetValue(cacheKeyForLastModified, out DateTime cachedLastModified)) { _memoryCache.Remove(cacheKeyForLastModified); } } return Ok(res.Result); } catch (Exception exp) { return BadRequest(exp.ToString()); } } /// /// cat by id /// /// /// /// /// [HttpGet] [Route("cat/{id}")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPoetCompleteViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetCatById(int id, bool poems = true, bool mainSections = false) { var cacheKey = $"cat/byid/{id}/{poems}"; if (!_memoryCache.TryGetValue(cacheKey, out GanjoorPoetCompleteViewModel cat)) { RServiceResult res = await _ganjoorService.GetCatById(id, poems, mainSections); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (res.Result == null) return NotFound(); cat = res.Result; if (AggressiveCacheEnabled) { _memoryCache.Set(cacheKey, cat, TimeSpan.FromHours(1)); } } return Ok(cat); } /// /// cat by full url /// /// /// /// /// [HttpGet] [Route("cat")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPoetCompleteViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetCatByUrl(string url, bool poems = true, bool mainSections = false) { var cacheKey = $"cat/byurl/{url}/{poems}"; if (!_memoryCache.TryGetValue(cacheKey, out GanjoorPoetCompleteViewModel cat)) { RServiceResult res = await _ganjoorService.GetCatByUrl(url, poems, mainSections); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (res.Result == null) return NotFound(); cat = res.Result; if (AggressiveCacheEnabled) _memoryCache.Set(cacheKey, cat, TimeSpan.FromHours(1)); } return Ok(cat); } /// /// set category extra info /// /// /// only RImageId field is valid [HttpPut("cat/extra/{id}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorCatViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Unauthorized)] [ProducesResponseType((int)HttpStatusCode.NotFound)] [ProducesResponseType((int)HttpStatusCode.Forbidden, Type = typeof(string))] public async Task SetCategoryExtraInfo(int id) { try { Guid? imageId = null; if (Request.Form.Files.Count > 0) { IFormFile file = Request.Form.Files[0]; RServiceResult image = await _imageFileService.Add(file, null, file.FileName, Path.Combine(Configuration.GetSection("PictureFileService")["StoragePath"], "CategoryImages")); if (!string.IsNullOrEmpty(image.ExceptionString)) { return BadRequest(image.ExceptionString); } image = await _imageFileService.Store(image.Result); if (!string.IsNullOrEmpty(image.ExceptionString)) { return BadRequest(image.ExceptionString); } imageId = image.Result.Id; } var res = await _ganjoorService.SetCategoryExtraInfo(id, Request.Form["bookName"], imageId, bool.Parse(Request.Form["sumUpSubsGeoLocations"]), Request.Form["mapName"]); if (!string.IsNullOrEmpty(res.ExceptionString)) { return BadRequest(res.ExceptionString); } if (res.Result == null) { return NotFound(); } return Ok(res.Result); } catch (Exception exp) { return BadRequest(exp.ToString()); } } /// /// generate missing book covers /// /// [HttpPut("generatemissingbookcovers")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Unauthorized)] [ProducesResponseType((int)HttpStatusCode.Forbidden, Type = typeof(string))] public async Task GenerateMissingBookCoversAsync() { var res = await _ganjoorService.GenerateMissingBookCoversAsync(); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// list of books /// /// [HttpGet] [Route("books")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorCatViewModel[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetBooksAsync() { var books = await _ganjoorService.GetBooksAsync(); if (!string.IsNullOrEmpty(books.ExceptionString)) { return BadRequest(books.ExceptionString); } return Ok(books.Result); } /// /// batch rename cat poems /// /// /// /// [HttpPut] [Route("cat/recaptionpoems/{id}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPageCompleteViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task BatchRenameCatPoemTitles(int id, [FromBody] GanjoorBatchNamingModel model) { Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.BatchRenameCatPoemTitles(id, model, userId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// batch resulg category poems /// /// /// [HttpPut] [Route("cat/reslugpoems/{id}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task BatchReSlugCatPoems(int id) { RServiceResult res = await _ganjoorService.BatchReSlugCatPoems(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } [HttpPut] [Route("cat/startassigningrhymes/{id}/{retag}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult FindCategoryPoemsRhymes(int id, bool retag) { RServiceResult res = _ganjoorService.FindCategoryPoemsRhymes(id, retag); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// set category poems language tag /// /// /// /// [HttpPut] [Route("cat/language/{id}/{language}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task SetCategoryLanguageTagAsync(int id, string language) { var res = await _ganjoorService.SetCategoryLanguageTagAsync(id, language); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// set category poems language tag /// /// /// /// [HttpPut] [Route("cat/poemformat/{id}/{poemformat}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task SetCategoryPoemFormatAsync(int id, GanjoorPoemFormat? poemformat) { var res = await _ganjoorService.SetCategoryPoemFormatAsync(id, poemformat); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// fill missing poem formats /// /// [HttpPut("poemformats/fillmissing")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult StartFillingSectionsPoemFormats() { var res = _ganjoorService.StartFillingSectionsPoemFormats(); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// start assigning poem rhythms /// /// /// /// /// [HttpPut] [Route("cat/startassigningrhythms/{id}/{retag}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult FindCategoryPoemsRhythms(int id, bool retag, string rhythm = "") { RServiceResult res = _ganjoorService.FindCategoryPoemsRhythms(id, retag, rhythm); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// delete a category /// /// /// [HttpDelete] [Route("cat/{id}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPageCompleteViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task DeleteCategoryAsync(int id) { var res = await _ganjoorService.DeleteCategoryAsync(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// Start Finding Missing Rhythms /// /// /// /// [HttpPost] [Route("startfindingmissingrhythms")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task StartFindingMissingRhythms(bool onlyPoemsWithRhymes = true, int poemsNum = 1000) { string systemEmail = $"{Configuration.GetSection("Ganjoor")["SystemEmail"]}"; var systemUserId = (Guid)(await _appUserService.FindUserByEmail(systemEmail)).Result.Id; string deletedUserEmail = $"{Configuration.GetSection("Ganjoor")["DeleteUserEmail"]}"; var deletedUserId = (Guid)(await _appUserService.FindUserByEmail(deletedUserEmail)).Result.Id; RServiceResult res = _ganjoorService.StartFindingMissingRhythms(systemUserId, deletedUserId, onlyPoemsWithRhymes, poemsNum); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// generate category toc /// /// /// /// [HttpGet] [Route("cat/toc/{id}/{options}")] [Produces("text/plain")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GenerateTableOfContents(int id, GanjoorTOC options = GanjoorTOC.Analyse) { var res = await _ganjoorService.GenerateTableOfContents(new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value), id, options); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// directly insert generated TOC /// /// /// /// [HttpPut] [Route("cat/toc/{id}/{options}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task DirectInsertGeneratedTableOfContents(int id, GanjoorTOC options = GanjoorTOC.Analyse) { Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); var res = await _ganjoorService.DirectInsertGeneratedTableOfContents(id, userId, options); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// start generating sub cats TOC /// /// /// /// [HttpPut("cat/subcats/startgentoc/{id}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult StartGeneratingSubCatsTOC(int id, GanjoorTOC ganjoorTOC = GanjoorTOC.TitlesAndFirstVerse) { Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); var res = _ganjoorService.StartGeneratingSubCatsTOC(userId, id, ganjoorTOC); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// regenerate TOCs /// /// [HttpPut("cat/toc/regen")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult StartRegeneratingTOCs() { Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); var res = _ganjoorService.StartRegeneratingTOCs(userId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// page by url /// /// /// /// TopRated, Oldest, or Newest. Only applies to poem pages. /// [HttpGet] [Route("page")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPageCompleteViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetPageByUrl(string url, bool catPoems = false, GanjoorCommentSortOrder commentSortOrder = GanjoorCommentSortOrder.TopRated) { RServiceResult res = await _ganjoorService.GetPageByUrl(url, catPoems, commentSortOrder); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (res.Result == null) return NotFound(); return Ok(res.Result); } /// /// modify page /// /// /// /// [HttpPut] [Route("page/{id}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPageCompleteViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task ModifyPage(int id, [FromBody] GanjoorModifyPageViewModel page) { Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.UpdatePageAsync(id, userId, page); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (res.Result == null) return NotFound(); return Ok(res.Result); } /// /// modify poem => only these fields: NoIndex, RedirectFromFullUrl, MixedModeOrder /// /// /// /// [HttpPut] [Route("poem/adminedit/{id}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPageCompleteViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task AdminEditPoem(int id, [FromBody] GanjoorModifyPageViewModel page) { Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.UpdatePoemAsync(id, userId, page); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (res.Result == null) return NotFound(); return Ok(res.Result); } /// /// clean cache by id /// /// /// [HttpDelete] [Route("page/cache/{id}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPageCompleteViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task CacheCleanForPageById(int id) { await _ganjoorService.CacheCleanForPageById(id); return Ok(); } /// /// delete page /// /// /// [HttpDelete] [Route("page/{id}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPageCompleteViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task DeletePageAsync(int id) { var res = await _ganjoorService.DeletePageAsync(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// older versions of a page (modifications history except for current version) /// /// /// [HttpGet] [Route("page/oldversions/{id}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPageSnapshotSummaryViewModel[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetOlderVersionsOfPage(int id) { RServiceResult res = await _ganjoorService.GetOlderVersionsOfPage(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// get old version of page /// /// /// [HttpGet] [Route("oldversion/{id}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorModifyPageViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetOldVersionOfPage(int id) { RServiceResult res = await _ganjoorService.GetOldVersionOfPage(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// page url by id /// /// /// [HttpGet] [Route("pageurl")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetPageUrlById(int id) { RServiceResult res = await _ganjoorService.GetPageUrlById(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (string.IsNullOrEmpty(res.Result)) return NotFound(); return Ok(res.Result); } /// /// get redirect url for a url /// /// /// [HttpGet] [Route("redirecturl")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetRedirectAddressForPageUrl(string url) { RServiceResult res = await _ganjoorService.GetRedirectAddressForPageUrl(url); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (string.IsNullOrEmpty(res.Result)) return NotFound(); return Ok(res.Result); } /// /// get poem by id /// /// /// /// /// /// /// /// /// not implemented yet /// /// next/previous /// /// /// [HttpGet] [Route("poem/{id}")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPoemCompleteViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] 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, bool relatedpoems = true, GanjoorCommentSortOrder commentSortOrder = GanjoorCommentSortOrder.TopRated) { RServiceResult res = await _ganjoorService.GetPoemById(id, catInfo, catPoems, rhymes, recitations, images, songs, comments, verseDetails, navigation, relatedpoems, true, commentSortOrder ); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (res.Result == null) return NotFound(); return Ok(res.Result); } /// /// get poem verses by id /// /// /// /// [HttpGet] [Route("poem/{id}/verses")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorVerseViewModel[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetPoemVerses(int id, int coupletIndex = -1) { RServiceResult res = await _ganjoorService.GetPoemVersesAsync(id, coupletIndex); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (res.Result == null) return NotFound(); return Ok(res.Result); } /// /// get poem by url /// /// /// /// /// not implemented yet /// /// /// not implemented yet /// not implemented yet /// /// next/previous /// [HttpGet] [Route("poem")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPoemCompleteViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] 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) { RServiceResult res = await _ganjoorService.GetPoemByUrl(url, catInfo, catPoems, rhymes, recitations, images, songs, comments, verseDetails, navigation); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (res.Result == null) return NotFound(); return Ok(res.Result); } /// /// get poem recitations (PlainText/HtmlText are intentionally empty) /// /// /// [HttpGet] [Route("poem/{id}/recitations")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(PublicRecitationViewModel[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetPoemRecitations(int id) { RServiceResult res = await _ganjoorService.GetPoemRecitations(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// get user upvoted recitations of a poem /// /// /// [HttpGet] [Route("poem/{id}/recitations/upvotes")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(int[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetUserPoemRecitationsUpVotes(int id) { var loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.GetUserPoemRecitationsUpVotes(id, loggedOnUserId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// get poem images (PlainText/HtmlText are intentionally empty) /// /// /// [HttpGet] [Route("poem/{id}/images")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(PoemRelatedImage[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetPoemImages(int id) { RServiceResult res = await _ganjoorService.GetPoemImages(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// get poem songs /// /// /// /// /// [HttpGet] [Route("poem/{id}/songs")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(PoemMusicTrackViewModel[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetPoemSongs(int id, bool approved, PoemMusicTrackType trackType = PoemMusicTrackType.All) { RServiceResult res = await _ganjoorService.GetPoemSongs(id, approved, trackType); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// Get Poem Comments /// /// /// /// TopRated, Oldest, or Newest /// [HttpGet] [Route("poem/{id}/comments")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorCommentSummaryViewModel[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetPoemComments(int id, int? coupletIndex, GanjoorCommentSortOrder sortOrder = GanjoorCommentSortOrder.TopRated) { RServiceResult res = await _ganjoorService.GetPoemComments(id, Guid.Empty, coupletIndex, sortOrder); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// Rate a comment (like / dislike / clear rating) /// /// comment id /// +1: like, -1: dislike, 0: remove previous rating /// [HttpPost] [Route("poem/comment/{id}/rate")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorCommentRatingResultViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task RateComment(int id, short value) { Guid userId = new Guid(User.Claims.First(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.RateCommentAsync(userId, id, value); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// get the logged on user's own rating values for a poem's comments /// (meant to be merged client-side into an already fetched, anonymously cacheable comment list) /// /// poem id /// [HttpGet] [Route("poem/{id}/comments/myratings")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorCommentUserRatingViewModel[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetUserCommentRatings(int id) { var loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.GetUserCommentRatings(id, loggedOnUserId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// Get Section Related ones /// /// /// /// /// zero or less than it means all /// [HttpGet] [Route("section/{poemId}/{sectionIndex}/related")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorCachedRelatedSection[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetRelatedSections(int poemId, int sectionIndex, int skip = 0, int itemsCount = 0) { RServiceResult res = await _ganjoorService.GetRelatedSections(poemId, sectionIndex, skip, itemsCount); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// find poem section rhyming letters /// /// /// [HttpGet] [Route("section/analyserhyme/{id}")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjooRhymeAnalysisResult))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task FindSectionRhyme(int id) { var res = await _ganjoorService.FindSectionRhyme(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// delete a poem section (section should not be linked to a poem verse of types other than paragraphs or comments) /// /// /// /// /// [HttpDelete] [Route("section/{poemId}/{sectionIndex}/{convertVerses}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task DeletePoemSectionByPoemIdAndIndexAsync(int poemId, int sectionIndex, bool convertVerses) { var res = await _ganjoorService.DeletePoemSectionByPoemIdAndIndexAsync(poemId, sectionIndex, convertVerses); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (res.Result == false) return NotFound(); return Ok(); } /// /// delete a poem /// /// /// [HttpDelete] [Route("poem/{id}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPageCompleteViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task DeletePoemAsync(int id) { var res = await _ganjoorService.DeletePoemAsync(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// send poem corrections /// /// /// [HttpPost] [Route("poem/correction")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPoemCorrectionViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task SuggestPoemCorrection([FromBody] GanjoorPoemCorrectionViewModel correction) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); correction.UserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.SuggestPoemCorrection(correction); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (res.Result == null) return NotFound(); return Ok(res.Result); } /// /// delete unreviewed user corrections for a poem /// /// /// [HttpDelete] [Route("poem/correction/{id}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPoemCorrectionViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task DeletePoemCorrections(int id) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); var userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); var res = await _ganjoorService.DeletePoemCorrections(userId, id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// returns last unreviewed correction from the user for a poem /// /// /// [HttpGet] [Route("poem/correction/last/{id}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPoemCorrectionViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetLastUnreviewedUserCorrectionForPoem(int id) { Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.GetLastUnreviewedUserCorrectionForPoem(userId, id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result);//might be null } /// /// get list of user suggested corrections /// /// /// [HttpGet] [Route("corrections/mine")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetUserCorrections([FromQuery] PagingParameterModel paging) { Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); var res = await _ganjoorService.GetUserCorrections(userId, paging); // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(res.Result.PagingMeta)); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result.Items); } /// /// get list of all suggested corrections /// /// /// userId /// [HttpGet] [Route("corrections/all")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetAllCorrections([FromQuery] PagingParameterModel paging, Guid? userId = null) { var res = await _ganjoorService.GetUserCorrections(userId ?? Guid.Empty, paging); // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(res.Result.PagingMeta)); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result.Items); } /// /// effective corrections for poem /// /// /// /// [HttpGet] [Route("poem/{id}/corrections/effective")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetPoemEffectiveCorrections(int id, [FromQuery] PagingParameterModel paging) { var res = await _ganjoorService.GetPoemEffectiveCorrections(id, paging); // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(res.Result.PagingMeta)); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result.Items); } /// /// get correction by id /// /// /// [HttpGet] [Route("correction/{id}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPoemCorrectionViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetCorrectionById(int id) { Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.GetCorrectionById(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result);//might be null } /// /// get next unreviewed correction /// /// /// /// [HttpGet] [Route("poem/correction/next")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPoemCorrectionViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetNextUnreviewedCorrection(int skip = 0, bool onlyUserCorrections = false) { RServiceResult res = await _ganjoorService.GetNextUnreviewedCorrection(skip, onlyUserCorrections); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); var resCount = await _ganjoorService.GetUnreviewedCorrectionCount(onlyUserCorrections); if (!string.IsNullOrEmpty(resCount.ExceptionString)) return BadRequest(resCount.ExceptionString); // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject( new PaginationMetadata() { totalCount = resCount.Result, pageSize = -1, currentPage = -1, hasNextPage = false, hasPreviousPage = false, totalPages = -1 }) ); return Ok(res.Result);//might be null } /// /// moderate poem correction /// /// /// [HttpPost] [Route("correction/moderate")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPoemCorrectionViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task ModeratePoemCorrection([FromBody] GanjoorPoemCorrectionViewModel correction) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.ModeratePoemCorrection(userId, correction); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (res.Result == null) return NotFound(); return Ok(res.Result); } /// /// break a poem from a verse forward /// /// /// id of new poem [HttpPost] [Route("poem/break")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(int))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task BreakPoemAsync([FromBody] PoemVerseOrder verse) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); var userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.BreakPoemAsync(verse.PoemId, verse.VOrder, userId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// suggest song for poem /// /// /// [HttpPost] [Route("song")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(PoemMusicTrackViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task SuggestSong([FromBody] PoemMusicTrackViewModel song) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); Guid sessionId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value); RServiceResult sessionCheckResult = await _appUserService.SessionExists(userId, sessionId); if (!string.IsNullOrEmpty(sessionCheckResult.ExceptionString)) { return StatusCode((int)HttpStatusCode.Forbidden); } var res = await _ganjoorService.SuggestSong(userId, song); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// get next unreviewed song /// /// /// /// [HttpGet] [Route("song")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(PoemMusicTrackViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Forbidden)] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetNextUnreviewedSong(int skip = 0, bool onlyMine = false) { Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); Guid sessionId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value); RServiceResult sessionCheckResult = await _appUserService.SessionExists(userId, sessionId); if (!string.IsNullOrEmpty(sessionCheckResult.ExceptionString)) { return StatusCode((int)HttpStatusCode.Forbidden); } var res = await _ganjoorService.GetNextUnreviewedSong(skip, onlyMine ? userId : Guid.Empty); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (res.Result == null) return NotFound(); var resCount = await _ganjoorService.GetUnreviewedSongsCount(onlyMine ? userId : Guid.Empty); if (!string.IsNullOrEmpty(resCount.ExceptionString)) return BadRequest(resCount.ExceptionString); // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject( new PaginationMetadata() { totalCount = resCount.Result, pageSize = -1, currentPage = -1, hasNextPage = false, hasPreviousPage = false, totalPages = -1 }) ); return Ok(res.Result); } /// /// get song by id /// /// /// [HttpGet] [Route("song/{id}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ReviewSongs)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(PoemMusicTrackViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Forbidden)] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetPoemSongById(int id) { var res = await _ganjoorService.GetPoemSongById(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// history of suggested songs by a user /// [HttpGet] [Route("song/user/stats/{userId}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ReviewSongs)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(UserSongSuggestionsHistory))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task GetUserSongsSuggestionsStatistics(Guid userId) { var res = await _ganjoorService.GetUserSongsSuggestionsStatistics(userId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// user suggested songs /// /// /// [HttpGet] [Route("song/mysuggestions")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetUserSuggestedSongs([FromQuery] PagingParameterModel paging) { Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); var res = await _ganjoorService.GetUserSuggestedSongs(userId, paging); // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(res.Result.PagingMeta)); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result.Items); } /// /// review song /// /// /// [HttpPut] [Route("song")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ReviewSongs)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(PoemMusicTrackViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task ReviewSong([FromBody] PoemMusicTrackViewModel song) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); var res = await _ganjoorService.ReviewSong(song); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// modify a published song /// /// /// [HttpPut] [Route("song/update")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ReviewSongs)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(PoemMusicTrackViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task ModifyPublishedSong([FromBody] PoemMusicTrackViewModel song) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); var res = await _ganjoorService.ModifyPublishedSong(song); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// delete a poem song by id /// /// /// [HttpDelete] [Route("song")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ReviewSongs)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task DeletePoemSongById(int id) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); var res = await _ganjoorService.DeletePoemSongById(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// directly insert a poem related song /// /// /// [HttpPost] [Route("song/add")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.AddSongs)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(PoemMusicTrackViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task DirectInsertSong([FromBody] PoemMusicTrackViewModel song) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); var res = await _ganjoorService.DirectInsertSong(song); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// get a random poem from hafez /// /// [HttpGet] [Route("hafez/faal")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPoemCompleteViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task Faal() { RServiceResult res = await _ganjoorService.Faal(2, true); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// get a random poem from a poet (c.ganjoor.net replacement), 0 means random poet, /// حافظ (2)، خیام (3)، ابوسعید ابوالخیر (26)، صائب (22)، سعدی (7)، باباطاهر (28)، مولوی (5)، اوحدی (19)، خواجو (20)، شهریار (35)، عراقی (21)، فروغی بسطامی (32)، سلمان ساوجی (40)، محتشم کاشانی (29)، امیرخسرو دهلوی (34)، سیف فرغانی (31)، عبید زاکانی (33)، هاتف اصفهانی (25) یا رهی معیری (41) /// /// /// [HttpGet] [Route("poem/random")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPoemCompleteViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetARandomPoem(int poetId) { RServiceResult res = await _ganjoorService.Faal(poetId, false); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// get a single comment information (only published comments, replies are not included) /// /// /// [HttpGet] [Route("comments/{id}")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorCommentFullViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetCommentByIdAsync(int id) { var res = await _ganjoorService.GetCommentByIdAsync(id); if (!string.IsNullOrEmpty(res.ExceptionString)) { return BadRequest(res.ExceptionString); } if(res.Result == null) { return NotFound(); } return Ok(res.Result); } /// /// get recent comments /// /// /// /// /// [HttpGet] [Route("comments")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetRecentComments([FromQuery] PagingParameterModel paging, Guid? filterUserId = null, string term = null) { var comments = await _ganjoorService.GetRecentComments(paging, filterUserId == null ? Guid.Empty : (Guid)filterUserId, true, false, term); if (!string.IsNullOrEmpty(comments.ExceptionString)) { return BadRequest(comments.ExceptionString); } // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(comments.Result.PagingMeta)); return Ok(comments.Result.Items); } /// /// get logged on users recent comments /// /// /// [HttpGet] [Route("comments/mine")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetMyRecentComments([FromQuery] PagingParameterModel paging) { Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); Guid sessionId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value); RServiceResult sessionCheckResult = await _appUserService.SessionExists(userId, sessionId); if (!string.IsNullOrEmpty(sessionCheckResult.ExceptionString)) { return StatusCode((int)HttpStatusCode.Forbidden); } var comments = await _ganjoorService.GetRecentComments(paging, userId, false); if (!string.IsNullOrEmpty(comments.ExceptionString)) { return BadRequest(comments.ExceptionString); } // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(comments.Result.PagingMeta)); return Ok(comments.Result.Items); } /// /// get awaiting comments /// /// /// [HttpGet] [Route("comments/awaiting")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ModerateOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetAwaitingComments([FromQuery] PagingParameterModel paging) { var comments = await _ganjoorService.GetRecentComments(paging, Guid.Empty, false, true); if (!string.IsNullOrEmpty(comments.ExceptionString)) { return BadRequest(comments.ExceptionString); } // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(comments.Result.PagingMeta)); return Ok(comments.Result.Items); } /// /// delete awaiting comment /// /// /// [HttpDelete] [Route("comment/awaiting/delete")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ModerateOperationShortName)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task DeleteAnybodyComment(int id) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); var res = await _ganjoorService.DeleteAnybodyComment(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (!res.Result) return NotFound(); return Ok(); } /// /// publish awaiting comment /// /// /// [HttpPut] [Route("comment/awaiting/publish")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ModerateOperationShortName)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task PublishAwaitingComment([FromBody] int id) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); var res = await _ganjoorService.PublishAwaitingComment(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (!res.Result) return NotFound(); return Ok(); } /// /// post new comment /// /// /// [HttpPost] [Route("comment")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorCommentSummaryViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task NewComment([FromBody] GanjoorCommentPostViewModel comment) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); string clientIPAddress = _httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString(); Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); Guid sessionId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value); RServiceResult sessionCheckResult = await _appUserService.SessionExists(userId, sessionId); if (!string.IsNullOrEmpty(sessionCheckResult.ExceptionString)) { return StatusCode((int)HttpStatusCode.Forbidden); } var res = await _ganjoorService.NewComment(userId, clientIPAddress, comment.PoemId, comment.HtmlComment, comment.InReplyToId, comment.CoupletIndex); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// edit user's own comment /// /// /// /// [HttpPut] [Route("comment/{id}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task EditMyComment(int id, [FromBody] string comment) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); Guid sessionId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value); RServiceResult sessionCheckResult = await _appUserService.SessionExists(userId, sessionId); if (!string.IsNullOrEmpty(sessionCheckResult.ExceptionString)) { return StatusCode((int)HttpStatusCode.Forbidden); } var res = await _ganjoorService.EditMyComment(userId, id, comment); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (!res.Result) return NotFound(); return Ok(); } /// /// link or unlink user's own comment to a coupletIndex /// /// /// /// couplet summary for linked comment [HttpPut] [Route("comment/{id}/editlink/{coupletIndex}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task LinkUnLinkMyComment(int id, int? coupletIndex) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); Guid sessionId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value); RServiceResult sessionCheckResult = await _appUserService.SessionExists(userId, sessionId); if (!string.IsNullOrEmpty(sessionCheckResult.ExceptionString)) { return StatusCode((int)HttpStatusCode.Forbidden); } var res = await _ganjoorService.LinkUnLinkMyComment(userId, id, coupletIndex); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (res.Result == null) return NotFound(); return Ok(res.Result); } /// /// delete user's own comment /// /// /// [HttpDelete] [Route("comment")] [Authorize] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task DeleteMyComment(int id) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); Guid sessionId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value); RServiceResult sessionCheckResult = await _appUserService.SessionExists(userId, sessionId); if (!string.IsNullOrEmpty(sessionCheckResult.ExceptionString)) { return StatusCode((int)HttpStatusCode.Forbidden); } var res = await _ganjoorService.DeleteMyComment(userId, id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (!res.Result) return NotFound(); return Ok(); } /// /// report a comment /// /// /// id of reported record [HttpPost] [Route("comment/report")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(int))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task ReportComment([FromBody] GanjoorPostReportCommentViewModel report) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); Guid sessionId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value); RServiceResult sessionCheckResult = await _appUserService.SessionExists(userId, sessionId); if (!string.IsNullOrEmpty(sessionCheckResult.ExceptionString)) { return StatusCode((int)HttpStatusCode.Forbidden); } RServiceResult res = await _ganjoorService.ReportComment(userId, report); if (!string.IsNullOrEmpty(res.ExceptionString)) { return BadRequest(res.ExceptionString); } return Ok(res.Result); } /// /// delete a report (without deleting corresponding comment) /// /// /// [HttpDelete] [Route("comment/report/{id}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ModerateOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(int))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task DeleteReport(int id) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); RServiceResult res = await _ganjoorService.DeleteReport(id); if (!string.IsNullOrEmpty(res.ExceptionString)) { return BadRequest(res.ExceptionString); } if (!res.Result) { return NotFound(); } return Ok(); } /// /// get list of reported comments /// /// /// [HttpGet] [Route("comments/reported")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ModerateOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetReportedComments([FromQuery] PagingParameterModel paging) { var comments = await _ganjoorService.GetReportedComments(paging); if (!string.IsNullOrEmpty(comments.ExceptionString)) { return BadRequest(comments.ExceptionString); } // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(comments.Result.PagingMeta)); return Ok(comments.Result.Items); } /// /// delete reported other users comment /// /// /// [HttpDelete] [Route("comment/reported/moderate/{reportid}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ModerateOperationShortName)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task DeleteModerateComment(int reportid) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); var res = await _ganjoorService.DeleteModerateComment(reportid); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (!res.Result) return NotFound(); return Ok(); } /// /// imports data from ganjoor SQLite database (form file) /// /// /// [HttpPost] [Route("sqlite/import/{poetId}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ImportOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task ImportLocalSQLiteDb(int poetId) { IFormFile file = Request.Form.Files[0]; RServiceResult res = await _ganjoorService.ImportFromSqlite(poetId, file); if (res.Result) return Ok(); return BadRequest(res.ExceptionString); } /// /// import category data from ganjoor SQLite database (form file) /// /// /// [HttpPost] [Route("sqlite/import/cat/{catId}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ImportOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task ImportCategoryFromSqlite(int catId) { IFormFile file = Request.Form.Files[0]; RServiceResult res = await _ganjoorService.ImportCategoryFromSqlite(catId, file); if (res.Result) return Ok(); return BadRequest(res.ExceptionString); } /// /// Apply corrections from sqlite /// /// /// /// [HttpPost] [Route("sqlite/update/{poetId}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ImportOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task ApplyCorrectionsFromSqlite(int poetId, string note) { IFormFile file = Request.Form.Files[0]; RServiceResult res = await _ganjoorService.ApplyCorrectionsFromSqlite(poetId, file, note); if (res.Result) return Ok(); return BadRequest(res.ExceptionString); } /// /// export a poet to sqlite database /// /// /// [HttpGet] [Route("sqlite/export/{poetId}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ImportOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(FileStreamResult))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task ExportToSqlite(int poetId) { RServiceResult res = await _ganjoorService.ExportToSqlite(poetId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return new FileStreamResult(new FileStream(res.Result, FileMode.Open, FileAccess.Read), "application/octet-stream"); } /// /// start exporting all poets /// /// [HttpPost("sqlite/batchexport")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ReviewSongs)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Unauthorized)] public IActionResult StartBatchGenerateGDBFiles() { try { var res = _ganjoorService.StartBatchGenerateGDBFiles(); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } catch (Exception exp) { return BadRequest(exp.ToString()); } } /// /// start exporting all published Ganjoor data to the public git-tracked JSON data set /// /// [HttpPost("publicdata/batchexport")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ImportOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Unauthorized)] public IActionResult StartBatchExportPublicGitData() { try { var res = _ganjoorService.StartBatchExportPublicGitData(); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } catch (Exception exp) { return BadRequest(exp.ToString()); } } /// /// start exporting the Tajik (Cyrillic) overlay data to its own, separate public /// git-tracked JSON data set — only poets/categories/poems with an actual Tajik /// translation are included /// /// [HttpPost("tajik/publicdata/batchexport")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ImportOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Unauthorized)] public IActionResult StartBatchExportTajikPublicGitData() { try { var res = _ganjoorService.StartBatchExportTajikPublicGitData(); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } catch (Exception exp) { return BadRequest(exp.ToString()); } } /// /// (re)build local Ganjoor content from a public data export tree (local folder or HTTP) — /// intended for local development use, not for production servers /// /// [HttpPost("publicdata/import")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ImportOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Unauthorized)] public IActionResult StartImportFromPublicDataRepo([FromBody] PublicDataImportRequestDto request) { try { var res = _ganjoorService.StartImportFromPublicDataRepo(request.UseHttp, request.Location, request.PoetId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } catch (Exception exp) { return BadRequest(exp.ToString()); } } /// /// Get user public profile /// /// /// [HttpGet("user/profile/{id}")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorUserPublicProfile))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetUserPublicProfile(Guid id) { RServiceResult userInfo = await _appUserService.GetUserInformation(id); if (userInfo.Result == null) { if (string.IsNullOrEmpty(userInfo.ExceptionString)) return NotFound(); return BadRequest(userInfo.ExceptionString); } return Ok ( new GanjoorUserPublicProfile() { Id = id, NickName = userInfo.Result.NickName, Bio = userInfo.Result.Bio, Website = userInfo.Result.Website, RImageId = userInfo.Result.RImageId } ); } /// /// Get Similar Poems accroding to prosody and rhyme informations /// /// /// cannot be empty /// can be empty /// send 0 for all /// send 0 for all /// /// /// /// /// /// /// return value is not complete or valid for some parts, you should use only the valid parts! [HttpGet] [Route("poems/similar")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetSimilarPoemsAsync([FromQuery] PagingParameterModel paging, string metre, string rhyme, int poetId = 0, int catId = 0, string language = "fa-IR", GanjoorPoemFormat format = GanjoorPoemFormat.Unknown, string term = null, int coupletCountsFrom = 0, int coupletCountsTo = 0, int[] e = null) { var pagedResult = await _ganjoorService.GetSimilarPoemsAsync(paging, metre, rhyme, poetId == 0 ? null : poetId, catId == 0 ? null : catId, language, format, term, coupletCountsFrom, coupletCountsTo, e == null ? [] : e); if (!string.IsNullOrEmpty(pagedResult.ExceptionString)) return BadRequest(pagedResult.ExceptionString); // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(pagedResult.Result.PagingMeta)); return Ok(pagedResult.Result.Items); } /// /// language tagged poem sections /// /// /// fa-IR, ar, ... /// 0 means all poets /// [HttpGet] [Route("sections/tagged/language")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetLanguageTaggedPoemSections([FromQuery] PagingParameterModel paging, string language, int poetId = 0) { var pagedResult = await _ganjoorService.GetLanguageTaggedPoemSections(paging, language, poetId == 0 ? (int?)null : poetId); if (!string.IsNullOrEmpty(pagedResult.ExceptionString)) return BadRequest(pagedResult.ExceptionString); // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(pagedResult.Result.PagingMeta)); return Ok(pagedResult.Result.Items); } /// /// search /// /// /// /// /// /// /// [HttpGet] [Route("poems/search")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task Search([FromQuery] PagingParameterModel paging, string term, int poetId = 0, int catId = 0, int[] e = null) { var pagedResult = await _ganjoorService.Search(paging, term, poetId == 0 ? (int?)null : poetId, catId == 0 ? (int?)null : catId, e == null ?[] : e); if (!string.IsNullOrEmpty(pagedResult.ExceptionString)) return BadRequest(pagedResult.ExceptionString); // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(pagedResult.Result.PagingMeta)); return Ok(pagedResult.Result.Items); } /// /// returns ganjoor metre list ordered by rhythm /// /// /// [HttpGet] [Route("rhythms")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetGanjoorMetres(bool sortOnVerseCount = false) { var res = await _ganjoorService.GetGanjoorMetres(sortOnVerseCount); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// find poem rhyme /// /// /// [HttpGet] [Route("poem/analysisrhyme/{id}")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjooRhymeAnalysisResult))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task FindPoemRhyme(int id) { var res = await _ganjoorService.FindPoemMainSectionRhyme(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// analysis poem to find its prosody information /// /// /// [HttpGet] [Route("poem/analysisrhythm/{id}")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task FindPoemRhythm(int id) { var res = await _ganjoorService.FindPoemMainSectionRhythm(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// examine site pages for broken links /// /// [HttpPost] [Route("healthcheck")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ImportOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult HealthCheckContents() { RServiceResult res = _ganjoorService.HealthCheckContents(); if (res.Result) return Ok(); return BadRequest(res.ExceptionString); } /// /// examine comments for long links /// /// [HttpPost] [Route("fixlongurlsincomments")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ImportOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult FindAndFixLongUrlsInComments() { RServiceResult res = _ganjoorService.FindAndFixLongUrlsInComments(); if (res.Result) return Ok(); return BadRequest(res.ExceptionString); } /// /// start filling poems couplet indices /// /// [HttpPost] [Route("fillpoemscoupletindices")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ImportOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult StartFillingPoemsCoupletIndices() { RServiceResult res = _ganjoorService.StartFillingPoemsCoupletIndices(); if (res.Result) return Ok(); return BadRequest(res.ExceptionString); } /// /// refill couplet indices for poem /// /// /// [HttpPost] [Route("refillcoupletindices/{poemId}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ImportOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task RefillCoupletIndicesAsync(int poemId) { RServiceResult res = await _ganjoorService.RefillCoupletIndicesAsync(poemId); if (!string.IsNullOrEmpty(res.ExceptionString)) { return BadRequest(res.ExceptionString); } return Ok(); } /// /// fill section couplet counts /// /// [HttpPost] [Route("fillsectioncoupletcounts")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ImportOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult StartFillingSectionCoupletCounts() { RServiceResult res = _ganjoorService.StartFillingSectionCoupletCounts(); if (res.Result) return Ok(); return BadRequest(res.ExceptionString); } /// /// regenerate poem full titles to fix an old bug /// /// [HttpPost] [Route("maintenance/regenfulltitles")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ImportOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult RegeneratePoemsFullTitles() { RServiceResult res = _ganjoorService.RegeneratePoemsFullTitles(); if (res.Result) return Ok(); return BadRequest(res.ExceptionString); } /// /// start finding rhymes /// /// [HttpPost] [Route("singlecouplets/startfindingrhymes")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ImportOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult StartFindingRhymesForSingleCouplets() { RServiceResult res = _ganjoorService.StartFindingRhymesForSingleCouplets(); if (res.Result) return Ok(); return BadRequest(res.ExceptionString); } /// /// separate verses in poem.PlainText with Environment.NewLine instead of SPACE /// /// if it is 0 it is ignored /// [HttpPost("regenplaintext/{catId}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ImportOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult RegerneratePoemsPlainText(int catId) { RServiceResult res = _ganjoorService.RegerneratePoemsPlainText(catId); if (res.Result) return Ok(); return BadRequest(res.ExceptionString); } /// /// start building sitemap /// /// [HttpPost("sitemap")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public IActionResult StartBuildingSitemap() { var res = _ganjoorService.StartBuildingSitemap(); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// build tj.ganjoor.net site map /// /// [HttpPost("sitemap/tajik")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public IActionResult StartBuildingTajikSitemap() { var res = _ganjoorService.StartBuildingTajikSitemap(); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// regenerate stats page /// /// [HttpPut("rebuild/stats")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Unauthorized)] public IActionResult StartUpdatingStatsPage() { try { Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); var res = _ganjoorService.StartUpdatingStatsPage(userId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } catch (Exception exp) { return BadRequest(exp.ToString()); } } /// /// start updating mundex page /// /// [HttpPut("rebuild/mundex")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ReviewSongs)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Unauthorized)] public IActionResult StartUpdatingMundexPage() { try { Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); var res = _ganjoorService.StartUpdatingMundexPage(userId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } catch (Exception exp) { return BadRequest(exp.ToString()); } } /// /// switch bookmark /// /// /// if you send a negative number it means you are trying to bookmark a comment /// [HttpPost] [Route("bookmark/switch/{poemId}/{coupletIndex}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task SwitchCoupletBookmark(int poemId, int coupletIndex) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.SwitchCoupletBookmark(loggedOnUserId, poemId, coupletIndex); if (!string.IsNullOrEmpty(res.ExceptionString)) { if (res.ExceptionString == "verse not found") return NotFound(); return BadRequest(res.ExceptionString); } return Ok(res.Result != null); } /// /// switch bookmark and return bookmark id ('0' for switching off a bookmark) /// /// /// if you send a negative number it means you are trying to bookmark a comment /// [HttpPost] [Route("bookmark/switch/ret/{poemId}/{coupletIndex}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task SwitchCoupletBookmarkReturnId(int poemId, int coupletIndex) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.SwitchCoupletBookmark(loggedOnUserId, poemId, coupletIndex); if (!string.IsNullOrEmpty(res.ExceptionString)) { if (res.ExceptionString == "verse not found") return NotFound(); return BadRequest(res.ExceptionString); } return Ok(res.Result == null ? "0" : res.Result.Id.ToString()); } /// /// Bookmark couplet if it is not /// /// /// /// [HttpPost] [Route("bookmark/{poemId}/{coupletIndex}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task BookmarkCoupletIfNotBookmarked(int poemId, int coupletIndex) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.BookmarkCoupletIfNotBookmarked(loggedOnUserId, poemId, coupletIndex); if (!string.IsNullOrEmpty(res.ExceptionString)) { if (res.ExceptionString == "verse not found") return NotFound(); return BadRequest(res.ExceptionString); } return Ok(res.Result != null); } /// /// delete bookmark /// /// /// [HttpDelete] [Route("bookmark/{bookmarkId}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task DeleteBookmark(Guid bookmarkId) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.DeleteGanjoorBookmark(bookmarkId, loggedOnUserId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// modify bookmark private note /// /// /// /// [HttpPut] [Route("bookmark/{bookmarkId}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task ModifyBookmarkPrivateNoteAsync(Guid bookmarkId, [FromBody] string note) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.ModifyBookmarkPrivateNoteAsync(bookmarkId, loggedOnUserId, note); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// get poem user bookmarks (only Id, CoupletIndex and DateTime are valid in the output view model) /// /// /// [HttpGet] [Route("bookmark/{poemId}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorUserBookmarkViewModel[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetPoemUserBookmarks(int poemId) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.GetPoemUserBookmarks(loggedOnUserId, poemId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// is the poem couplet is bookmarked by user /// /// /// /// [HttpGet] [Route("bookmark/{poemId}/{coupletIndex}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task IsCoupletBookmarked(int poemId, int coupletIndex) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.IsCoupletBookmarked(loggedOnUserId, poemId, coupletIndex); if (!string.IsNullOrEmpty(res.ExceptionString)) { if (res.ExceptionString == "verse not found") return NotFound(); return BadRequest(res.ExceptionString); } return Ok(res.Result); } /// /// user bookmarks /// /// /// /// a phrase to be searched through user private notes /// /// [HttpGet] [Route("bookmark")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorUserBookmarkViewModel[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetUserBookmarks([FromQuery] PagingParameterModel paging, string q) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult<(PaginationMetadata PagingMeta, GanjoorUserBookmarkViewModel[] Bookmarks)> res = await _ganjoorService.GetUserBookmarks(paging, loggedOnUserId, q); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(res.Result.PagingMeta)); return Ok(res.Result.Bookmarks); } /// /// start generating related sections info for wholepoem sections /// /// /// [HttpPost] [Route("generaterelatedsectionsinfo")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ImportOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult StartGeneratingRelatedSectionsInfo(bool regenerate = false) { RServiceResult res = _ganjoorService.StartGeneratingRelatedSectionsInfo(regenerate); if (res.Result) return Ok(); return BadRequest(res.ExceptionString); } /// /// get next ganjoor poem probable metre /// /// [HttpGet] [Route("probablemetre/next")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPoemSection))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetNextGanjoorPoemProbableMetre() { RServiceResult res = await _ganjoorService.GetNextGanjoorPoemProbableMetre(); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (res.Result == null) return NotFound(); return Ok(res.Result); } /// /// get a list of ganjoor poems probable metres /// /// /// [HttpGet] [Route("probablemetre/list")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetUnreviewedGanjoorPoemProbableMetres([FromQuery] PagingParameterModel paging) { var res = await _ganjoorService.GetUnreviewedGanjoorPoemProbableMetres(paging); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(res.Result.PagingMeta)); return Ok(res.Result.Items); } /// /// save ganjoor poem probable metre /// /// /// /// [HttpPut] [Route("probablemetre/save/{id}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task SaveGanjoorPoemProbableMetre(int id, [FromBody] string metre) { RServiceResult res = await _ganjoorService.SaveGanjoorPoemProbableMetre(id, metre); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// dismiss ganjoor poem probable metre /// /// /// [HttpDelete] [Route("probablemetre/dismiss/{id}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task DismissGanjoorPoemProbableMetre(int id) { RServiceResult res = await _ganjoorService.SaveGanjoorPoemProbableMetre(id, "dismissed"); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// Finding Category Poems Duplicates /// /// /// /// /// [HttpPost("duplicates/{srcCatId}/{destCatId}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult StartFindingCategoryPoemsDuplicates(int srcCatId, int destCatId, bool hardTry = false) { var res = _ganjoorService.StartFindingCategoryPoemsDuplicates(srcCatId, destCatId, hardTry); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// manually add a duplicate for a poems /// /// /// /// /// [HttpPost("duplicates/manual/{srcCatId}/{srcPoemId}/{destPoemId}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task AdDuplicateAsync(int srcCatId, int srcPoemId, int destPoemId) { var res = await _ganjoorService.AdDuplicateAsync(srcCatId, srcPoemId, destPoemId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// delete a duplicate /// /// /// [HttpDelete("duplicates/{id}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task DeleteDuplicateAsync(int id) { var res = await _ganjoorService.DeleteDuplicateAsync(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// list of category saved duplicated poems /// /// /// [HttpGet] [Route("duplicates/{catId}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetCategoryDuplicates(int catId) { var res = await _ganjoorService.GetCategoryDuplicates(catId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// start removing category duplicates /// /// /// /// [HttpPut("duplicates/finish/{srcCatId}/{destCatId}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult StartRemovingCategoryDuplicates(int srcCatId, int destCatId) { var res = _ganjoorService.StartRemovingCategoryDuplicates(srcCatId, destCatId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// get couplet sections /// /// /// /// [HttpGet] [Route("couplet/{poemId}/{coupletIndex}/sections")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetCoupletSectionsAsync(int poemId, int coupletIndex) { var res = await _ganjoorService.GetCoupletSectionsAsync(poemId, coupletIndex); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (res.Result == null) return NotFound(); return Ok(res.Result); } /// /// get all poem sections /// /// /// [HttpGet] [Route("sections/{poemId}")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetPoemSectionsAsync(int poemId) { var res = await _ganjoorService.GetPoemSectionsAsync(poemId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// regenerate poem sections (dangerous: wipes out existing data) /// /// /// [HttpPost] [Route("poem/{id}/sections/regenerate")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task RegeneratePoemSections(int id) { var res = await _ganjoorService.RegeneratePoemSections(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// update related sections manually /// /// /// /// [HttpPost] [Route("sections/updaterelated")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult UpdateRelatedSections(int metreId, string rhyme) { _ganjoorService.UpdateRelatedSections(metreId, rhyme); return Ok(); } /// /// regenerate category related sections /// /// category id /// [HttpPut] [Route("cat/{id}/regenrelatedsections")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult StartRegeneratingCateoryRelatedSections(int id) { RServiceResult res = _ganjoorService.StartRegeneratingCateoryRelatedSections(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// get a specific poem section /// /// /// [HttpGet] [Route("section/{sectionId}")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetPoemSectionByIdAsync(int sectionId) { var res = await _ganjoorService.GetPoemSectionByIdAsync(sectionId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (res.Result == null) return NotFound(); return Ok(res.Result); } /// /// start band couplets fix /// /// [HttpPost("ontime/fixbandcouplets")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult StartOnTimeBandCoupletsFix() { var res = _ganjoorService.StartOneTimeBandCoupletsFix(); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// returns last unreviewed correction from the user for a section /// /// section id /// [HttpGet] [Route("section/correction/last/{id}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPoemSectionCorrectionViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetLastUnreviewedUserCorrectionForSection(int id) { Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.GetLastUnreviewedUserCorrectionForSection(userId, id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result);//might be null } /// /// send a correction for a section /// /// /// [HttpPost] [Route("section/correction")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPoemSectionCorrectionViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task SuggestPoemSectionCorrection([FromBody] GanjoorPoemSectionCorrectionViewModel correction) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); correction.UserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.SuggestPoemSectionCorrection(correction); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (res.Result == null) return NotFound(); return Ok(res.Result); } /// /// moderate poem section correction /// /// /// [HttpPost] [Route("section/moderate")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPoemSectionCorrectionViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task ModeratePoemSectionCorrection([FromBody] GanjoorPoemSectionCorrectionViewModel correction) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.ModeratePoemSectionCorrection(userId, correction); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (res.Result == null) return NotFound(); return Ok(res.Result); } /// /// delete unreviewed user corrections for a poem section /// /// /// [HttpDelete] [Route("section/correction/{id}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task DeletePoemSectionCorrections(int id) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); var userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); var res = await _ganjoorService.DeletePoemSectionCorrections(userId, id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// get section correction by id /// /// /// [HttpGet] [Route("section/correction/{id}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPoemSectionCorrectionViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetSectionCorrectionById(int id) { Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.GetSectionCorrectionById(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (res.Result == null) return NotFound(); return Ok(res.Result);//might be null } /// /// get next unreviewed correction for poem sections /// /// /// /// [HttpGet] [Route("section/correction/next")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPoemSectionCorrectionViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetNextUnreviewedPoemSectionCorrection(int skip = 0, bool deletedUserSections = false) { RServiceResult res = await _ganjoorService.GetNextUnreviewedPoemSectionCorrection(skip, deletedUserSections); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); var resCount = await _ganjoorService.GetUnreviewedPoemSectionCorrectionCount(deletedUserSections); if (!string.IsNullOrEmpty(resCount.ExceptionString)) return BadRequest(resCount.ExceptionString); // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject( new PaginationMetadata() { totalCount = resCount.Result, pageSize = -1, currentPage = -1, hasNextPage = false, hasPreviousPage = false, totalPages = -1 }) ); return Ok(res.Result);//might be null } /// /// get list of user suggested corrections /// /// /// [HttpGet] [Route("section/corrections/mine")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetUserSectionCorrections([FromQuery] PagingParameterModel paging) { Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); var res = await _ganjoorService.GetUserSectionCorrections(userId, paging); // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(res.Result.PagingMeta)); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result.Items); } /// /// get list of all suggested corrections /// /// /// [HttpGet] [Route("section/corrections/all")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetAllSectionCorrections([FromQuery] PagingParameterModel paging) { var res = await _ganjoorService.GetUserSectionCorrections(Guid.Empty, paging); // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(res.Result.PagingMeta)); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result.Items); } /// /// effective corrections for section /// /// /// /// [HttpGet] [Route("section/{id}/corrections/effective")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetSectionEffectiveCorrections(int id, [FromQuery] PagingParameterModel paging) { var res = await _ganjoorService.GetSectionEffectiveCorrections(id, paging); // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(res.Result.PagingMeta)); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result.Items); } /// /// transfer poems and sections from a meter to another one and delete the source meter /// /// /// /// [HttpPut] [Route("prosody/transfer/{srcId}/{destId}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task TransferMeterAsync(int srcId, int destId) { RServiceResult res = await _ganjoorService.TransferMeterAsync(srcId, destId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// get poem tags ordered by LunarDateTotalNumber then by Id /// /// /// [HttpGet] [Route("poem/{id}/geotag")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetPoemGeoDateTagsAsync(int id) { var res = await _ganjoorService.GetPoemGeoDateTagsAsync(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// add poem geo tag /// /// /// [HttpPost] [Route("poem/geotag")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(PoemGeoDateTag))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task AddPoemGeoDateTagAsync([FromBody] PoemGeoDateTag tag) { RServiceResult res = await _ganjoorService.AddPoemGeoDateTagAsync(tag); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// update poem tag /// /// /// [HttpPut] [Route("poem/geotag")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task UpdatePoemGeoDateTagAsync([FromBody] PoemGeoDateTag tag) { RServiceResult res = await _ganjoorService.UpdatePoemGeoDateTagAsync(tag); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// delete poem tag /// /// /// [HttpDelete] [Route("poem/geotag/{id}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task DeletePoemGeoDateTagAsync(int id) { RServiceResult res = await _ganjoorService.DeletePoemGeoDateTagAsync(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// get a categoty poem tags /// /// /// [HttpGet] [Route("cat/{id}/geotag")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetCatPoemGeoDateTagsAsync(int id) { var res = await _ganjoorService.GetCatPoemGeoDateTagsAsync(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// get the network of people relevant to this category/work (every person tagged in a poem /// under its subtree, plus their relatives/affiliates one hop out) - for the "شخصیت‌ها" tab /// on a category/poet page /// /// /// [HttpGet] [Route("cat/{id}/persongraph")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPersonGraphViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetCatPersonGraphAsync(int id) { var res = await _personService.GetCatPersonGraphAsync(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// synchronize https:://naskban.ir links (logs in and then out to naskban.ir using auth info) /// /// /// number of synched links [HttpPost("naskban")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ModerateOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult SynchronizeNaskbanLinks( [AuditIgnore] [FromBody] LoginViewModel loginViewModel ) { var userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); _ganjoorService.SynchronizeNaskbanLinks(new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value), loginViewModel.Username, loginViewModel.Password); return Ok(); } [HttpPut("naskban")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ModerateOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult JustifyNaskbanPageNumbers( [AuditIgnore] [FromBody] LoginViewModel loginViewModel ) { _ganjoorService.JustifyNaskbanPageNumbers(loginViewModel.Username, loginViewModel.Password); return Ok(); } /// /// mark naskban links for poems of a categiory and its children as human reviewed /// /// /// /// /// [HttpPut("naskban/humanreviewed/{naskbanBookId}/{catId}/{humanReviewed}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ModerateOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult MarkNaskbanLinksAsHumanReviewed(int naskbanBookId, int catId, bool humanReviewed) { _ganjoorService.MarkNaskbanLinksAsHumanReviewed(naskbanBookId, catId, humanReviewed); return Ok(); } /// /// mark naskban links a text original for a category /// /// /// /// /// [HttpPut("naskban/textoriginal/{naskbanBookId}/{catId}/{textOriginal}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ModerateOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult MarkNaskbanLinksAsTextOriginal(int naskbanBookId, int catId, bool textOriginal) { _ganjoorService.MarkNaskbanLinksAsTextOriginal(naskbanBookId, catId, textOriginal); return Ok(); } /// /// import naskban ganjoor matchings /// /// /// [HttpPut("naskban/import/matchingbooks")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ModerateOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult ImportNaskbanGanjoorPoemMatchFindings( [AuditIgnore] [FromBody] LoginViewModel loginViewModel ) { _ganjoorService.ImportNaskbanGanjoorPoemMatchFindings(loginViewModel.Username, loginViewModel.Password); return Ok(); } /// /// discover poet naskban paper sources /// /// /// /// [HttpPut("naskban/import/poetbooks/{poetId}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ModerateOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult DiscoverPoetNaskbanPaperSources( int poetId, [AuditIgnore] [FromBody] LoginViewModel loginViewModel ) { _ganjoorService.DiscoverPoetNaskbanPaperSources(poetId, loginViewModel.Username, loginViewModel.Password); return Ok(); } /// /// delete poem related naskban images by url /// /// /// [HttpDelete("naskban")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task DeletePoemRelatedNaskbanImagesByNaskbanUrlAsync(string naskbanUrl) { var res = await _ganjoorService.DeletePoemRelatedNaskbanImagesByNaskbanUrlAsync(naskbanUrl); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// import paper sources from museum /// /// [HttpPut] [Route("papersources/import/{poetid}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ModerateOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public IActionResult ImportPaperSourcesFromMuseum(int poetid = 0) { _ganjoorService.ImportPaperSourcesFromMuseum(poetid); return Ok(); } /// /// get paper sources for a catgeory /// /// /// [HttpGet] [Route("cat/{id}/papersources")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPaperSource[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetCategoryPaperSourcesAsync(int id) { var res = await _ganjoorService.GetCategoryPaperSourcesAsync(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// get category poem related images /// /// /// [HttpGet] [Route("cat/{id}/images")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(PoemRelatedImageEx[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetCatPoemImagesAsync(int id) { var res = await _ganjoorService.GetCatPoemImagesAsync(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// extracting quoted poems /// /// [HttpPost("quoted/extract")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult StartExtractingQuotedPoems() { var res = _ganjoorService.StartExtractingQuotedPoems(); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// regenerate related poems pages /// /// [HttpPut("quoted/pages/generate")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult StartRegeneratingRelatedPoemsPages() { var loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); var res = _ganjoorService.StartRegeneratingRelatedPoemsPages(loggedOnUserId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// regenerate two poets similar page /// /// /// /// [HttpPut("quoted/pages/generate/{poetId}/{relatedPoetId}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult StartRegeneratingRelatedPoemsPageAsync(int poetId, int relatedPoetId) { var loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); var res = _ganjoorService.StartRegeneratingRelatedPoemsPageAsync(loggedOnUserId, poetId, relatedPoetId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// discover related poems /// /// /// /// /// /// /// [HttpPost("quoted/discover/{poetId}/{relatedPoetId}/{breakOnFirstSimilar}/{relatedSubCatId}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult StartDiscoverRelatedPoems(int poetId, int relatedPoetId, bool breakOnFirstSimilar = false, int relatedSubCatId = 0, bool insertReverse = false) { var res = _ganjoorService.StartDiscoverRelatedPoems(poetId, relatedPoetId, breakOnFirstSimilar, relatedSubCatId == 0 ? null : relatedSubCatId, insertReverse); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// get quoted poems for a poem /// /// /// /// /// /// /// /// [HttpGet] [Route("poem/{id}/quoteds")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorQuotedPoemViewModel[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetGanjoorQuotedPoemsForPoemAsync(int id, int skip, int itemsCount, bool? onlyClaimedByBothPoets = null, bool? published = null, bool? chosenForMainList = null) { RServiceResult res = await _ganjoorService.GetGanjoorQuotedPoemsForPoemAsync(id, skip, itemsCount, onlyClaimedByBothPoets, published, chosenForMainList); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// two poems quoted records /// /// /// /// /// [HttpGet] [Route("poem/{id}/quoteds/{relatedId}")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorQuotedPoemViewModel[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetGanjoorQuotedPoemsForRelatedAsync(int id, int relatedId, bool? published = null) { RServiceResult res = await _ganjoorService.GetGanjoorQuotedPoemsForRelatedAsync(id, relatedId, published); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// get quoted poems /// /// /// /// /// /// /// /// [HttpGet] [Route("quoted")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorQuotedPoemViewModel[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetGanjoorQuotedPoemsAsync(int? poetId, int? relatedPoetId, bool? chosen, bool? published, bool? claimed, bool? indirect) { RServiceResult res = await _ganjoorService.GetGanjoorQuotedPoemsAsync(poetId, relatedPoetId, chosen, published, claimed, indirect); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// get quoted by id /// /// /// [HttpGet] [Route("quoted/{id}")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorQuotedPoemViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetGanjoorQuotedPoemByIdAsync(Guid id) { RServiceResult res = await _ganjoorService.GetGanjoorQuotedPoemByIdAsync(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// insert quoted /// /// /// [HttpPost] [Route("quoted")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorQuotedPoemViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task InsertGanjoorQuotedPoemAsync([FromBody] GanjoorQuotedPoemViewModel quoted) { var loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.InsertGanjoorQuotedPoemAsync(quoted, loggedOnUserId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// update quoted /// /// /// [HttpPut] [Route("quoted")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task UpdateGanjoorQuotedPoemsAsync([FromBody] GanjoorQuotedPoemViewModel quoted) { var loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.UpdateGanjoorQuotedPoemsAsync(quoted, loggedOnUserId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// delete quoted /// /// /// [HttpDelete] [Route("quoted")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task DeleteGanjoorQuotedPoemByIdAsync(Guid id) { var loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.DeleteGanjoorQuotedPoemByIdAsync(id, loggedOnUserId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// suggest new quote (for normal users) /// /// /// [HttpPost] [Route("quoted/suggest")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorQuotedPoemViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task SuggestGanjoorQuotedPoemAsync([FromBody] GanjoorQuotedPoemViewModel quoted) { var loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.SuggestGanjoorQuotedPoemAsync(quoted, loggedOnUserId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// get list of user suggested corrections /// /// /// [HttpGet] [Route("quoted/suggest")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetUserQuotedSuggestionsAsync([FromQuery] PagingParameterModel paging) { Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); var res = await _ganjoorService.GetUserQuotedSuggestionsAsync(userId, paging); // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(res.Result.PagingMeta)); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result.Items); } /// /// next unmoderated quoted poem /// /// /// [HttpGet] [Route("quoted/suggestion/next")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorQuotedPoemViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetNextUnmoderatedGanjoorQuotedPoemAsync(int skip = 0) { RServiceResult res = await _ganjoorService.GetNextUnmoderatedGanjoorQuotedPoemAsync(skip); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); var resCount = await _ganjoorService.GetUnmoderatedGanjoorQuotedsCountAsync(); if (!string.IsNullOrEmpty(resCount.ExceptionString)) return BadRequest(resCount.ExceptionString); // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject( new PaginationMetadata() { totalCount = resCount.Result, pageSize = -1, currentPage = -1, hasNextPage = false, hasPreviousPage = false, totalPages = -1 }) ); return Ok(res.Result);//might be null } /// /// moderate quoted poems /// /// /// [HttpPut] [Route("quoted/moderate")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task ModerateGanjoorQuotedPoemAsync([FromBody] GanjoorQuotedPoemModerationViewModel model) { var loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.ModerateGanjoorQuotedPoemAsync(model, loggedOnUserId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// digital source from tag /// /// /// [HttpGet] [Route("source")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(DigitalSource))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetDigitalSourceFromTagAsync(string sourceUrlSlug) { RServiceResult res = await _ganjoorService.GetDigitalSourceFromTagAsync(sourceUrlSlug); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result);//might be null } /// /// tag category with source /// /// /// /// [HttpPut("source/{catId}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult TagCategoryWithSource(int catId, [FromBody]DigitalSource source) { _ganjoorService.TagCategoryWithSource(catId, source); return Ok(); } /// /// update digital sources stats /// /// [HttpPost("source/stats/rebuild")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult UpdateDigitalSourcesStats() { Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); _ganjoorService.UpdateDigitalSourcesStats(userId); return Ok(); } /// /// add new page /// /// /// [HttpPost] [Route("page")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPage))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task AddPageAsync([FromBody]GanjoorPage page) { var res = await _ganjoorService.AddPageAsync(page); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// build word counts /// /// /// /// [HttpPost("wordcounts/rebuild/{poetId}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task BuildCategoryWordCountsAsync(bool reset = false, int poetId = 0) { await _ganjoorService.BuildCategoryWordCountsAsync(reset, poetId); return Ok(); } /// /// one time fixer for word counts new RowNmbrInCat fields /// /// [HttpPut("wordcounts/fillwordcounts")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult FillCategoryWordCountsRowNmbrInCat() { _ganjoorService.FillCategoryWordCountsRowNmbrInCat(); return Ok(); } /// /// one time fixer for category word count summries /// /// [HttpPut("wordcounts/fillwordcountsummeries")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult FillCategoryWordCountSummaries() { _ganjoorService.FillCategoryWordCountSummaries(); return Ok(); } /// /// category word counts /// /// /// can be empty /// /// [HttpGet] [Route("wordcounts/{catId}")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetCategoryWordCountsAsync(int catId, string term, [FromQuery] PagingParameterModel paging) { var pagedResult = await _ganjoorService.GetCategoryWordCountsAsync(catId, term, paging); if (!string.IsNullOrEmpty(pagedResult.ExceptionString)) return BadRequest(pagedResult.ExceptionString); // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(pagedResult.Result.PagingMeta)); return Ok(pagedResult.Result.Items); } /// /// category words summary /// /// /// [HttpGet] [Route("wordsums/{catId}")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(CategoryWordCountSummary))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetCategoryWordCountSummaryAsync(int catId) { var res = await _ganjoorService.GetCategoryWordCountSummaryAsync(catId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// comparison of word counts for poets /// /// /// /// can be null or can be extracted from poetId /// is not necessary unless you mean a specific poet and do not provide catId /// [HttpGet] [Route("wordcounts/bycat")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetCategoryWordCountsByPoetsAsync(string term, [FromQuery] PagingParameterModel paging, int? catId, int? poetId) { var pagedResult = await _ganjoorService.GetCategoryWordCountsBySubCatsAsync(term, paging, catId, poetId); if (!string.IsNullOrEmpty(pagedResult.ExceptionString)) return BadRequest(pagedResult.ExceptionString); var wordCountResult = await _ganjoorService.GetCategoryWordCountByTermAsync(term, catId, poetId); if(!string.IsNullOrEmpty(wordCountResult.ExceptionString)) return BadRequest(wordCountResult.ExceptionString); // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(pagedResult.Result.PagingMeta)); HttpContext.Response.Headers.Append("items-count", JsonConvert.SerializeObject(wordCountResult.Result.Count)); return Ok(pagedResult.Result.Items); } /// /// CategoryWordCount for a specific term in a category /// /// /// /// /// [HttpGet] [Route("wordcounts/bycat/count")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(CategoryWordCount))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetCategoryWordCountByTermAsync(string term, int? catId, int? poetId) { var countResult = await _ganjoorService.GetCategoryWordCountByTermAsync(term, catId, poetId); if (!string.IsNullOrEmpty(countResult.ExceptionString)) return BadRequest(countResult.ExceptionString); return Ok(countResult.Result); } /* /// /// import tajik from sqlite /// /// /// [HttpPost] [Route("tajik/sqlite/import/{poetId}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ImportOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task TajikImportFromSqlite(int poetId = 0) { IFormFile file = Request.Form.Files[0]; RServiceResult res = await _ganjoorService.TajikImportFromSqlite(poetId, file); if (res.Result) return Ok(); return BadRequest(res.ExceptionString); }*/ [HttpPut] [Route("tajik/sqlite/onetime/import")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ImportOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult TajikOneTimeImportFromSqlite() { RServiceResult res = _ganjoorService.TajikImportFromSqlite(0, "C:\\Tools\\import\\output.s3db"); if (res.Result) return Ok(); return BadRequest(res.ExceptionString); } /// /// re-runs the Tajik poet/category HTML generators for every already-imported poet and /// category page, overwriting their stored TajikHtmlText. Needed one-time after changing /// either generator function (e.g. adding poem excerpts to the table of contents), since /// the normal SQLite import skips pages that already exist and never refreshes them. /// Runs as a background job - check the Admin area's LongRunningJobs page for progress. /// /// [HttpPut] [Route("tajik/regenerate-html")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ImportOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult RegenerateTajikCatAndPoetHtmlTextAsync() { RServiceResult res = _ganjoorService.RegenerateTajikCatAndPoetHtmlTextAsync(); if (res.Result) return Ok(); return BadRequest(res.ExceptionString); } /// /// recovery job: regenerates TajikHtmlText for Tajik poems whose stored HTML currently /// contains a link (the signature left by the CatPage id corruption bug) - use after /// RegenerateTajikCatAndPoetHtmlText (or anything else) may have corrupted poem pages. /// Check the Admin area's LongRunningJobs page for progress. /// /// [HttpPut] [Route("tajik/restore-poem-html")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + RMuseumSecurableItem.ImportOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult RestoreTajikPoemHtmlTextAsync() { RServiceResult res = _ganjoorService.RestoreTajikPoemHtmlTextAsync(); if (res.Result) return Ok(); return BadRequest(res.ExceptionString); } /// /// tajik poets /// /// [HttpGet] [Route("tajik/poets")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorTajikPoet[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetTajikPoetsAsync() { var cacheKey = $"ganjoor/tajik/poets"; if (!_memoryCache.TryGetValue(cacheKey, out GanjoorTajikPoet[] poets)) { RServiceResult res = await _ganjoorService.GetTajikPoetsAsync(); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); poets = res.Result; if (AggressiveCacheEnabled) _memoryCache.Set(cacheKey, poets, TimeSpan.FromHours(1)); } return Ok(poets); } /// /// tajik page by url /// /// /// /// [HttpGet] [Route("tajik/page")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorPageCompleteViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetTajikPageByUrlAsync(string url, bool catPoems = false) { RServiceResult res = await _ganjoorService.GetTajikPageByUrlAsync(url, catPoems); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (res.Result == null) return NotFound(); return Ok(res.Result); } /// /// search tajik poems /// /// /// /// /// /// [HttpGet] [Route("tajik/search")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task SearchTajikAsync([FromQuery] PagingParameterModel paging, string term, int poetId = 0, int catId = 0) { var pagedResult = await _ganjoorService.SearchTajikAsync(paging, term, poetId == 0 ? (int?)null : poetId, catId == 0 ? (int?)null : catId); if (!string.IsNullOrEmpty(pagedResult.ExceptionString)) return BadRequest(pagedResult.ExceptionString); // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(pagedResult.Result.PagingMeta)); return Ok(pagedResult.Result.Items); } /// /// fill couplet summaries using open ai /// /// /// /// [HttpPut("ai/generate/summaries")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult OpenAIStartFillingCoupletSummaries(int startFrom = 0, int count = 0) { _ganjoorService.OpenAIStartFillingCoupletSummaries(startFrom, count); return Ok(); } /// /// fill poem summaries using open ai /// /// /// /// [HttpPut("ai/generate/poem/summaries")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult OpenAIStartFillingPoemSummaries(int startFrom = 0, int count = 0) { _ganjoorService.OpenAIStartFillingPoemSummaries(startFrom, count); return Ok(); } /// /// geo tag poems using AI /// /// /// /// [HttpPut("ai/generate/poem/geo")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult OpenAIStartFillingGeoLocations(int startFrom = 0, int count = 0) { _ganjoorService.OpenAIStartFillingGeoLocations(startFrom, count); return Ok(); } /// /// send cat corrections /// /// /// [HttpPost] [Route("cat/correction")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorCatCorrectionViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task SuggestCatCorrectionAsync([FromBody] GanjoorCatCorrectionViewModel correction) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); correction.UserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.SuggestCatCorrectionAsync(correction); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (res.Result == null) return NotFound(); return Ok(res.Result); } /// /// delete unreviewed user cat corrections for a poem /// /// /// [HttpDelete] [Route("cat/correction/{id}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task DeleteCatCorrectionsAsync(int id) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); var userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); var res = await _ganjoorService.DeleteCatCorrectionsAsync(userId, id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// returns last unreviewed correction from the user for a cat /// /// /// [HttpGet] [Route("cat/correction/last/{id}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorCatCorrectionViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetLastUnreviewedUserCorrectionForCatAsync(int id) { Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.GetLastUnreviewedUserCorrectionForCatAsync(userId, id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result);//might be null } /// /// get list of user suggested cat corrections /// /// /// [HttpGet] [Route("cat/corrections/mine")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetUserCatCorrectionsAsync([FromQuery] PagingParameterModel paging) { Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); var res = await _ganjoorService.GetUserCatCorrectionsAsync(userId, paging); // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(res.Result.PagingMeta)); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result.Items); } /// /// get list of all suggested cat corrections /// /// /// userId /// [HttpGet] [Route("cat/corrections/all")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetAllCatCorrectionsAsync([FromQuery] PagingParameterModel paging, Guid? userId = null) { var res = await _ganjoorService.GetUserCatCorrectionsAsync(userId ?? Guid.Empty, paging); // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(res.Result.PagingMeta)); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result.Items); } /// /// effective corrections for a cat /// /// /// /// [HttpGet] [Route("cat/{id}/corrections/effective")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetCatEffectiveCorrectionsAsync(int id, [FromQuery] PagingParameterModel paging) { var res = await _ganjoorService.GetCatEffectiveCorrectionsAsync(id, paging); // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(res.Result.PagingMeta)); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result.Items); } /// /// get cat correction by id /// /// /// [HttpGet] [Route("cat/correction/{id}")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorCatCorrectionViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetCatCorrectionByIdAsync(int id) { Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.GetCatCorrectionByIdAsync(id); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result);//might be null } /// /// get next unreviewed cat correction /// /// /// /// [HttpGet] [Route("cat/correction/next")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorCatCorrectionViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetNextUnreviewedCatCorrectionAsync(int skip = 0, bool onlyUserCorrections = false) { RServiceResult res = await _ganjoorService.GetNextUnreviewedCatCorrectionAsync(skip, onlyUserCorrections); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); var resCount = await _ganjoorService.GetUnreviewedCatCorrectionCountAsync(onlyUserCorrections); if (!string.IsNullOrEmpty(resCount.ExceptionString)) return BadRequest(resCount.ExceptionString); // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject( new PaginationMetadata() { totalCount = resCount.Result, pageSize = -1, currentPage = -1, hasNextPage = false, hasPreviousPage = false, totalPages = -1 }) ); return Ok(res.Result);//might be null } /// /// moderate cat correction /// /// /// [HttpPost] [Route("cat/correction/moderate")] [Authorize(Policy = RMuseumSecurableItem.GanjoorEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorCatCorrectionViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task ModerateCatCorrectionAsync([FromBody] GanjoorCatCorrectionViewModel correction) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); Guid userId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _ganjoorService.ModerateCatCorrectionAsync(userId, correction); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (res.Result == null) return NotFound(); return Ok(res.Result); } /// /// readonly mode /// public bool ReadOnlyMode { get { try { return bool.Parse(Configuration["ReadOnlyMode"]); } catch { return false; } } } /// /// Ganjoor Service /// protected readonly IGanjoorService _ganjoorService; /// /// IAppUserService instance /// protected IAppUserService _appUserService; /// /// for client IP resolution /// protected IHttpContextAccessor _httpContextAccessor; /// /// Image Service /// protected readonly IImageFileService _imageFileService; /// /// IMemoryCache /// protected readonly IMemoryCache _memoryCache; /// /// aggressive cache /// private bool AggressiveCacheEnabled { get { try { return bool.Parse(Configuration["AggressiveCacheEnabled"]); } catch { return false; } } } /// /// Configuration /// protected IConfiguration Configuration { get; } /// /// related people (family tree / person tagging) service - used here only for the /// category-scoped "cat/{id}/persongraph" endpoint; everything else about people lives in /// GanjoorRelatedPersonController /// protected readonly IGanjoorRelatedPersonService _personService; /// /// constructor /// /// /// /// /// /// /// /// public GanjoorController( IGanjoorService ganjoorService, IAppUserService appUserService, IHttpContextAccessor httpContextAccessor, IImageFileService imageFileService, IMemoryCache memoryCache, IConfiguration configuration, IGanjoorRelatedPersonService personService ) { _ganjoorService = ganjoorService; _appUserService = appUserService; _httpContextAccessor = httpContextAccessor; _imageFileService = imageFileService; _memoryCache = memoryCache; Configuration = configuration; _personService = personService; } } }