diff --git a/RMuseum/Controllers/PDFLibraryController.cs b/RMuseum/Controllers/PDFLibraryController.cs new file mode 100644 index 00000000..add8596a --- /dev/null +++ b/RMuseum/Controllers/PDFLibraryController.cs @@ -0,0 +1,237 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using RMuseum.Models.Artifact; +using RMuseum.Models.Auth.Memory; +using RMuseum.Models.PDFLibrary; +using RMuseum.Models.PDFLibrary.ViewModels; +using RMuseum.Services; +using RSecurityBackend.Models.Auth.Memory; +using RSecurityBackend.Models.Generic; +using System.Linq; +using System; +using System.Net; +using System.Threading.Tasks; +using RSecurityBackend.Services; + +namespace RMuseum.Controllers +{ + [Produces("application/json")] + [Route("api/pdf")] + public class PDFLibraryController : Controller + { + /// + /// start importing a local pdf file + /// + /// + /// + [HttpPost] + [Authorize(Policy = RMuseumSecurableItem.PDFLibraryEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] + [ProducesResponseType((int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] + public IActionResult StartImportingLocalPDF([FromBody] NewPDFBookViewModel model) + { + var res = _pdfService.StartImportingLocalPDF(model); + if (!string.IsNullOrEmpty(res.ExceptionString)) + return BadRequest(res.ExceptionString); + return Ok(); + } + + /// + /// secure get a pdf book + /// + /// + /// + [HttpGet("secure/{id}")] + [Authorize] + [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(PDFBook))] + [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + public async Task GetUserVisible(int id) + { + 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 bookRes = null; + if (visibleItems.Length == 1 && visibleItems[0] == PublishStatus.Published) + { + bookRes = await _pdfService.GetPDFBookByIdAsync(id, new PublishStatus[] { PublishStatus.Published }); + if (!string.IsNullOrEmpty(bookRes.ExceptionString)) + { + return BadRequest(bookRes.ExceptionString); + } + if (bookRes.Result == null) + return NotFound(); + } + if (bookRes == null) + { + bookRes = await _pdfService.GetPDFBookByIdAsync(id, visibleItems); + } + + if (!string.IsNullOrEmpty(bookRes.ExceptionString)) + { + return BadRequest(bookRes.ExceptionString); + } + if (bookRes.Result == null) + return NotFound(); + + Response.GetTypedHeaders().LastModified = bookRes.Result.LastModified; + + var requestHeaders = Request.GetTypedHeaders(); + if (requestHeaders.IfModifiedSince.HasValue && + requestHeaders.IfModifiedSince.Value >= bookRes.Result.LastModified) + { + return StatusCode(StatusCodes.Status304NotModified); + } + + + return Ok(bookRes.Result); + } + + private async Task> _GetUserVisibleArtifactStatusSet(Guid loggedOnUserId, Guid sessionId) + { + RServiceResult + canView = + await _userPermissionChecker.Check + ( + loggedOnUserId, + sessionId, + RMuseumSecurableItem.PDFLibraryEntityShortName, + 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 published PDF Book by id + /// + /// + /// + [HttpGet("{id}")] + [AllowAnonymous] + [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(PDFBook))] + [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + public async Task GetPDFBookByIdAsync(int id) + { + var bookRes = await _pdfService.GetPDFBookByIdAsync(id, new PublishStatus[] { PublishStatus.Published }); + + if (!string.IsNullOrEmpty(bookRes.ExceptionString)) + { + return BadRequest(bookRes.ExceptionString); + } + if (bookRes.Result == null) + return NotFound(); + + Response.GetTypedHeaders().LastModified = bookRes.Result.LastModified; + + var requestHeaders = Request.GetTypedHeaders(); + if (requestHeaders.IfModifiedSince.HasValue && + requestHeaders.IfModifiedSince.Value >= bookRes.Result.LastModified) + { + return StatusCode(StatusCodes.Status304NotModified); + } + + + return Ok(bookRes.Result); + } + + /// + /// add a new book + /// + /// + /// + + [HttpPost("book")] + [Authorize(Policy = RMuseumSecurableItem.PDFLibraryEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] + [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(Book))] + [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] + public async Task AddBookAsync([FromBody] Book book) + { + var res = await _pdfService.AddBookAsync(book); + if (!string.IsNullOrEmpty(res.ExceptionString)) + return BadRequest(res.ExceptionString); + return Ok(res.Result); + } + + /// + /// add a new author + /// + /// + /// + + [HttpPost("author")] + [Authorize(Policy = RMuseumSecurableItem.PDFLibraryEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] + [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(Author))] + [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] + public async Task AddAuthorAsync([FromBody] Author author) + { + var res = await _pdfService.AddAuthorAsync(author); + if (!string.IsNullOrEmpty(res.ExceptionString)) + return BadRequest(res.ExceptionString); + return Ok(res.Result); + } + + /// + /// add a new mukti volume pdf collection + /// + /// + /// + [HttpPost("volumes")] + [Authorize(Policy = RMuseumSecurableItem.PDFLibraryEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] + [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(MultiVolumePDFCollection))] + [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] + public async Task AddMultiVolumePDFCollection([FromBody] MultiVolumePDFCollection volumes) + { + var res = await _pdfService.AddMultiVolumePDFCollection(volumes); + if (!string.IsNullOrEmpty(res.ExceptionString)) + return BadRequest(res.ExceptionString); + return Ok(res.Result); + } + + /// + /// PDF Service + /// + protected readonly IPDFLibraryService _pdfService; + + /// + /// IUserPermissionChecker instance + /// + protected IUserPermissionChecker _userPermissionChecker; + + /// + /// constructor + /// + /// + /// + public PDFLibraryController(IPDFLibraryService pdfService, IUserPermissionChecker userPermissionChecker) + { + _pdfService = pdfService; + _userPermissionChecker = userPermissionChecker; + } + } +} diff --git a/RMuseum/Models/Auth/Memory/RMuseumSecurableItem.cs b/RMuseum/Models/Auth/Memory/RMuseumSecurableItem.cs index d1c8e9cc..4b104c18 100644 --- a/RMuseum/Models/Auth/Memory/RMuseumSecurableItem.cs +++ b/RMuseum/Models/Auth/Memory/RMuseumSecurableItem.cs @@ -108,6 +108,11 @@ namespace RMuseum.Models.Auth.Memory /// public const string ModeratePoetPhotos = "photos"; + /// + /// pdf + /// + public const string PDFLibraryEntityShortName = "pdf"; + /// /// list of forms and their permissions /// @@ -194,6 +199,16 @@ namespace RMuseum.Models.Auth.Memory new SecurableItemOperation(ModerateOperationShortName, "مدیریت", false), } }, + new SecurableItem() + { + ShortName = PDFLibraryEntityShortName, + Description = "نسک‌بان", + Operations = new SecurableItemOperation[] + { + new SecurableItemOperation(ModifyOperationShortName, "ویرایش محتوا", false), + new SecurableItemOperation(ViewDraftOperationShortName, "مشاهدهٔ پیش‌نویس‌ها", false), + } + }, diff --git a/RMuseum/RMuseum.xml b/RMuseum/RMuseum.xml index 388514db..b166feed 100644 --- a/RMuseum/RMuseum.xml +++ b/RMuseum/RMuseum.xml @@ -2343,6 +2343,65 @@ + + + start importing a local pdf file + + + + + + + secure get a pdf book + + + + + + + get published PDF Book by id + + + + + + + add a new book + + + + + + + add a new author + + + + + + + add a new mukti volume pdf collection + + + + + + + PDF Service + + + + + IUserPermissionChecker instance + + + + + constructor + + + + returns paginated published recitations (if poetId or catId is non-zero its ordered by poemId ascending if not it is ordered by publish date descending) @@ -4387,6 +4446,11 @@ photos + + + pdf + + list of forms and their permissions @@ -17424,6 +17488,14 @@ طول تصویر بندانگشتی + + + get pdf book by id + + + + + start importing local pdf file @@ -17431,6 +17503,42 @@ + + + add author + + + + + + + get author by id + + + + + + + get authors + + + + + + + + add book + + + + + + + add multi volume pdf collection + + + + Database Context @@ -18234,6 +18342,50 @@ PDF Library Services + + + get pdf book by id + + + + + + + + add author + + + + + + + get author by id + + + + + + + get authors + + + + + + + + add book + + + + + + + add multi volume pdf collection + + + + start importing local pdf file diff --git a/RMuseum/Services/IPDFLibraryService.cs b/RMuseum/Services/IPDFLibraryService.cs index 05e70a30..c98bbf36 100644 --- a/RMuseum/Services/IPDFLibraryService.cs +++ b/RMuseum/Services/IPDFLibraryService.cs @@ -1,5 +1,8 @@ -using RMuseum.Models.PDFLibrary.ViewModels; +using RMuseum.Models.Artifact; +using RMuseum.Models.PDFLibrary; +using RMuseum.Models.PDFLibrary.ViewModels; using RSecurityBackend.Models.Generic; +using System.Threading.Tasks; namespace RMuseum.Services { @@ -8,6 +11,50 @@ namespace RMuseum.Services /// public interface IPDFLibraryService { + /// + /// get pdf book by id + /// + /// + /// + /// + Task> GetPDFBookByIdAsync(int id, PublishStatus[] statusArray); + + /// + /// add author + /// + /// + /// + Task> AddAuthorAsync(Author author); + + /// + /// get author by id + /// + /// + /// + Task> GetAuthorByIdAsync(int id); + + /// + /// get authors + /// + /// + /// + /// + Task> GetAuthorsAsync(PagingParameterModel paging, string authorName); + + /// + /// add book + /// + /// + /// + Task> AddBookAsync(Book book); + + /// + /// add multi volume pdf collection + /// + /// + /// + Task> AddMultiVolumePDFCollection(MultiVolumePDFCollection multiVolumePDFCollection); + /// /// start importing local pdf file /// diff --git a/RMuseum/Services/Implementation/PDFLibraryService-Partials/PDFLibraryService-LocalPDF.cs b/RMuseum/Services/Implementation/PDFLibraryService-Partials/PDFLibraryService-LocalPDF.cs index f81cdb20..d0b438ca 100644 --- a/RMuseum/Services/Implementation/PDFLibraryService-Partials/PDFLibraryService-LocalPDF.cs +++ b/RMuseum/Services/Implementation/PDFLibraryService-Partials/PDFLibraryService-LocalPDF.cs @@ -20,7 +20,7 @@ namespace RMuseum.Services.Implementation { public partial class PDFLibraryService { - private async Task> ImportLocalPDFFile(RMuseumDbContext context, int bookId, int? volumeId, int volumeOrder, string filePath, string srcUrl, bool skipUpload) + private async Task> ImportLocalPDFFileAsync(RMuseumDbContext context, int bookId, int? volumeId, int volumeOrder, string filePath, string srcUrl, bool skipUpload) { string fileChecksum = PoemAudio.ComputeCheckSum(filePath); if ( diff --git a/RMuseum/Services/Implementation/PDFLibraryService.cs b/RMuseum/Services/Implementation/PDFLibraryService.cs index d10fc17c..a8852b72 100644 --- a/RMuseum/Services/Implementation/PDFLibraryService.cs +++ b/RMuseum/Services/Implementation/PDFLibraryService.cs @@ -1,12 +1,16 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using RMuseum.DbContext; +using RMuseum.Models.Artifact; using RMuseum.Models.PDFLibrary; using RMuseum.Models.PDFLibrary.ViewModels; using RSecurityBackend.Models.Generic; using RSecurityBackend.Services; +using RSecurityBackend.Services.Implementation; +using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; namespace RMuseum.Services.Implementation { @@ -15,6 +19,32 @@ namespace RMuseum.Services.Implementation /// public partial class PDFLibraryService : IPDFLibraryService { + /// + /// get pdf book by id + /// + /// + /// + /// + public async Task> GetPDFBookByIdAsync(int id, PublishStatus[] statusArray) + { + try + { + var pdfBook = await _context.PDFBooks.AsNoTracking() + .Include(b => b.Book) + .Include(b => b.MultiVolumePDFCollection) + .Include(b => b.Contributers) + .Include(b => b.Tags) + .Include(b => b.Pages) + .Where(b => statusArray.Contains(b.Status) &&b.Id == id) + .SingleOrDefaultAsync(); + return new RServiceResult(pdfBook); + + } + catch (Exception exp) + { + return new RServiceResult(null, exp.ToString()); + } + } /// /// start importing local pdf file @@ -29,7 +59,7 @@ namespace RMuseum.Services.Implementation { using (RMuseumDbContext context = new RMuseumDbContext(new DbContextOptions())) { - var pdfRes = await ImportLocalPDFFile(context, model.BookId, model.MultiVolumePDFCollectionId, model.VolumeOrder, model.LocalImportingPDFFilePath, model.OriginalSourceUrl, model.SkipUpload); + var pdfRes = await ImportLocalPDFFileAsync(context, model.BookId, model.MultiVolumePDFCollectionId, model.VolumeOrder, model.LocalImportingPDFFilePath, model.OriginalSourceUrl, model.SkipUpload); if (pdfRes.Result != null) { var pdfBook = pdfRes.Result; @@ -49,7 +79,7 @@ namespace RMuseum.Services.Implementation pdfBook.OriginalSourceName = model.OriginalSourceName; pdfBook.OriginalFileUrl = model.OriginalFileUrl; List roles = new List(); - if(model.WriterId != null) + if (model.WriterId != null) { roles.Add(new AuthorRole() { @@ -81,7 +111,7 @@ namespace RMuseum.Services.Implementation Role = "نویسنده", }); } - if(model.TranslatorId != null) + if (model.TranslatorId != null) { roles.Add(new AuthorRole() { @@ -129,7 +159,7 @@ namespace RMuseum.Services.Implementation Role = "مصحح", }); } - if(roles.Count > 0) + if (roles.Count > 0) { pdfBook.Contributers = roles; } @@ -137,35 +167,118 @@ namespace RMuseum.Services.Implementation await context.SaveChangesAsync(); } } - } ); - - return new RServiceResult(true); } + /// + /// add author + /// + /// + /// + public async Task> AddAuthorAsync(Author author) + { + try + { + _context.Add(author); + await _context.SaveChangesAsync(); + return new RServiceResult(author); + } + catch (Exception exp) + { + return new RServiceResult(null, exp.ToString()); + } + } + + /// + /// get author by id + /// + /// + /// + public async Task> GetAuthorByIdAsync(int id) + { + try + { + return new RServiceResult(await _context.Authors.AsNoTracking().Where(a => a.Id == id).SingleOrDefaultAsync()); + } + catch (Exception exp) + { + return new RServiceResult(null, exp.ToString()); + } + } + + /// + /// get authors + /// + /// + /// + /// + public async Task> GetAuthorsAsync(PagingParameterModel paging, string authorName) + { + var source = + _context.Authors + .Where(a => string.IsNullOrEmpty(authorName) || (authorName.Contains(a.Name) || (!string.IsNullOrEmpty(a.NameInOriginalLanguage) && authorName.Contains(a.NameInOriginalLanguage)) )) + .AsQueryable(); + (PaginationMetadata PagingMeta, Author[] Items) paginatedResult = + await QueryablePaginator.Paginate(source, paging); + return new RServiceResult<(PaginationMetadata PagingMeta, Author[] Authors)>(paginatedResult); + } + + /// + /// add book + /// + /// + /// + public async Task> AddBookAsync(Book book) + { + try + { + _context.Add(book); + await _context.SaveChangesAsync(); + return new RServiceResult(book); + } + catch (Exception exp) + { + return new RServiceResult(null, exp.ToString()); + } + } + + /// + /// add multi volume pdf collection + /// + /// + /// + public async Task> AddMultiVolumePDFCollection(MultiVolumePDFCollection multiVolumePDFCollection) + { + try + { + _context.Add(multiVolumePDFCollection); + await _context.SaveChangesAsync(); + return new RServiceResult(multiVolumePDFCollection); + } + catch (Exception exp) + { + return new RServiceResult(null, exp.ToString()); + } + } + /// /// Database Context /// protected readonly RMuseumDbContext _context; - /// /// Background Task Queue Instance /// protected readonly IBackgroundTaskQueue _backgroundTaskQueue; - /// /// image file service /// protected readonly IImageFileService _imageFileService; - /// /// Configuration /// protected IConfiguration Configuration { get; } - - /// /// constructor ///