Bulk Add to Collection (#674)
* Fixed the typeahead not having the same size input box as other inputs * Implemented the ability to add multiple series to a collection through bulk operations flow. Updated book parser to handle "@import url('...');" syntax as well as @import '...'; * Implemented the ability to create a new Collection tag via bulk operations flow.
This commit is contained in:
parent
908c872f5c
commit
52f8fbe3db
31 changed files with 309 additions and 16 deletions
|
@ -2,7 +2,9 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Data;
|
||||
using API.DTOs;
|
||||
using API.DTOs.CollectionTags;
|
||||
using API.Entities;
|
||||
using API.Extensions;
|
||||
using API.Interfaces;
|
||||
|
@ -90,6 +92,40 @@ namespace API.Controllers
|
|||
return BadRequest("Something went wrong, please try again");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a collection tag onto multiple Series. If tag id is 0, this will create a new tag.
|
||||
/// </summary>
|
||||
/// <param name="dto"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("update-for-series")]
|
||||
public async Task<ActionResult> AddToMultipleSeries(CollectionTagBulkAddDto dto)
|
||||
{
|
||||
var tag = await _unitOfWork.CollectionTagRepository.GetFullTagAsync(dto.CollectionTagId);
|
||||
if (tag == null)
|
||||
{
|
||||
tag = DbFactory.CollectionTag(0, dto.CollectionTagTitle, String.Empty, false);
|
||||
_unitOfWork.CollectionTagRepository.Add(tag);
|
||||
}
|
||||
|
||||
|
||||
var seriesMetadatas = await _unitOfWork.SeriesRepository.GetSeriesMetadataForIdsAsync(dto.SeriesIds);
|
||||
foreach (var metadata in seriesMetadatas)
|
||||
{
|
||||
if (!metadata.CollectionTags.Any(t => t.Title.Equals(tag.Title, StringComparison.InvariantCulture)))
|
||||
{
|
||||
metadata.CollectionTags.Add(tag);
|
||||
_unitOfWork.SeriesMetadataRepository.Update(metadata);
|
||||
}
|
||||
}
|
||||
|
||||
if (!_unitOfWork.HasChanges()) return Ok();
|
||||
if (await _unitOfWork.CommitAsync())
|
||||
{
|
||||
return Ok();
|
||||
}
|
||||
return BadRequest("There was an issue updating series with collection tag");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For a given tag, update the summary if summary has changed and remove a set of series from the tag.
|
||||
/// </summary>
|
||||
|
|
|
@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
|||
using System.Xml.Serialization;
|
||||
using API.Comparators;
|
||||
using API.DTOs;
|
||||
using API.DTOs.CollectionTags;
|
||||
using API.DTOs.Filtering;
|
||||
using API.DTOs.OPDS;
|
||||
using API.Entities;
|
||||
|
|
18
API/DTOs/CollectionTags/CollectionTagBulkAddDto.cs
Normal file
18
API/DTOs/CollectionTags/CollectionTagBulkAddDto.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace API.DTOs.CollectionTags
|
||||
{
|
||||
public class CollectionTagBulkAddDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Collection Tag Id
|
||||
/// </summary>
|
||||
/// <remarks>Can be 0 which then will use Title to create a tag</remarks>
|
||||
public int CollectionTagId { get; init; }
|
||||
public string CollectionTagTitle { get; init; }
|
||||
/// <summary>
|
||||
/// Series Ids to add onto Collection Tag
|
||||
/// </summary>
|
||||
public IEnumerable<int> SeriesIds { get; init; }
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
namespace API.DTOs
|
||||
namespace API.DTOs.CollectionTags
|
||||
{
|
||||
public class CollectionTagDto
|
||||
{
|
|
@ -1,10 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace API.DTOs
|
||||
namespace API.DTOs.CollectionTags
|
||||
{
|
||||
public class UpdateSeriesForTagDto
|
||||
{
|
||||
public CollectionTagDto Tag { get; init; }
|
||||
public ICollection<int> SeriesIdsToRemove { get; init; }
|
||||
public IEnumerable<int> SeriesIdsToRemove { get; init; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using API.DTOs.CollectionTags;
|
||||
using API.Entities;
|
||||
|
||||
namespace API.DTOs
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using API.DTOs.CollectionTags;
|
||||
|
||||
namespace API.DTOs
|
||||
{
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.DTOs;
|
||||
using API.DTOs.CollectionTags;
|
||||
using API.Entities;
|
||||
using API.Interfaces.Repositories;
|
||||
using AutoMapper;
|
||||
|
@ -21,6 +22,11 @@ namespace API.Data.Repositories
|
|||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public void Add(CollectionTag tag)
|
||||
{
|
||||
_context.CollectionTag.Add(tag);
|
||||
}
|
||||
|
||||
public void Remove(CollectionTag tag)
|
||||
{
|
||||
_context.CollectionTag.Remove(tag);
|
||||
|
|
20
API/Data/Repositories/SeriesMetadataRepository.cs
Normal file
20
API/Data/Repositories/SeriesMetadataRepository.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using API.Entities;
|
||||
using API.Interfaces.Repositories;
|
||||
|
||||
namespace API.Data.Repositories
|
||||
{
|
||||
public class SeriesMetadataRepository : ISeriesMetadataRepository
|
||||
{
|
||||
private readonly DataContext _context;
|
||||
|
||||
public SeriesMetadataRepository(DataContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public void Update(SeriesMetadata seriesMetadata)
|
||||
{
|
||||
_context.SeriesMetadata.Update(seriesMetadata);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ using System.Linq;
|
|||
using System.Threading.Tasks;
|
||||
using API.Data.Scanner;
|
||||
using API.DTOs;
|
||||
using API.DTOs.CollectionTags;
|
||||
using API.DTOs.Filtering;
|
||||
using API.Entities;
|
||||
using API.Extensions;
|
||||
|
@ -485,5 +486,13 @@ namespace API.Data.Repositories
|
|||
TotalChunks = totalChunks
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<IList<SeriesMetadata>> GetSeriesMetadataForIdsAsync(IEnumerable<int> seriesIds)
|
||||
{
|
||||
return await _context.SeriesMetadata
|
||||
.Where(sm => seriesIds.Contains(sm.SeriesId))
|
||||
.Include(sm => sm.CollectionTags)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ namespace API.Data
|
|||
public IFileRepository FileRepository => new FileRepository(_context);
|
||||
public IChapterRepository ChapterRepository => new ChapterRepository(_context, _mapper);
|
||||
public IReadingListRepository ReadingListRepository => new ReadingListRepository(_context, _mapper);
|
||||
public ISeriesMetadataRepository SeriesMetadataRepository => new SeriesMetadataRepository(_context);
|
||||
|
||||
/// <summary>
|
||||
/// Commits changes to the DB. Completes the open transaction.
|
||||
|
|
|
@ -8,6 +8,9 @@ namespace API.Entities
|
|||
{
|
||||
[Key]
|
||||
public ServerSettingKey Key { get; set; }
|
||||
/// <summary>
|
||||
/// The value of the Setting. Converter knows how to convert to the correct type
|
||||
/// </summary>
|
||||
public string Value { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using API.DTOs;
|
||||
using API.DTOs.CollectionTags;
|
||||
using API.DTOs.Reader;
|
||||
using API.DTOs.ReadingLists;
|
||||
using API.DTOs.Settings;
|
||||
|
|
|
@ -15,6 +15,7 @@ namespace API.Interfaces
|
|||
IFileRepository FileRepository { get; }
|
||||
IChapterRepository ChapterRepository { get; }
|
||||
IReadingListRepository ReadingListRepository { get; }
|
||||
ISeriesMetadataRepository SeriesMetadataRepository { get; }
|
||||
bool Commit();
|
||||
Task<bool> CommitAsync();
|
||||
bool HasChanges();
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using API.DTOs;
|
||||
using API.DTOs.CollectionTags;
|
||||
using API.Entities;
|
||||
|
||||
namespace API.Interfaces.Repositories
|
||||
{
|
||||
public interface ICollectionTagRepository
|
||||
{
|
||||
void Add(CollectionTag tag);
|
||||
void Remove(CollectionTag tag);
|
||||
Task<IEnumerable<CollectionTagDto>> GetAllTagDtosAsync();
|
||||
Task<IEnumerable<CollectionTagDto>> SearchTagDtosAsync(string searchQuery);
|
||||
|
|
9
API/Interfaces/Repositories/ISeriesMetadataRepository.cs
Normal file
9
API/Interfaces/Repositories/ISeriesMetadataRepository.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
using API.Entities;
|
||||
|
||||
namespace API.Interfaces.Repositories
|
||||
{
|
||||
public interface ISeriesMetadataRepository
|
||||
{
|
||||
void Update(SeriesMetadata seriesMetadata);
|
||||
}
|
||||
}
|
|
@ -54,5 +54,6 @@ namespace API.Interfaces.Repositories
|
|||
Task<PagedList<Series>> GetFullSeriesForLibraryIdAsync(int libraryId, UserParams userParams);
|
||||
Task<Series> GetFullSeriesForSeriesIdAsync(int seriesId);
|
||||
Task<Chunk> GetChunkInfo(int libraryId = 0);
|
||||
Task<IList<SeriesMetadata>> GetSeriesMetadataForIdsAsync(IEnumerable<int> seriesIds);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace API.Parser
|
|||
|
||||
public static readonly Regex FontSrcUrlRegex = new Regex(@"(src:url\(.{1})" + "([^\"']*)" + @"(.{1}\))",
|
||||
MatchOptions, RegexTimeout);
|
||||
public static readonly Regex CssImportUrlRegex = new Regex("(@import\\s[\"|'])(?<Filename>[\\w\\d/\\._-]+)([\"|'];?)",
|
||||
public static readonly Regex CssImportUrlRegex = new Regex("@import\\s([\"|']|url\\([\"|'])(?<Filename>[^'\"]+)[\"|']\\)?;",
|
||||
MatchOptions, RegexTimeout);
|
||||
|
||||
private static readonly string XmlRegexExtensions = @"\.xml";
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue