using ganjoor; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using RMuseum.DbContext; using RMuseum.Models.Artifact; using RMuseum.Models.ImportJob; using RMuseum.Models.PDFLibrary; using RMuseum.Models.PDFLibrary.ViewModels; using RSecurityBackend.Models.Generic; using RSecurityBackend.Models.Image; using RSecurityBackend.Services; using RSecurityBackend.Services.Implementation; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; namespace RMuseum.Services.Implementation { /// /// PDF Library Services /// 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.PDFFile) .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()); } } /// /// get all pdfbooks (including CoverImage info but not pages or tagibutes info) /// /// /// /// public async Task> GetAllPDFBooksAsync(PagingParameterModel paging, PublishStatus[] statusArray) { try { var source = _context.PDFBooks.AsNoTracking() .Include(a => a.CoverImage) .Where(a => statusArray.Contains(a.Status)) .OrderByDescending(t => t.DateTime) .AsQueryable(); (PaginationMetadata PagingMeta, PDFBook[] Books) paginatedResult = await QueryablePaginator.Paginate(source, paging); return new RServiceResult<(PaginationMetadata PagingMeta, PDFBook[] Books)>(paginatedResult); } catch (Exception exp) { return new RServiceResult<(PaginationMetadata PagingMeta, PDFBook[] Books)>((null, null), exp.ToString()); } } /// /// start importing local pdf file /// /// /// public async Task> StartImportingLocalPDFAsync(NewPDFBookViewModel model) { try { if (model == null) { return new RServiceResult(false, "model == null"); } if (!File.Exists(model.LocalImportingPDFFilePath)) { return new RServiceResult(false, $"file does not exist! : {model.LocalImportingPDFFilePath}"); } string fileChecksum = PoemAudio.ComputeCheckSum(model.LocalImportingPDFFilePath); if ( ( await _context.ImportJobs .Where(j => j.JobType == JobType.Pdf && j.SrcContent == fileChecksum && !(j.Status == ImportJobStatus.Failed || j.Status == ImportJobStatus.Aborted)) .SingleOrDefaultAsync() ) != null ) { return new RServiceResult(false, $"Job is already scheduled or running for importing pdf file {model.LocalImportingPDFFilePath} (duplicated checksum: {fileChecksum})"); } _backgroundTaskQueue.QueueBackgroundWorkItem ( async token => { using (RMuseumDbContext context = new RMuseumDbContext(new DbContextOptions())) { var pdfRes = await ImportLocalPDFFileAsync(context, model.BookId, model.MultiVolumePDFCollectionId, model.VolumeOrder, model.LocalImportingPDFFilePath, model.OriginalSourceUrl, model.SkipUpload, fileChecksum); if (pdfRes.Result != null) { var pdfBook = pdfRes.Result; pdfBook.Title = model.Title; pdfBook.SubTitle = model.SubTitle; pdfBook.AuthorsLine = model.AuthorsLine; pdfBook.ISBN = model.ISBN; pdfBook.Description = model.Description; pdfBook.IsTranslation = model.IsTranslation; pdfBook.TranslatorsLine = model.TranslatorsLine; pdfBook.TitleInOriginalLanguage = model.TitleInOriginalLanguage; pdfBook.PublisherLine = model.PublisherLine; pdfBook.PublishingDate = model.PublishingDate; pdfBook.PublishingLocation = model.PublishingLocation; pdfBook.PublishingNumber = model.PublishingNumber == 0 ? null : model.PublishingNumber; pdfBook.ClaimedPageCount = model.ClaimedPageCount == 0 ? null : model.ClaimedPageCount; pdfBook.OriginalSourceName = model.OriginalSourceName; pdfBook.OriginalFileUrl = model.OriginalFileUrl; List roles = new List(); if (model.WriterId != null && model.WriterId != 0) { roles.Add(new AuthorRole() { Author = await context.Authors.Where(a => a.Id == model.WriterId).SingleAsync(), Role = "نویسنده", }); } if (model.Writer2Id != null && model.Writer2Id != 0) { roles.Add(new AuthorRole() { Author = await context.Authors.Where(a => a.Id == model.Writer2Id).SingleAsync(), Role = "نویسنده", }); } if (model.Writer3Id != null && model.Writer3Id != 0) { roles.Add(new AuthorRole() { Author = await context.Authors.Where(a => a.Id == model.Writer3Id).SingleAsync(), Role = "نویسنده", }); } if (model.Writer4Id != null && model.Writer4Id != 0) { roles.Add(new AuthorRole() { Author = await context.Authors.Where(a => a.Id == model.Writer4Id).SingleAsync(), Role = "نویسنده", }); } if (model.TranslatorId != null && model.TranslatorId != 0) { roles.Add(new AuthorRole() { Author = await context.Authors.Where(a => a.Id == model.TranslatorId).SingleAsync(), Role = "مترجم", }); } if (model.Translator2Id != null && model.Translator2Id != 0) { roles.Add(new AuthorRole() { Author = await context.Authors.Where(a => a.Id == model.Translator2Id).SingleAsync(), Role = "مترجم", }); } if (model.Translator3Id != null && model.Translator3Id != 0) { roles.Add(new AuthorRole() { Author = await context.Authors.Where(a => a.Id == model.Translator3Id).SingleAsync(), Role = "مترجم", }); } if (model.Translator4Id != null && model.Translator4Id != 0) { roles.Add(new AuthorRole() { Author = await context.Authors.Where(a => a.Id == model.Translator4Id).SingleAsync(), Role = "مترجم", }); } if (model.CollectorId != null && model.CollectorId != 0) { roles.Add(new AuthorRole() { Author = await context.Authors.Where(a => a.Id == model.CollectorId).SingleAsync(), Role = "مصحح", }); } if (model.Collector2Id != null && model.Collector2Id != 0) { roles.Add(new AuthorRole() { Author = await context.Authors.Where(a => a.Id == model.Collector2Id).SingleAsync(), Role = "مصحح", }); } if (roles.Count > 0) { pdfBook.Contributers = roles; } context.Update(pdfBook); await context.SaveChangesAsync(); } } } ); return new RServiceResult(true); } catch (Exception exp) { return new RServiceResult(false, exp.ToString()); } } /// /// edit pdf book master record /// /// /// /// /// public async Task> EditPDFBookMasterRecordAsync(PDFBook model, bool canChangeStatusToAwaiting, bool canPublish) { try { if (string.IsNullOrEmpty(model.Title)) { return new RServiceResult(null, "Name could not be empty."); } PDFBook pdfBook = await _context.PDFBooks .Where(a => a.Id == model.Id) .SingleOrDefaultAsync(); if (pdfBook != null) { if (pdfBook.Status != model.Status) { if (!canChangeStatusToAwaiting) { return new RServiceResult(null, "User should be able to change status to Awaiting to complete this operation."); } if ( ! ( (pdfBook.Status == PublishStatus.Draft && model.Status == PublishStatus.Awaiting) || (pdfBook.Status == PublishStatus.Awaiting && model.Status == PublishStatus.Draft) ) ) { if (!canPublish) { return new RServiceResult(null, "User should have Publish permission to complete this operation."); } } } pdfBook.Status = model.Status; pdfBook.Title = model.Title; pdfBook.SubTitle = model.SubTitle; pdfBook.AuthorsLine = model.AuthorsLine; pdfBook.ISBN = model.ISBN; pdfBook.Description = model.Description; pdfBook.IsTranslation = model.IsTranslation; pdfBook.TranslatorsLine = model.TranslatorsLine; pdfBook.TitleInOriginalLanguage = model.TitleInOriginalLanguage; pdfBook.PublisherLine = model.PublisherLine; pdfBook.PublishingDate = model.PublishingDate; pdfBook.PublishingLocation = model.PublishingLocation; pdfBook.PublishingNumber = model.PublishingNumber == 0 ? null : model.PublishingNumber; pdfBook.ClaimedPageCount = model.ClaimedPageCount == 0 ? null : model.ClaimedPageCount; pdfBook.OriginalSourceName = model.OriginalSourceName; pdfBook.OriginalFileUrl = model.OriginalFileUrl; pdfBook.VolumeOrder = model.VolumeOrder; pdfBook.MultiVolumePDFCollectionId = model.MultiVolumePDFCollectionId; pdfBook.BookId = model.BookId; pdfBook.LastModified = DateTime.Now; _context.Update(pdfBook); await _context.SaveChangesAsync(); } return new RServiceResult(pdfBook); } catch (Exception exp) { return new RServiceResult(null, exp.ToString()); } } /// /// Copy PDF Book Cover Image From Page Thumbnail image /// /// /// /// public async Task> SetPDFBookCoverImageFromPageAsync(int pdfBookId, int pdfpageId) { try { PDFBook pdfBook = await _context .PDFBooks.Where(a => a.Id == pdfBookId) .SingleOrDefaultAsync(); if (pdfBook == null) return new RServiceResult(false, "pdf book not found."); PDFPage pdfPage = await _context.PDFPages.AsNoTracking().Include(p => p.ThumbnailImage).Where(p => p.Id == pdfpageId).SingleOrDefaultAsync(); if (pdfPage == null) return new RServiceResult(false, "Page not found."); pdfBook.CoverImage = RImage.DuplicateExcludingId(pdfPage.ThumbnailImage); pdfBook.LastModified = DateTime.Now; _context.Update(pdfBook); await _context.SaveChangesAsync(); return new RServiceResult(true); } catch (Exception exp) { return new RServiceResult(false, exp.ToString()); } } /// /// an incomplete prototype for removing PDF books /// /// /// public async Task> RemovePDFBookAsync(int pdfBookId) { try { PDFBook record = await _context.PDFBooks .Include(a => a.Pages).ThenInclude(i => i.ThumbnailImage) .Include(a => a.Pages).ThenInclude(i => i.Tags) .Include(a => a.Tags) .Where(a => a.Id == pdfBookId) .SingleOrDefaultAsync(); if (record == null) { return new RServiceResult(false, "PDFBook not found."); } if (record.Status == PublishStatus.Published) { return new RServiceResult(false, "Can not delete published pdf book"); } string artifactFolder = Path.Combine(_imageFileService.ImageStoragePath, record.StorageFolderName); foreach (PDFPage pages in record.Pages) { _context.TagValues.RemoveRange(pages.Tags); } _context.RemoveRange(record.Pages); _context.TagValues.RemoveRange(record.Tags); _context.Remove(record); await _context.SaveChangesAsync(); if (!string.IsNullOrEmpty(artifactFolder)) { try { Directory.Delete(artifactFolder, true); } catch { //ignore errors } } } catch (Exception exp) { return new RServiceResult(false, exp.ToString()); } return new RServiceResult(true); } /// /// get tagged publish pdfbooks (including CoverImage info but not pages or tagibutes info) /// /// /// /// /// public async Task> GetPDFBookByTagValueAsync(string tagUrl, string valueUrl, PublishStatus[] statusArray) { try { RTag tag = await _context.Tags .Where(a => a.FriendlyUrl == tagUrl) .SingleOrDefaultAsync(); if (tag == null) return new RServiceResult(new PDFBook[] { }); PDFBook[] taggedItems = await _context.PDFBooks.Include(a => a.Tags) .Include(a => a.CoverImage) .Where(a => statusArray.Contains(a.Status) && a.Tags != null && a.Tags.Any(v => v.RTagId == tag.Id && v.FriendlyUrl == valueUrl)) .OrderByDescending(t => t.DateTime) .AsNoTracking() .ToArrayAsync(); foreach (PDFBook taggedItem in taggedItems) taggedItem.Tags = null; return new RServiceResult(taggedItems); } catch (Exception exp) { return new RServiceResult(null, exp.ToString()); } } /// /// add pdf book tag value /// /// /// /// public async Task> TagPDFBookAsync(int pdfBookId, RTag rTag) { try { RTag type = await _context.Tags.Where(a => a.Id == rTag.Id).SingleOrDefaultAsync(); PDFBook item = await _context.PDFBooks.Include(i => i.Tags).Where(i => i.Id == pdfBookId).SingleOrDefaultAsync(); int order = item.Tags.Where(t => t.RTagId == type.Id).Count() == 0 ? 1 : item.Tags.Where(t => t.RTagId == type.Id).OrderByDescending(t => t.Order).FirstOrDefault().Order + 1; RTagValue tag = new RTagValue() { Order = order, Value = "", ValueInEnglish = "", ValueSupplement = "", RTag = type, Status = PublishStatus.Published }; item.Tags.Add(tag); item.LastModified = DateTime.Now; _context.Update(item); await _context.SaveChangesAsync(); return new RServiceResult(tag); } catch (Exception exp) { return new RServiceResult(null, exp.ToString()); } } /// /// remove pdf book tag value /// /// /// /// public async Task> UnTagPDFBookAsync(int pdfBookId, Guid tagValueId) { try { PDFBook item = await _context.PDFBooks.Include(i => i.Tags).Where(i => i.Id == pdfBookId).SingleOrDefaultAsync(); item.Tags.Remove(item.Tags.Where(t => t.Id == tagValueId).SingleOrDefault()); item.LastModified = DateTime.Now; _context.Update(item); await _context.SaveChangesAsync(); return new RServiceResult(true); } catch (Exception exp) { return new RServiceResult(false, exp.ToString()); } } /// /// edit pdf book tag value /// /// /// /// apply on all same value tags /// public async Task> EditPDFBookTagValueAsync(int pdfBookId, RTagValue edited, bool global) { try { if (string.IsNullOrEmpty(edited.Value)) { return new RServiceResult(null, "Value could not be empty."); } PDFBook pdfBook = await _context.PDFBooks .Include(a => a.Tags) .Where(a => a.Id == pdfBookId) .SingleOrDefaultAsync(); if (pdfBook == null) return new RServiceResult(null); RTagValue tag = pdfBook.Tags.Where(a => a.Id == edited.Id) .SingleOrDefault(); if (tag != null) { tag.Order = edited.Order; tag.ValueSupplement = edited.ValueSupplement; _context.Update(tag); if (global) { RTagValue[] sameValueTags = await _context.TagValues.Where(v => v.Value == tag.Value && v.RTagId == tag.RTagId).ToArrayAsync(); foreach (RTagValue sameValueTag in sameValueTags) { sameValueTag.Value = edited.Value; sameValueTag.ValueInEnglish = edited.ValueInEnglish; sameValueTag.Status = edited.Status; sameValueTag.FriendlyUrl = edited.FriendlyUrl; _context.Update(sameValueTag); RArtifactMasterRecord correspondingArtifact = await _context.Artifacts.Include(a => a.Tags).Where(a => a.Tags.Contains(sameValueTag)).SingleOrDefaultAsync(); if (correspondingArtifact != null) { correspondingArtifact.LastModified = DateTime.Now; _context.Update(correspondingArtifact); } RArtifactItemRecord correspondingItem = await _context.Items.Include(a => a.Tags).Where(a => a.Tags.Contains(sameValueTag)).SingleOrDefaultAsync(); if (correspondingItem != null) { correspondingItem.LastModified = DateTime.Now; _context.Update(correspondingItem); } } await _context.SaveChangesAsync(); } else { tag.Value = edited.Value; tag.ValueInEnglish = edited.ValueInEnglish; tag.Order = edited.Order; tag.FriendlyUrl = edited.FriendlyUrl; tag.Status = edited.Status; tag.ValueSupplement = edited.ValueSupplement; _context.Update(tag); pdfBook.LastModified = DateTime.Now; _context.Update(pdfBook); await _context.SaveChangesAsync(); } } return new RServiceResult(tag); } catch (Exception exp) { return new RServiceResult(null, exp.ToString()); } } /// /// 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()); } } /// /// update author /// /// /// public async Task> UpdateAuthorAsync(Author model) { try { var dbAuthor = await _context.Authors.Where(author => author.Id == model.Id).SingleOrDefaultAsync(); dbAuthor.Name = model.Name; dbAuthor.NameInOriginalLanguage = model.NameInOriginalLanguage; dbAuthor.Bio = model.Bio; dbAuthor.ImageId = model.ImageId; dbAuthor.ExtenalImageUrl = model.ExtenalImageUrl; _context.Update(dbAuthor); await _context.SaveChangesAsync(); return new RServiceResult(model); } catch (Exception exp) { return new RServiceResult(null, exp.ToString()); } } /// /// delete author by id /// /// /// public async Task> DeleteAuthorAsync(int id) { try { var dbAuthor = await _context.Authors.Where(author => author.Id == id).SingleOrDefaultAsync(); _context.Remove(dbAuthor); await _context.SaveChangesAsync(); return new RServiceResult(true); } catch (Exception exp) { return new RServiceResult(false, 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) { try { 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); } catch (Exception exp) { return new RServiceResult<(PaginationMetadata PagingMeta, Author[] Authors)>((null, null), exp.ToString()); } } /// /// add pdf book contributer /// /// /// /// /// public async Task> AddPDFBookContributerAsync(int pdfBookId, int authorId, string role) { try { role = role.Trim(); var pdfBook = await _context.PDFBooks.Include(b => b.Contributers).ThenInclude(a => a.Author).Where(b => b.Id == pdfBookId).SingleAsync(); if(pdfBook.Contributers.Any(a => a.Author.Id == authorId && a.Role == role)) { return new RServiceResult(false, "author contribution already added"); } var author = await _context.Authors.AsNoTracking().Where(a => a.Id == authorId).SingleAsync(); pdfBook.Contributers.Add(new AuthorRole() { Author = author, Role = role }); _context.Update(pdfBook); await _context.SaveChangesAsync(); return new RServiceResult(true); } catch (Exception exp) { return new RServiceResult(false, exp.ToString()); } } /// /// remove contribution from pdf book /// /// /// /// public async Task> DeletePDFBookContributerAsync(int pdfBookId, int contributionId) { try { var pdfBook = await _context.PDFBooks.Include(b => b.Contributers).ThenInclude(a => a.Author).Where(b => b.Id == pdfBookId).SingleAsync(); if (!pdfBook.Contributers.Any(a => a.Id == contributionId)) { return new RServiceResult(false, "author contribution not found."); } var contribution = pdfBook.Contributers.Where(a => a.Id == contributionId).Single(); pdfBook.Contributers.Remove(contribution); _context.Update(pdfBook); await _context.SaveChangesAsync(); return new RServiceResult(true); } catch (Exception exp) { return new RServiceResult(false, exp.ToString()); } } /// /// 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 /// /// /// /// /// public PDFLibraryService(RMuseumDbContext context, IBackgroundTaskQueue backgroundTaskQueue, IImageFileService imageFileService, IConfiguration configuration) { _context = context; _backgroundTaskQueue = backgroundTaskQueue; _imageFileService = imageFileService; Configuration = configuration; } } }