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.Artifact; using RMuseum.Models.Artifact.ViewModels; using RMuseum.Models.Auth.Memory; using RMuseum.Models.Bookmark; using RMuseum.Models.Bookmark.ViewModels; using RMuseum.Models.GanjoorIntegration; using RMuseum.Models.GanjoorIntegration.ViewModels; using RMuseum.Models.ImportJob; using RMuseum.Models.Note; using RMuseum.Models.Note.ViewModels; using RMuseum.Services; using RSecurityBackend.Models.Auth.Memory; using RSecurityBackend.Models.Generic; using RSecurityBackend.Services; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading.Tasks; namespace RMuseum.Controllers { [Produces("application/json")] [Route("api/artifacts")] public class ArtifactController : Controller { /// /// HTTP dates only have one-second precision - truncate before using a value for /// Last-Modified/If-Modified-Since comparisons, or a DB value with sub-second precision /// would always fail the >= comparison against a client's necessarily-truncated header /// /// /// private static DateTime TruncateToSeconds(DateTime dt) { return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second, dt.Kind); } /// /// gets specified publish artifact item info (including images + attributes) /// /// /// /// [HttpGet("{artifactUrl}/{itemUrl}")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RArtifactItemRecordViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetArtifactItemByFrienlyUrl(string artifactUrl, string itemUrl) { RServiceResult itemInfo = await _artifactService.GetArtifactItemByFrienlyUrl(artifactUrl, itemUrl, new PublishStatus[] { PublishStatus.Published }); if (!string.IsNullOrEmpty(itemInfo.ExceptionString)) { return BadRequest(itemInfo.ExceptionString); } if (itemInfo.Result == null) return NotFound(); Response.GetTypedHeaders().LastModified = TruncateToSeconds(itemInfo.Result.Item.LastModified); Response.Headers.CacheControl = "public,max-age=300"; var requestHeaders = Request.GetTypedHeaders(); if (requestHeaders.IfModifiedSince.HasValue && requestHeaders.IfModifiedSince.Value >= TruncateToSeconds(itemInfo.Result.Item.LastModified)) { return StatusCode(StatusCodes.Status304NotModified); } return Ok(itemInfo.Result); } /// /// get all published artifacts (including CoverImage info but not items info) /// /// /// [HttpGet] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task Get([FromQuery] PagingParameterModel paging) { var cacheKey = $"artifacts/all/{paging.PageSize}/{paging.PageNumber}"; if (!_memoryCache.TryGetValue(cacheKey, out RServiceResult<(PaginationMetadata PagingMeta, RArtifactMasterRecord[] Items)> itemsInfo)) { itemsInfo = await _artifactService.GetAll(paging, new PublishStatus[] { PublishStatus.Published }); if (!string.IsNullOrEmpty(itemsInfo.ExceptionString)) { return BadRequest(itemsInfo.ExceptionString); } _memoryCache.Set(cacheKey, itemsInfo, TimeSpan.FromHours(1)); } if (itemsInfo.Result.Items.Count() > 0) { DateTime lastModification = itemsInfo.Result.Items.Max(i => i.LastModified); Response.GetTypedHeaders().LastModified = TruncateToSeconds(lastModification); Response.Headers.CacheControl = "public,max-age=300"; var requestHeaders = Request.GetTypedHeaders(); if (requestHeaders.IfModifiedSince.HasValue && requestHeaders.IfModifiedSince.Value >= TruncateToSeconds(lastModification)) { return StatusCode(StatusCodes.Status304NotModified); } } // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(itemsInfo.Result.PagingMeta)); return Ok(itemsInfo.Result.Items); } /// /// get list of artifact statuses user can see /// /// /// /// private async Task> _GetUserVisibleArtifactStatusSet(Guid loggedOnUserId, Guid sessionId) { RServiceResult canView = await _userPermissionChecker.Check ( loggedOnUserId, sessionId, User.Claims.Any(c => c.Type == "Language") ? User.Claims.First(c => c.Type == "Language").Value : "fa-IR", RMuseumSecurableItem.ArtifactEntityShortName, RMuseumSecurableItem.ViewDraftOperationShortName ); if (!string.IsNullOrEmpty(canView.ExceptionString)) return new RServiceResult(null, canView.ExceptionString); PublishStatus[] visibleItems = canView.Result ? new PublishStatus[] { PublishStatus.Published, PublishStatus.Restricted, PublishStatus.Draft, PublishStatus.Awaiting } : new PublishStatus[] { PublishStatus.Published }; return new RServiceResult(visibleItems); } /// /// get all artifacts visible by user (including CoverImage info but not items info) /// /// /// [HttpGet] [Route("secure")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetUserVisible([FromQuery] PagingParameterModel paging) { RServiceResult v = await _GetUserVisibleArtifactStatusSet ( new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value), new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value) ); if (!string.IsNullOrEmpty(v.ExceptionString)) return BadRequest(v.ExceptionString); PublishStatus[] visibleItems = v.Result; if (visibleItems.Length == 1 && visibleItems[0] == PublishStatus.Published) //Caching { return await Get(paging); } RServiceResult<(PaginationMetadata PagingMeta, RArtifactMasterRecord[] Items)> itemsInfo = await _artifactService.GetAll(paging, visibleItems); if (!string.IsNullOrEmpty(itemsInfo.ExceptionString)) { return BadRequest(itemsInfo.ExceptionString); } if (itemsInfo.Result.Items.Count() > 0) { DateTime lastModification = itemsInfo.Result.Items.Max(i => i.LastModified); Response.GetTypedHeaders().LastModified = TruncateToSeconds(lastModification); Response.Headers.CacheControl = "private,max-age=300"; var requestHeaders = Request.GetTypedHeaders(); if (requestHeaders.IfModifiedSince.HasValue && requestHeaders.IfModifiedSince.Value >= TruncateToSeconds(lastModification)) { return StatusCode(StatusCodes.Status304NotModified); } } // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(itemsInfo.Result.PagingMeta)); return Ok(itemsInfo.Result.Items); } [HttpGet("tagged/{tagUrl}/{valueUrl}")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetByTagValue(string tagUrl, string valueUrl) { RServiceResult itemsInfo = await _artifactService.GetByTagValue(tagUrl, valueUrl, new PublishStatus[] { PublishStatus.Published }); if (!string.IsNullOrEmpty(itemsInfo.ExceptionString)) { return BadRequest(itemsInfo.ExceptionString); } if (itemsInfo.Result.Length > 0) { DateTime lastModification = itemsInfo.Result.Max(i => i.LastModified); Response.GetTypedHeaders().LastModified = TruncateToSeconds(lastModification); Response.Headers.CacheControl = "public,max-age=300"; var requestHeaders = Request.GetTypedHeaders(); if (requestHeaders.IfModifiedSince.HasValue && requestHeaders.IfModifiedSince.Value >= TruncateToSeconds(lastModification)) { return StatusCode(StatusCodes.Status304NotModified); } } return Ok(itemsInfo.Result); } /// /// gets specified publish artifact info (including CoverImage + images) /// /// /// [HttpGet("{friendlyUrl}")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RArtifactMasterRecordViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task Get(string friendlyUrl) { var cacheKey = $"artifacts/friendlyUrl/{friendlyUrl}"; if (!_memoryCache.TryGetValue(cacheKey, out RServiceResult itemInfo)) { itemInfo = await _artifactService.GetByFriendlyUrl(friendlyUrl, new PublishStatus[] { PublishStatus.Published }); if (!string.IsNullOrEmpty(itemInfo.ExceptionString)) { return BadRequest(itemInfo.ExceptionString); } if (itemInfo.Result == null) return NotFound(); _memoryCache.Set(cacheKey, itemInfo, TimeSpan.FromHours(1)); } Response.GetTypedHeaders().LastModified = TruncateToSeconds(itemInfo.Result.LastModified); Response.Headers.CacheControl = "public,max-age=300"; var requestHeaders = Request.GetTypedHeaders(); if (requestHeaders.IfModifiedSince.HasValue && requestHeaders.IfModifiedSince.Value >= TruncateToSeconds(itemInfo.Result.LastModified)) { return StatusCode(StatusCodes.Status304NotModified); } return Ok(itemInfo.Result); } /// /// get specific artifact info with limited number of images /// /// /// /// [HttpGet("limited/{friendlyUrl}/{count}")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RArtifactMasterRecordViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetByFriendlyUrlLimitedItemsAsync(string friendlyUrl, int count = 21) { var cacheKey = $"artifacts/friendlyUrl/limited/{friendlyUrl}/{count}"; if (!_memoryCache.TryGetValue(cacheKey, out RServiceResult itemInfo)) { itemInfo = await _artifactService.GetByFriendlyUrlLimitedItemsAsync(friendlyUrl, [PublishStatus.Published], count); if (!string.IsNullOrEmpty(itemInfo.ExceptionString)) { return BadRequest(itemInfo.ExceptionString); } if (itemInfo.Result == null) return NotFound(); _memoryCache.Set(cacheKey, itemInfo, TimeSpan.FromHours(1)); } Response.GetTypedHeaders().LastModified = TruncateToSeconds(itemInfo.Result.LastModified); Response.Headers.CacheControl = "public,max-age=300"; var requestHeaders = Request.GetTypedHeaders(); if (requestHeaders.IfModifiedSince.HasValue && requestHeaders.IfModifiedSince.Value >= TruncateToSeconds(itemInfo.Result.LastModified)) { return StatusCode(StatusCodes.Status304NotModified); } return Ok(itemInfo.Result); } /// /// get artifact images according to start and count params /// /// /// /// /// [HttpGet("itemsof/{id}/{start}/{count}")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RArtifactMasterRecordViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetArtifactItemsAsync(Guid id, int start, int count = 21) { var result = await _artifactService.GetArtifactItemsAsync(id, start, count); if(!string.IsNullOrEmpty(result.ExceptionString)) return BadRequest(result.ExceptionString); return Ok(result.Result); } /// /// remove unpublished artifact having no notes and not bookmarked /// /// /// [HttpDelete("{artifactId}")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + SecurableItem.DeleteOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task RemoveArtifact(Guid artifactId) { RServiceResult res = await _artifactService.RemoveArtifact(artifactId, true); if (!string.IsNullOrEmpty(res.ExceptionString)) { return BadRequest(res.ExceptionString); } return Ok(res.Result); } /// /// gets specified publish artifact info (including CoverImage + images) where its items are filteted by tag /// /// /// /// [HttpGet("{friendlyUrl}/filteritemsbytag/{tagFriendlyUrl}")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RArtifactMasterRecordViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetByFriendlyUrlFilterItemsByTag(string friendlyUrl, string tagFriendlyUrl) { return await GetByFriendlyUrlFilterItemsByTag(friendlyUrl, tagFriendlyUrl, null); } /// /// gets specified publish artifact info (including CoverImage + images) where its items are filteted by tag /// /// /// /// /// [HttpGet("{friendlyUrl}/filteritemsbytag/{tagFriendlyUrl}/{valueFriendlyUrl}")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RArtifactMasterRecordViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetByFriendlyUrlFilterItemsByTag(string friendlyUrl, string tagFriendlyUrl, string valueFriendlyUrl) { var cacheKey = $"{friendlyUrl}/filteritemsbytag/{tagFriendlyUrl}"; if (valueFriendlyUrl != null) { cacheKey += $"/{valueFriendlyUrl}"; } if (!_memoryCache.TryGetValue(cacheKey, out RServiceResult itemInfo)) { itemInfo = await _artifactService.GetByFriendlyUrlFilterItemsByTag(friendlyUrl, new PublishStatus[] { PublishStatus.Published }, tagFriendlyUrl, valueFriendlyUrl); if (!string.IsNullOrEmpty(itemInfo.ExceptionString)) { return BadRequest(itemInfo.ExceptionString); } if (itemInfo.Result == null) return NotFound(); _memoryCache.Set(cacheKey, itemInfo, TimeSpan.FromHours(1)); } Response.GetTypedHeaders().LastModified = TruncateToSeconds(itemInfo.Result.LastModified); Response.Headers.CacheControl = "public,max-age=300"; var requestHeaders = Request.GetTypedHeaders(); if (requestHeaders.IfModifiedSince.HasValue && requestHeaders.IfModifiedSince.Value >= TruncateToSeconds(itemInfo.Result.LastModified)) { return StatusCode(StatusCodes.Status304NotModified); } return Ok(itemInfo.Result); } /// /// gets specified publish artifact info (including CoverImage + images) /// /// /// [HttpGet("secure/{friendlyUrl}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RArtifactMasterRecordViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetUserVisible(string friendlyUrl) { RServiceResult v = await _GetUserVisibleArtifactStatusSet ( new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value), new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value) ); if (!string.IsNullOrEmpty(v.ExceptionString)) return BadRequest(v.ExceptionString); PublishStatus[] visibleItems = v.Result; RServiceResult itemInfo = null; if (visibleItems.Length == 1 && visibleItems[0] == PublishStatus.Published) { var cacheKey = $"artifacts/friendlyUrl/{friendlyUrl}"; if (!_memoryCache.TryGetValue(cacheKey, out itemInfo)) { itemInfo = await _artifactService.GetByFriendlyUrl(friendlyUrl, new PublishStatus[] { PublishStatus.Published }); if (!string.IsNullOrEmpty(itemInfo.ExceptionString)) { return BadRequest(itemInfo.ExceptionString); } if (itemInfo.Result == null) return NotFound(); _memoryCache.Set(cacheKey, itemInfo, TimeSpan.FromHours(1)); } } if (itemInfo == null) { itemInfo = await _artifactService.GetByFriendlyUrl(friendlyUrl, visibleItems); } if (!string.IsNullOrEmpty(itemInfo.ExceptionString)) { return BadRequest(itemInfo.ExceptionString); } if (itemInfo.Result == null) return NotFound(); Response.GetTypedHeaders().LastModified = TruncateToSeconds(itemInfo.Result.LastModified); Response.Headers.CacheControl = "private,max-age=300"; var requestHeaders = Request.GetTypedHeaders(); if (requestHeaders.IfModifiedSince.HasValue && requestHeaders.IfModifiedSince.Value >= TruncateToSeconds(itemInfo.Result.LastModified)) { return StatusCode(StatusCodes.Status304NotModified); } return Ok(itemInfo.Result); } /// /// gets specified publish artifact info (including CoverImage + images) /// /// /// /// [HttpGet("secure/limited/{friendlyUrl}/{count}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RArtifactMasterRecordViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetUserVisibleLimitedItemsAsync(string friendlyUrl, int count = 21) { RServiceResult v = await _GetUserVisibleArtifactStatusSet ( new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value), new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value) ); if (!string.IsNullOrEmpty(v.ExceptionString)) return BadRequest(v.ExceptionString); PublishStatus[] visibleItems = v.Result; RServiceResult itemInfo = null; if (visibleItems.Length == 1 && visibleItems[0] == PublishStatus.Published) { var cacheKey = $"artifacts/friendlyUrl/limited/{friendlyUrl}/{count}"; if (!_memoryCache.TryGetValue(cacheKey, out itemInfo)) { itemInfo = await _artifactService.GetByFriendlyUrlLimitedItemsAsync(friendlyUrl, [PublishStatus.Published], count); if (!string.IsNullOrEmpty(itemInfo.ExceptionString)) { return BadRequest(itemInfo.ExceptionString); } if (itemInfo.Result == null) return NotFound(); _memoryCache.Set(cacheKey, itemInfo, TimeSpan.FromHours(1)); } } if (itemInfo == null) { itemInfo = await _artifactService.GetByFriendlyUrlLimitedItemsAsync(friendlyUrl, visibleItems, count); } if (!string.IsNullOrEmpty(itemInfo.ExceptionString)) { return BadRequest(itemInfo.ExceptionString); } if (itemInfo.Result == null) return NotFound(); Response.GetTypedHeaders().LastModified = TruncateToSeconds(itemInfo.Result.LastModified); Response.Headers.CacheControl = "private,max-age=300"; var requestHeaders = Request.GetTypedHeaders(); if (requestHeaders.IfModifiedSince.HasValue && requestHeaders.IfModifiedSince.Value >= TruncateToSeconds(itemInfo.Result.LastModified)) { return StatusCode(StatusCodes.Status304NotModified); } return Ok(itemInfo.Result); } /// /// gets specified publish artifact info (including CoverImage + images) where its items are filteted by tag /// /// /// /// [HttpGet("secure/{friendlyUrl}/filteritemsbytag/{tagFriendlyUrl}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RArtifactMasterRecordViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetUserVisibleByFriendlyUrlFilterItemsByTag(string friendlyUrl, string tagFriendlyUrl) { return await GetUserVisibleByFriendlyUrlFilterItemsByTag(friendlyUrl, tagFriendlyUrl, null); } /// /// gets specified publish artifact info (including CoverImage + images) where its items are filteted by tag /// /// /// /// /// [HttpGet("secure/{friendlyUrl}/filteritemsbytag/{tagFriendlyUrl}/{valueFriendlyUrl}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RArtifactMasterRecordViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetUserVisibleByFriendlyUrlFilterItemsByTag(string friendlyUrl, string tagFriendlyUrl, string valueFriendlyUrl) { RServiceResult v = await _GetUserVisibleArtifactStatusSet ( new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value), new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value) ); if (!string.IsNullOrEmpty(v.ExceptionString)) return BadRequest(v.ExceptionString); PublishStatus[] visibleItems = v.Result; if (visibleItems.Length == 1 && visibleItems[0] == PublishStatus.Published) { return await GetByFriendlyUrlFilterItemsByTag(friendlyUrl, tagFriendlyUrl, valueFriendlyUrl); } RServiceResult itemInfo = await _artifactService.GetByFriendlyUrlFilterItemsByTag(friendlyUrl, visibleItems, tagFriendlyUrl, valueFriendlyUrl); if (!string.IsNullOrEmpty(itemInfo.ExceptionString)) { return BadRequest(itemInfo.ExceptionString); } if (itemInfo.Result == null) return NotFound(); Response.GetTypedHeaders().LastModified = TruncateToSeconds(itemInfo.Result.LastModified); Response.Headers.CacheControl = "private,max-age=300"; var requestHeaders = Request.GetTypedHeaders(); if (requestHeaders.IfModifiedSince.HasValue && requestHeaders.IfModifiedSince.Value >= TruncateToSeconds(itemInfo.Result.LastModified)) { return StatusCode(StatusCodes.Status304NotModified); } return Ok(itemInfo.Result); } /// /// edit artifactt master record (user should have additional permissions artifact:awaiting and artifact:publish to change status of artifact) /// /// /// editing related collections such as images and attributed or complex properties such as CoverImage is ignored /// /// /// /// [HttpPut("{id}")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task Put(Guid id, [FromBody] RArtifactMasterRecord item) { if (id != item.Id) { return BadRequest(); } Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); Guid sessionId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value); RServiceResult canChangeStatusToAwaiting = await _userPermissionChecker.Check ( loggedOnUserId, sessionId, User.Claims.Any(c => c.Type == "Language") ? User.Claims.First(c => c.Type == "Language").Value : "fa-IR", RMuseumSecurableItem.ArtifactEntityShortName, RMuseumSecurableItem.ToAwaitingStatusOperationShortName ); if (!string.IsNullOrEmpty(canChangeStatusToAwaiting.ExceptionString)) return BadRequest(canChangeStatusToAwaiting.ExceptionString); RServiceResult canPublish = await _userPermissionChecker.Check ( loggedOnUserId, sessionId, User.Claims.Any(c => c.Type == "Language") ? User.Claims.First(c => c.Type == "Language").Value : "fa-IR", RMuseumSecurableItem.ArtifactEntityShortName, RMuseumSecurableItem.PublishOperationShortName ); if (!string.IsNullOrEmpty(canPublish.ExceptionString)) return BadRequest(canPublish.ExceptionString); RServiceResult itemInfo = await _artifactService.EditMasterRecord(item, canChangeStatusToAwaiting.Result, canPublish.Result); if (!string.IsNullOrEmpty(itemInfo.ExceptionString)) { return BadRequest(itemInfo.ExceptionString); } if (itemInfo == null) { return NotFound(); } return Ok(); ; } /// /// Set Artifact Cover Item Index /// /// /// /// [HttpPut("{id}/cover")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task SetArtifactCoverItemIndex(Guid id, [FromBody] int itemIndex) { RServiceResult res = await _artifactService.SetArtifactCoverItemIndex(id, itemIndex); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(); return Ok(); } /// /// get tag bundle by frindly url /// /// /// [HttpGet("tagbundle/{friendlyUrl}")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RTagBundleViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetTagBundleByFiendlyUrl(string friendlyUrl) { var cacheKey = $"tagbundle/{friendlyUrl}"; if (!_memoryCache.TryGetValue(cacheKey, out RServiceResult tag)) { tag = await _artifactService.GetTagBundleByFiendlyUrl(friendlyUrl); if (!string.IsNullOrEmpty(tag.ExceptionString)) { return BadRequest(tag.ExceptionString); } if (tag.Result == null) return NotFound(); _memoryCache.Set(cacheKey, tag, TimeSpan.FromHours(1)); } RServiceResult lastModified = await _artifactService.GetMaxArtifactLastModified(); if (!string.IsNullOrEmpty(lastModified.ExceptionString)) { return BadRequest(lastModified.ExceptionString); } DateTime lastModification = lastModified.Result; Response.GetTypedHeaders().LastModified = TruncateToSeconds(lastModification); Response.Headers.CacheControl = "public,max-age=300"; var requestHeaders = Request.GetTypedHeaders(); if (requestHeaders.IfModifiedSince.HasValue && requestHeaders.IfModifiedSince.Value >= TruncateToSeconds(lastModification)) { return StatusCode(StatusCodes.Status304NotModified); } return Ok(tag.Result); } /// /// add new tag /// /// only name is processed /// [HttpPost("tag")] [Authorize(Policy = RMuseumSecurableItem.TagEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RTag))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task NewTag([FromBody] RTag tag) { RServiceResult res = await _artifactService.AddTag(tag.Name); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// get all tags /// /// /// [HttpGet("tag")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetTags([FromQuery] PagingParameterModel paging) { RServiceResult<(PaginationMetadata PagingMeta, RTag[] Items)> itemsInfo = await _artifactService.GetAllTags(paging); if (!string.IsNullOrEmpty(itemsInfo.ExceptionString)) { return BadRequest(itemsInfo.ExceptionString); } // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(itemsInfo.Result.PagingMeta)); return Ok(itemsInfo.Result.Items); } [HttpGet("tag/{friendlyUrl}")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RTag))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetTagByFriendlyUrl(string friendlyUrl) { RServiceResult tag = await _artifactService.GetTagByFriendlyUrl(friendlyUrl); if (!string.IsNullOrEmpty(tag.ExceptionString)) { return BadRequest(tag.ExceptionString); } return Ok(tag.Result); } /// /// edit tag /// /// /// editable fields are limited /// /// /// /// [HttpPut("tag/{tagid}")] [Authorize(Policy = RMuseumSecurableItem.TagEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task PutTag(Guid tagid, [FromBody] RTag tag) { if (tagid != tag.Id) { return BadRequest(); } RServiceResult itemInfo = await _artifactService.EditTag(tag); if (!string.IsNullOrEmpty(itemInfo.ExceptionString)) { return BadRequest(itemInfo.ExceptionString); } if (itemInfo == null) { return NotFound(); } return Ok(); ; } /// /// changes order of tags based on their position in artifacts /// /// /// /// up / down /// the other tag which its Order got replaced with the input id [HttpPut("tag/move/{tagid}/in/{artifactId}/{upordown}")] [Authorize(Policy = RMuseumSecurableItem.TagEntityShortName + ":" + RMuseumSecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(Guid))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task MoveTag(Guid tagId, Guid artifactId, string upordown) { RServiceResult otherTagId = await _artifactService.EditTagOrderBasedOnArtifact(tagId, artifactId, upordown == "up"); if (!string.IsNullOrEmpty(otherTagId.ExceptionString)) { return BadRequest(otherTagId.ExceptionString); } return Ok((Guid)otherTagId.Result); } /// /// changes order of tags based on their position in artifact items /// /// /// /// up / down /// the other tag which its Order got replaced with the input id [HttpPut("tag/move/{tagid}/in/item/{itemId}/{upordown}")] [Authorize(Policy = RMuseumSecurableItem.TagEntityShortName + ":" + RMuseumSecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(Guid))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task MoveTagInItem(Guid tagId, Guid itemId, string upordown) { RServiceResult otherTagId = await _artifactService.EditTagOrderBasedOnItem(tagId, itemId, upordown == "up"); if (!string.IsNullOrEmpty(otherTagId.ExceptionString)) { return BadRequest(otherTagId.ExceptionString); } return Ok((Guid)otherTagId.Result); } /// /// get tag value bundle by frindly url /// /// /// /// [HttpGet("tagbundle/{friendlyUrl}/{valueUrl}")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RArtifactTagViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetTagValueBundleByFiendlyUrl(string friendlyUrl, string valueUrl) { RServiceResult tag = await _artifactService.GetTagValueBundleByFiendlyUrl(friendlyUrl, valueUrl); if (!string.IsNullOrEmpty(tag.ExceptionString)) { return BadRequest(tag.ExceptionString); } if (tag.Result == null) return NotFound(); return Ok(tag.Result); } /// /// get tag value by frindly url /// /// /// /// [HttpGet("tagvalue/{tagId}/{friendlyUrl}")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RTagValue))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetTagValueByFriendlyUrl(Guid tagId, string friendlyUrl) { RServiceResult tag = await _artifactService.GetTagValueByFriendlyUrl(tagId, friendlyUrl); if (!string.IsNullOrEmpty(tag.ExceptionString)) { return BadRequest(tag.ExceptionString); } return Ok(tag.Result); } /// /// add new tag value to artifact /// /// /// only name is processed /// [HttpPost("tagvalue/{artifactid}")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + RMuseumSecurableItem.EditTagValueOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RTagValue))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task NewArtifactTagValue(Guid artifactId, [FromBody] RTag tag) { RServiceResult res = await _artifactService.TagArtifact(artifactId, tag); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// edit artifact attribute value /// /// /// editable fields are limited /// /// /// /// apply on all same value tags /// [HttpPut("tagvalue/{artifactid}/{global=true}")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + RMuseumSecurableItem.EditTagValueOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task PutAttributeValue(Guid artifactid, bool global, [FromBody] RTagValue tagvalue) { RServiceResult itemInfo = await _artifactService.EditTagValue(artifactid, tagvalue, global); if (!string.IsNullOrEmpty(itemInfo.ExceptionString)) { return BadRequest(itemInfo.ExceptionString); } if (itemInfo == null) { return NotFound(); } return Ok(); ; } /// /// remove tag from artifact /// /// /// /// [HttpDelete("tagvalue/{artifactId}/{tagValueId}")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + RMuseumSecurableItem.EditTagValueOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task RemoveArtifactTagValue(Guid artifactId, Guid tagValueId) { RServiceResult res = await _artifactService.UnTagArtifact(artifactId, tagValueId); if (!string.IsNullOrEmpty(res.ExceptionString)) { return BadRequest(res.ExceptionString); } return Ok(res.Result); } /// /// add new tag value to item /// /// /// only tag.id is processed /// [HttpPost("itemtagvalue/{itemId}")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + RMuseumSecurableItem.EditTagValueOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RTagValue))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task NewItemTagValue(Guid itemId, [FromBody] RTag tag) { RServiceResult res = await _artifactService.TagItem(itemId, tag); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// edit item attribute value /// /// /// editable fields are limited /// /// /// /// apply on all same value tags /// [HttpPut("itemtagvalue/{itemId}/{global=true}")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + RMuseumSecurableItem.EditTagValueOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task PutItemTagValue(Guid itemId, bool global, [FromBody] RTagValue tagvalue) { RServiceResult itemInfo = await _artifactService.EditItemTagValue(itemId, tagvalue, global); if (!string.IsNullOrEmpty(itemInfo.ExceptionString)) { return BadRequest(itemInfo.ExceptionString); } if (itemInfo == null) { return NotFound(); } return Ok(); ; } /// /// remove tag from item /// /// /// /// [HttpDelete("itemtagvalue/{itemId}/{tagValueId}")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + RMuseumSecurableItem.EditTagValueOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task RemoveItemTagValue(Guid itemId, Guid tagValueId) { RServiceResult res = await _artifactService.UnTagItem(itemId, tagValueId); if (!string.IsNullOrEmpty(res.ExceptionString)) { return BadRequest(res.ExceptionString); } return Ok(res.Result); } /// /// changes order of tag values based on their position in an artifact /// /// /// /// /// up / down /// the other tag which its Order got replaced with the input id [HttpPut("tagvalue/{artifactid}/{tagId}/{valueId}/move/{upordown}")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + RMuseumSecurableItem.EditTagValueOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(Guid))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task MoveTagValue(Guid artifactId, Guid tagId, Guid valueId, string upordown) { RServiceResult otherValueId = await _artifactService.EditTagValueOrder(artifactId, tagId, valueId, upordown == "up"); if (!string.IsNullOrEmpty(otherValueId.ExceptionString)) { return BadRequest(otherValueId.ExceptionString); } return Ok((Guid)otherValueId.Result); } /// /// changes order of tag values based on their position in an item /// /// /// /// /// up / down /// the other tag which its Order got replaced with the input id [HttpPut("itemtagvalue/{itemId}/{tagId}/{valueId}/move/{upordown}")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + RMuseumSecurableItem.EditTagValueOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(Guid))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task MoveItemTagValue(Guid itemId, Guid tagId, Guid valueId, string upordown) { RServiceResult otherValueId = await _artifactService.EditItemTagValueOrder(itemId, tagId, valueId, upordown == "up"); if (!string.IsNullOrEmpty(otherValueId.ExceptionString)) { return BadRequest(otherValueId.ExceptionString); } return Ok((Guid)otherValueId.Result); } /// /// update an artifact item info /// /// /// [HttpPut("item")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task UpdateArtifactItemRecordAsync([FromBody]RArtifactItemRecord item) { var res = await _artifactService.UpdateArtifactItemRecordAsync(item); if(!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// gets specified publish artifact item info (including images + attributes) /// /// /// /// [HttpGet("secure/{artifactUrl}/{itemUrl}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RArtifactItemRecordViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetArtifactItemByFrienlyUrlUserVisible(string artifactUrl, string itemUrl) { RServiceResult v = await _GetUserVisibleArtifactStatusSet ( new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value), new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value) ); if (!string.IsNullOrEmpty(v.ExceptionString)) return BadRequest(v.ExceptionString); PublishStatus[] visibleItems = v.Result; RServiceResult itemInfo = await _artifactService.GetArtifactItemByFrienlyUrl(artifactUrl, itemUrl, visibleItems); if (!string.IsNullOrEmpty(itemInfo.ExceptionString)) { return BadRequest(itemInfo.ExceptionString); } if (itemInfo.Result == null) return NotFound(); Response.GetTypedHeaders().LastModified = TruncateToSeconds(itemInfo.Result.Item.LastModified); Response.Headers.CacheControl = "private,max-age=300"; var requestHeaders = Request.GetTypedHeaders(); if (requestHeaders.IfModifiedSince.HasValue && requestHeaders.IfModifiedSince.Value >= TruncateToSeconds(itemInfo.Result.Item.LastModified)) { return StatusCode(StatusCodes.Status304NotModified); } return Ok(itemInfo.Result); } /// /// Add new artifact (multipart/form-data) /// /// /// const data = new FormData(); /// data.append('name', 'تست'); /// data.append('description', ''); /// data.append('srcUrl', ''); /// data.append('picTitle', 'تست'); /// data.append('picDescription', ''); /// data.append('file', this.file); /// data.append('picSrcUrl', ''); /// /// /// /// [HttpPost] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + SecurableItem.AddOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RArtifactMasterRecord))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public virtual async Task Post() { try { NewArtifact newArtifactInfo = new NewArtifact() { Name = Request.Form["name"], Description = Request.Form["description"], SrcUrl = Request.Form["srcUrl"], PicTitle = Request.Form["picTitle"], PicDescription = Request.Form["picDescription"], File = Request.Form.Files[0], PicSrcUrl = Request.Form["picSrcUrl"] }; RServiceResult result = await _artifactService.Add ( newArtifactInfo.Name, newArtifactInfo.Description, newArtifactInfo.SrcUrl, newArtifactInfo.PicTitle, newArtifactInfo.PicDescription, newArtifactInfo.File, newArtifactInfo.PicSrcUrl, null); if (result.Result == null) return BadRequest(result.ExceptionString); return Ok(result.Result); } catch (Exception exp) { return BadRequest(exp.ToString()); } } /// /// import from external resources /// /// pdf/loc/princeton/harvard/qajarwomen/hathitrust/penn/cam/bl/folder/walters/cbl/append/iiif /// 119/foldername /// golestan-baysonghori/artifact id /// /// /// [HttpPost] [Route("import")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + RMuseumSecurableItem.ImportOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task Import(string srcType, string resourceNumber, string friendlyUrl, string resourcePrefix, bool skipUpload) { RServiceResult res = await _artifactService.Import(srcType, resourceNumber, friendlyUrl, resourcePrefix, skipUpload); if (res.Result) return Ok(); return BadRequest(res.ExceptionString); } /// /// keep alive (for import background service) /// /// true [HttpGet] [Route("keep-alive")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public IActionResult KeepAlive() { return Ok(true); } /// /// retry import /// /// [HttpPost] [Route("retryimport")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + RMuseumSecurableItem.ImportOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task RetryImport(JobType jobType = JobType.BritishLibrary, bool skipUpload = false) { RServiceResult res = await _artifactService.RescheduleJobs(jobType, skipUpload); if (res.Result) return Ok(); return BadRequest(res.ExceptionString); } /// /// due to a bug in loc json outputs some artifacts with more than 1000 pages were downloaded incompletely /// /// 123456 /// /// [HttpPost] [Route("reexamineimport")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + RMuseumSecurableItem.ImportOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(string[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task ReExamineLocDownloads(string pass, bool skipUpload) { if (pass != "123456") //this is a heavy processor consuming call, so prevent mistaken call of it from swagger ui return BadRequest("invalid password"); RServiceResult res = await _artifactService.ReExamineLocDownloads(skipUpload); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } [HttpGet] [Route("jobs")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + SecurableItem.AddOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetImportJobs([FromQuery] PagingParameterModel paging) { RServiceResult<(PaginationMetadata PagingMeta, ImportJob[] Items)> itemsInfo = await _artifactService.GetImportJobs(paging); if (!string.IsNullOrEmpty(itemsInfo.ExceptionString)) { return BadRequest(itemsInfo.ExceptionString); } // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(itemsInfo.Result.PagingMeta)); return Ok(itemsInfo.Result.Items); } /// /// bookmark artifact /// /// /// [HttpPost] [Route("bookmark/{artifactId}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RUserBookmark))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task BookmarkArtifact(Guid artifactId) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _artifactService.BookmarkArtifact(artifactId, loggedOnUserId, RBookmarkType.Bookmark); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// bookmark item /// /// /// [HttpPost] [Route("bookmark/item/{itemId}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RUserBookmark))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task BookmarkItem(Guid itemId) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _artifactService.BookmarkItem(itemId, loggedOnUserId, RBookmarkType.Bookmark); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// item bookmarks info /// /// /// [HttpDelete] [Route("bookmark/{bookmarkId}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task DeleteBookmark(Guid bookmarkId) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _artifactService.DeleteUserBookmark(bookmarkId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } /// /// fav artifact /// /// /// [HttpPost] [Route("fav/{artifactId}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RUserBookmark))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task FavArtifact(Guid artifactId) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _artifactService.BookmarkArtifact(artifactId, loggedOnUserId, RBookmarkType.Favorite); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// fav item /// /// /// [HttpPost] [Route("fav/item/{itemId}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RUserBookmark))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task FavItem(Guid itemId) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _artifactService.BookmarkItem(itemId, loggedOnUserId, RBookmarkType.Favorite); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// artifact bookmarks info /// /// /// [HttpGet] [Route("bookmark/{artifactId}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RUserBookmark[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetArtifactUserBookmarks(Guid artifactId) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _artifactService.GetArtifactUserBookmarks(artifactId, loggedOnUserId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// item bookmarks info /// /// /// [HttpGet] [Route("bookmark/item/{itemId}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RUserBookmark[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GeItemUserBookmarks(Guid itemId) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _artifactService.GeItemUserBookmarks(itemId, loggedOnUserId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// user bookmarks /// /// [HttpGet] [Route("bookmark")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RUserBookmark[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetUserBookmarks([FromQuery] PagingParameterModel paging) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult v = await _GetUserVisibleArtifactStatusSet ( loggedOnUserId, new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value) ); if (!string.IsNullOrEmpty(v.ExceptionString)) return BadRequest(v.ExceptionString); PublishStatus[] visibleItems = v.Result; RServiceResult<(PaginationMetadata PagingMeta, RUserBookmarkViewModel[] Bookmarks)> res = await _artifactService.GetBookmarks(paging, loggedOnUserId, RBookmarkType.Bookmark, visibleItems); 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); } /// /// user favorites /// /// [HttpGet] [Route("fav")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RUserBookmarkViewModel[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetUserFavorites([FromQuery] PagingParameterModel paging) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult v = await _GetUserVisibleArtifactStatusSet ( loggedOnUserId, new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value) ); if (!string.IsNullOrEmpty(v.ExceptionString)) return BadRequest(v.ExceptionString); PublishStatus[] visibleItems = v.Result; RServiceResult<(PaginationMetadata PagingMeta, RUserBookmarkViewModel[] Bookmarks)> res = await _artifactService.GetBookmarks(paging, loggedOnUserId, RBookmarkType.Favorite, visibleItems); 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); } /// /// add note for artifact /// /// /// [HttpPost] [Route("note")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RUserNoteViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task AddUserNoteToArtifact([FromBody] PostUserNote note) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _artifactService.AddUserNoteToArtifact(note.EntityId, loggedOnUserId, note.NoteType, note.Contents, note.ReferenceNoteId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// add note for artifact item /// /// /// [HttpPost] [Route("note/item")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RUserNoteViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task AddUserNoteToArtifactItem([FromBody] PostUserNote note) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _artifactService.AddUserNoteToArtifactItem(note.EntityId, loggedOnUserId, note.NoteType, note.Contents, note.ReferenceNoteId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// Edit User Notes /// /// a note can not be edited by a user other than its owner or another using having note:moderate permission /// /// only htmlContent is processed /// [HttpPut] [Route("note/{noteId}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RUserNoteViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task EditUserNote(Guid noteId, [FromBody] PostUserNote note) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult canModerate = await _userPermissionChecker.Check ( loggedOnUserId, new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value), User.Claims.Any(c => c.Type == "Language") ? User.Claims.First(c => c.Type == "Language").Value : "fa-IR", RMuseumSecurableItem.NoteEntityShortName, RMuseumSecurableItem.ModerateOperationShortName ); if (!string.IsNullOrEmpty(canModerate.ExceptionString)) return BadRequest(canModerate.ExceptionString); RServiceResult res = await _artifactService.EditUserNote(noteId, canModerate.Result ? (Guid?)null : loggedOnUserId, note.Contents); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// delete user notes /// /// /// 1. a note can not be deleted by a user other than its owner or another using having note:moderate permission /// 2. all notes which have refernce to deleting note (sent in reply to it) would be deleted irrelevant of their ownership /// /// /// list of notes deleted [HttpDelete] [Route("note/{noteId}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(Guid[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task DeleteUserNote(Guid noteId) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult canModerate = await _userPermissionChecker.Check ( loggedOnUserId, new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value), User.Claims.Any(c => c.Type == "Language") ? User.Claims.First(c => c.Type == "Language").Value : "fa-IR", RMuseumSecurableItem.NoteEntityShortName, RMuseumSecurableItem.ModerateOperationShortName ); if (!string.IsNullOrEmpty(canModerate.ExceptionString)) return BadRequest(canModerate.ExceptionString); RServiceResult res = await _artifactService.DeleteUserNote(noteId, canModerate.Result ? (Guid?)null : loggedOnUserId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// get private notes for artifact /// /// /// [HttpGet] [Route("note/private/{artifactId}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RUserNoteViewModel[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetArtifactUserNotes(Guid artifactId) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _artifactService.GetArtifactUserNotes(artifactId, loggedOnUserId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// get private notes for artifact /// /// /// [HttpGet] [Route("note/{artifactId}")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RUserNoteViewModel[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetArtifactPublicNotes(Guid artifactId) { RServiceResult res = await _artifactService.GetArtifactPublicNotes(artifactId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// get private notes for artifact item /// /// /// [HttpGet] [Route("note/item/private/{itemId}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RUserNoteViewModel[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetArtifactItemUserNotes(Guid itemId) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _artifactService.GetArtifactItemUserNotes(itemId, loggedOnUserId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// get public notes for artifact item /// /// /// [HttpGet] [Route("note/item/{itemId}")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RUserNoteViewModel[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetArtifactItemPublicNotes(Guid itemId) { RServiceResult res = await _artifactService.GetArtifactItemPublicNotes(itemId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// user public notes /// /// [HttpGet] [Route("note/public")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RUserNoteViewModel[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetUserPublicNotes([FromQuery] PagingParameterModel paging) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult v = await _GetUserVisibleArtifactStatusSet ( loggedOnUserId, new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value) ); if (!string.IsNullOrEmpty(v.ExceptionString)) return BadRequest(v.ExceptionString); PublishStatus[] visibleItems = v.Result; RServiceResult<(PaginationMetadata PagingMeta, RUserNoteViewModel[] Notes)> res = await _artifactService.GetUserNotes(loggedOnUserId, paging, RNoteType.Public, visibleItems); 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.Notes); } /// /// user public notes /// /// [HttpGet] [Route("note/private")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RUserNoteViewModel[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetUserPrivateNotes([FromQuery] PagingParameterModel paging) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult v = await _GetUserVisibleArtifactStatusSet ( loggedOnUserId, new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value) ); if (!string.IsNullOrEmpty(v.ExceptionString)) return BadRequest(v.ExceptionString); PublishStatus[] visibleItems = v.Result; RServiceResult<(PaginationMetadata PagingMeta, RUserNoteViewModel[] Notes)> res = await _artifactService.GetUserNotes(loggedOnUserId, paging, RNoteType.Private, visibleItems); 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.Notes); } /// /// all users public notes /// /// [HttpGet] [Route("note/all")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RUserNoteViewModel[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetAllPublicNotes([FromQuery] PagingParameterModel paging) { RServiceResult<(PaginationMetadata PagingMeta, RUserNoteViewModel[] Notes)> res = await _artifactService.GetAllPublicNotes(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.Notes); } /// /// report a public note /// /// /// id of saved report [HttpPost] [Route("note/report")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(Guid))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task ReportPublicNote([FromBody] PostRUserNoteAbuseReportViewModel 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 _artifactService.ReportPublicNote(userId, report.NoteId, report.ReasonText); if (!string.IsNullOrEmpty(res.ExceptionString)) { return BadRequest(res.ExceptionString); } return Ok(res.Result); } /// /// Get a list of reported notes /// /// /// [HttpGet] [Route("notes/reported")] [Authorize(Policy = RMuseumSecurableItem.NoteEntityShortName + ":" + RMuseumSecurableItem.ModerateOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetReportedPublicNotes([FromQuery] PagingParameterModel paging) { var notes = await _artifactService.GetReportedPublicNotes(paging); if (!string.IsNullOrEmpty(notes.ExceptionString)) { return BadRequest(notes.ExceptionString); } // Paging Header HttpContext.Response.Headers.Append("paging-headers", JsonConvert.SerializeObject(notes.Result.PagingMeta)); return Ok(notes.Result.Items); } /// /// delete a report for abuse in public user notes /// /// /// [HttpDelete] [Route("note/report/{id}")] [Authorize(Policy = RMuseumSecurableItem.NoteEntityShortName + ":" + RMuseumSecurableItem.ModerateOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(int))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task DeclinePublicNoteReport(Guid id) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); RServiceResult res = await _artifactService.DeclinePublicNoteReport(id); if (!string.IsNullOrEmpty(res.ExceptionString)) { return BadRequest(res.ExceptionString); } if (!res.Result) { return NotFound(); } return Ok(); } /// /// delete a reported user note (accept the complaint) /// /// /// [HttpDelete] [Route("note/reported/moderate/{reportid}")] [Authorize(Policy = RMuseumSecurableItem.NoteEntityShortName + ":" + RMuseumSecurableItem.ModerateOperationShortName)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task AcceptPublicNoteReport(Guid reportid) { if (ReadOnlyMode) return BadRequest("سایت به دلایل فنی مثل انتقال سرور موقتاً در حالت فقط خواندنی قرار دارد. لطفاً ساعاتی دیگر مجدداً تلاش کنید."); var res = await _artifactService.AcceptPublicNoteReport(reportid); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); if (!res.Result) return NotFound(); return Ok(); } /// /// suggest ganjoor link /// /// /// [HttpPost] [Route("ganjoor")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorLinkViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task SuggestGanjoorLink([FromBody] LinkSuggestion link) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult suggestion = await _artifactService.SuggestGanjoorLink(loggedOnUserId, link); if (!string.IsNullOrEmpty(suggestion.ExceptionString)) return BadRequest(suggestion.ExceptionString); return Ok(suggestion.Result); } /// /// get suggested ganjoor links /// /// /// /// [HttpGet] [Route("ganjoor")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorLinkViewModel[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetSuggestedLinks(ReviewResult status, bool notSynced) { RServiceResult res = await _artifactService.GetSuggestedLinks(status, notSynced); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// finds next unsynchronized suggested link with an aleady synched one from the artificat if exists, /// return value might be null or an array with length 1 or 2 (has paging-headers) /// /// has paging-headers /// /// return value might be null or an array with length 1 or 2 [HttpGet] [Route("ganjoor/nextunsychedimage")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(GanjoorLinkViewModel[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetNextUnsynchronizedSuggestedLinkWithAlreadySynchedOneForPoem(int skip) { RServiceResult res = await _artifactService.GetNextUnsynchronizedSuggestedLinkWithAlreadySynchedOneForPoem(skip); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); var resCount = await _artifactService.GetUnsynchronizedSuggestedLinksCount(); 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); } /// /// review suggested ganjoor link /// /// /// /// [HttpPut] [Route("ganjoor/review/{linkId}/{result}")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + RMuseumSecurableItem.ReviewGanjoorLinksOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task ReviewGanjoorLink(Guid linkId, ReviewResult result) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult suggestion = await _artifactService.ReviewSuggestedLink(linkId, loggedOnUserId, result); if (!string.IsNullOrEmpty(suggestion.ExceptionString)) return BadRequest(suggestion.ExceptionString); return Ok(); } /// /// mark suggested ganjoor link as synchronized /// /// /// display ogn page /// [HttpPut] [Route("ganjoor/sync/{linkId}/{displayOnPage}")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + RMuseumSecurableItem.ReviewGanjoorLinksOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task SynchronizeGanjoorLink(Guid linkId, bool displayOnPage) { RServiceResult suggestion = await _artifactService.SynchronizeSuggestedLink(linkId, displayOnPage); if (!string.IsNullOrEmpty(suggestion.ExceptionString)) return BadRequest(suggestion.ExceptionString); return Ok(); } /// /// remove ganjoor link /// /// /// /// [HttpDelete] [Route("ganjoor")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + RMuseumSecurableItem.ReviewGanjoorLinksOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task RemoveGanjoorLinkAsync(Guid linkId, bool removeItemLink) { RServiceResult suggestion = await _artifactService.RemoveGanjoorLinkAsync(linkId, removeItemLink); if (!string.IsNullOrEmpty(suggestion.ExceptionString)) return BadRequest(suggestion.ExceptionString); return Ok(); } /// ///toc / temporary one time api / to be removed /// /// [HttpPut] [Route("ganjoor/toc")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + RMuseumSecurableItem.ReviewGanjoorLinksOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task AddTOCForSuggestedLinks() { RServiceResult suggestion = await _artifactService.AddTOCForSuggestedLinks(); if (!string.IsNullOrEmpty(suggestion.ExceptionString)) return BadRequest(suggestion.ExceptionString); return Ok(suggestion.Result); } /// /// get suggested pinterest links /// /// /// /// [HttpGet] [Route("pinterest")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(PinterestLinkViewModel[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GetSuggestedPinterestLinks(ReviewResult status, bool notSynced) { RServiceResult res = await _artifactService.GetSuggestedPinterestLinks(status, notSynced); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// suggest pinterest link for ganjoor /// /// /// [HttpPost] [Route("pinterest")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(PinterestLinkViewModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task SuggestPinterestLink([FromBody] PinterestSuggestion suggestion) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _artifactService.SuggestPinterestLink(loggedOnUserId, suggestion); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// review suggested ganjoor pinterest link /// /// /// /// /// /// /// [HttpPut] [Route("pinterest/review/{linkId}/{result}")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + RMuseumSecurableItem.ReviewGanjoorLinksOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task ReviewSuggestedPinterestLink(Guid linkId, string altText, ReviewResult result, string reviewDesc, string imageUrl) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult suggestion = await _artifactService.ReviewSuggestedPinterestLink(linkId, loggedOnUserId, altText, result, reviewDesc, imageUrl); if (!string.IsNullOrEmpty(suggestion.ExceptionString)) return BadRequest(suggestion.ExceptionString); return Ok(); } /// /// mark suggested pinterest ganjoor link as synchronized /// /// /// [HttpPut] [Route("pinterest/sync/{linkId}")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + RMuseumSecurableItem.ReviewGanjoorLinksOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task SynchronizeSuggestedPinterestLink(Guid linkId) { RServiceResult suggestion = await _artifactService.SynchronizeSuggestedPinterestLink(linkId); if (!string.IsNullOrEmpty(suggestion.ExceptionString)) return BadRequest(suggestion.ExceptionString); return Ok(); } /// /// remove pinterest link /// /// /// [HttpDelete] [Route("pinterest")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + RMuseumSecurableItem.ReviewGanjoorLinksOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task RemovePinterestLinkAsync(Guid linkId) { RServiceResult suggestion = await _artifactService.RemovePinterestLinkAsync(linkId); if (!string.IsNullOrEmpty(suggestion.ExceptionString)) return BadRequest(suggestion.ExceptionString); return Ok(); } /// /// start filling GanjoorLink table OriginalSource values /// /// [HttpPut("ganjoorlink/fillsource")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + RMuseumSecurableItem.ReviewGanjoorLinksOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Unauthorized)] public IActionResult StartFillingGanjoorLinkOriginalSources() { try { var res = _artifactService.StartFillingGanjoorLinkOriginalSources(); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } catch (Exception exp) { return BadRequest(exp.ToString()); } } /// /// start removing original images /// /// [HttpDelete("originalimages/remove")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + RMuseumSecurableItem.ImportOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Unauthorized)] public IActionResult StartRemovingOriginalImages() { try { var res = _artifactService.StartRemovingOriginalImages(); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } catch (Exception exp) { return BadRequest(exp.ToString()); } } /// /// search artifacts /// /// /// /// [HttpGet] [Route("search")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task SearchArtifacts([FromQuery] PagingParameterModel paging, string term) { var pagedResult = await _artifactService.SearchArtifacts(paging, term); 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 artifact items /// /// /// /// [HttpGet] [Route("search/items")] [AllowAnonymous] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task SearchArtifactItems([FromQuery] PagingParameterModel paging, string term) { var pagedResult = await _artifactService.SearchArtifactItems(paging, term); 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); } /// /// start setting an artifact items as a category poems text original source /// /// /// /// [HttpPut("ganjoorlink/settextorigin/{ganjoorCatId}/{artifactId}")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + RMuseumSecurableItem.ReviewGanjoorLinksOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Unauthorized)] public IActionResult StartSettingArtifactAsTextOriginalSource(int ganjoorCatId, Guid artifactId) { try { var res = _artifactService.StartSettingArtifactAsTextOriginalSource(ganjoorCatId, artifactId); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } catch (Exception exp) { return BadRequest(exp.ToString()); } } /// /// upload artifact to external server /// /// /// /// [HttpPut("upload/external/{friendlyUrl}")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Unauthorized)] public IActionResult StartUploadingArtifactToExternalServer(string friendlyUrl, bool skipUpload) { try { var res = _artifactService.StartUploadingArtifactToExternalServer(friendlyUrl, skipUpload); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(); } catch (Exception exp) { return BadRequest(exp.ToString()); } } /// /// create images for ganjoor /// /// /// [HttpPut("ai/create/images")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Unauthorized)] public async Task OpenAIStartCreatingImagesForPoemsAsync(int startPoetId = 0) { try { await _artifactService.OpenAIStartCreatingImagesForPoemsAsync(startPoetId); return Ok(); } catch (Exception exp) { return BadRequest(exp.ToString()); } } /// /// create images for ganjoor - offline /// /// [HttpPut("ai/create/images/offline")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Unauthorized)] public async Task OpenAIStartCreatingImagesForPoemsOfflineAsync() { try { await _artifactService.OpenAIStartCreatingImagesForPoemsOfflineAsync(); return Ok(); } catch (Exception exp) { return BadRequest(exp.ToString()); } } /// /// add a new item to an artifact /// /// /// /// [HttpPut("item/{artifactFriendlyUrl}")] [Authorize(Policy = RMuseumSecurableItem.ArtifactEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RArtifactItemRecord))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Unauthorized)] public async Task AddItemToArtifactAsync(string artifactFriendlyUrl, IFormFile jpeg) { var res = await _artifactService.AddItemToArtifactAsync(artifactFriendlyUrl, jpeg, null); if (!string.IsNullOrEmpty(res.ExceptionString)) return BadRequest(res.ExceptionString); return Ok(res.Result); } /// /// readonly mode /// public bool ReadOnlyMode { get { try { return bool.Parse(Configuration["ReadOnlyMode"]); } catch { return false; } } } /// /// constructor /// /// /// /// /// /// /// public ArtifactController(IArtifactService artifactService, IUserPermissionChecker userPermissionChecker, IMemoryCache memoryCache, ICaptchaService captchaService, IAppUserService appUserService, IConfiguration configuration) { _artifactService = artifactService; _userPermissionChecker = userPermissionChecker; _memoryCache = memoryCache; _captchaService = captchaService; _appUserService = appUserService; Configuration = configuration; } /// /// Artifact Service /// protected readonly IArtifactService _artifactService; /// /// IUserPermissionChecker instance /// protected IUserPermissionChecker _userPermissionChecker; /// /// IMemoryCache /// protected readonly IMemoryCache _memoryCache; /// /// Captcha service /// protected readonly ICaptchaService _captchaService; /// /// IAppUserService instance /// protected IAppUserService _appUserService; /// /// Configuration /// protected IConfiguration Configuration { get; } } }