Collection Rework (#2830)
This commit is contained in:
parent
0dacc061f1
commit
deaaccb96a
93 changed files with 5413 additions and 1120 deletions
|
@ -1,16 +1,18 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Constants;
|
||||
using API.Data;
|
||||
using API.Data.Repositories;
|
||||
using API.DTOs.Collection;
|
||||
using API.DTOs.CollectionTags;
|
||||
using API.Entities.Metadata;
|
||||
using API.Entities;
|
||||
using API.Extensions;
|
||||
using API.Helpers.Builders;
|
||||
using API.Services;
|
||||
using API.Services.Plus;
|
||||
using Kavita.Common;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace API.Controllers;
|
||||
|
@ -38,50 +40,37 @@ public class CollectionController : BaseApiController
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a list of all collection tags on the server for the logged in user.
|
||||
/// Returns all Collection tags for a given User
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<IEnumerable<CollectionTagDto>>> GetAllTags()
|
||||
public async Task<ActionResult<IEnumerable<AppUserCollectionDto>>> GetAllTags(bool ownedOnly = false)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername());
|
||||
if (user == null) return Unauthorized();
|
||||
var isAdmin = await _unitOfWork.UserRepository.IsUserAdminAsync(user);
|
||||
if (isAdmin)
|
||||
{
|
||||
return Ok(await _unitOfWork.CollectionTagRepository.GetAllTagDtosAsync());
|
||||
}
|
||||
|
||||
return Ok(await _unitOfWork.CollectionTagRepository.GetAllPromotedTagDtosAsync(user.Id));
|
||||
return Ok(await _unitOfWork.CollectionTagRepository.GetCollectionDtosAsync(User.GetUserId(), !ownedOnly));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches against the collection tags on the DB and returns matches that meet the search criteria.
|
||||
/// <remarks>Search strings will be cleaned of certain fields, like %</remarks>
|
||||
/// Returns all collections that contain the Series for the user with the option to allow for promoted collections (non-user owned)
|
||||
/// </summary>
|
||||
/// <param name="queryString">Search term</param>
|
||||
/// <param name="seriesId"></param>
|
||||
/// <param name="ownedOnly"></param>
|
||||
/// <returns></returns>
|
||||
[Authorize(Policy = "RequireAdminRole")]
|
||||
[HttpGet("search")]
|
||||
public async Task<ActionResult<IEnumerable<CollectionTagDto>>> SearchTags(string? queryString)
|
||||
[HttpGet("all-series")]
|
||||
public async Task<ActionResult<IEnumerable<AppUserCollectionDto>>> GetCollectionsBySeries(int seriesId, bool ownedOnly = false)
|
||||
{
|
||||
queryString ??= string.Empty;
|
||||
queryString = queryString.Replace(@"%", string.Empty);
|
||||
if (queryString.Length == 0) return await GetAllTags();
|
||||
|
||||
return Ok(await _unitOfWork.CollectionTagRepository.SearchTagDtosAsync(queryString, User.GetUserId()));
|
||||
return Ok(await _unitOfWork.CollectionTagRepository.GetCollectionDtosBySeriesAsync(User.GetUserId(), seriesId, !ownedOnly));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a collection exists with the name
|
||||
/// </summary>
|
||||
/// <param name="name">If empty or null, will return true as that is invalid</param>
|
||||
/// <returns></returns>
|
||||
[Authorize(Policy = "RequireAdminRole")]
|
||||
[HttpGet("name-exists")]
|
||||
public async Task<ActionResult<bool>> DoesNameExists(string name)
|
||||
{
|
||||
return Ok(await _collectionService.TagExistsByName(name));
|
||||
return Ok(await _unitOfWork.CollectionTagRepository.CollectionExists(name, User.GetUserId()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -90,13 +79,15 @@ public class CollectionController : BaseApiController
|
|||
/// </summary>
|
||||
/// <param name="updatedTag"></param>
|
||||
/// <returns></returns>
|
||||
[Authorize(Policy = "RequireAdminRole")]
|
||||
[HttpPost("update")]
|
||||
public async Task<ActionResult> UpdateTag(CollectionTagDto updatedTag)
|
||||
public async Task<ActionResult> UpdateTag(AppUserCollectionDto updatedTag)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (await _collectionService.UpdateTag(updatedTag)) return Ok(await _localizationService.Translate(User.GetUserId(), "collection-updated-successfully"));
|
||||
if (await _collectionService.UpdateTag(updatedTag, User.GetUserId()))
|
||||
{
|
||||
return Ok(await _localizationService.Translate(User.GetUserId(), "collection-updated-successfully"));
|
||||
}
|
||||
}
|
||||
catch (KavitaException ex)
|
||||
{
|
||||
|
@ -107,18 +98,94 @@ public class CollectionController : BaseApiController
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a collection tag onto multiple Series. If tag id is 0, this will create a new tag.
|
||||
/// Promote/UnPromote multiple collections in one go. Will only update the authenticated user's collections and will only work if the user has promotion role
|
||||
/// </summary>
|
||||
/// <param name="dto"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("promote-multiple")]
|
||||
public async Task<ActionResult> PromoteMultipleCollections(PromoteCollectionsDto dto)
|
||||
{
|
||||
// This needs to take into account owner as I can select other users cards
|
||||
var collections = await _unitOfWork.CollectionTagRepository.GetCollectionsByIds(dto.CollectionIds);
|
||||
var userId = User.GetUserId();
|
||||
|
||||
if (!User.IsInRole(PolicyConstants.PromoteRole) && !User.IsInRole(PolicyConstants.AdminRole))
|
||||
{
|
||||
return BadRequest(await _localizationService.Translate(userId, "permission-denied"));
|
||||
}
|
||||
|
||||
foreach (var collection in collections)
|
||||
{
|
||||
if (collection.AppUserId != userId) continue;
|
||||
collection.Promoted = dto.Promoted;
|
||||
_unitOfWork.CollectionTagRepository.Update(collection);
|
||||
}
|
||||
|
||||
if (!_unitOfWork.HasChanges()) return Ok();
|
||||
await _unitOfWork.CommitAsync();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Promote/UnPromote multiple collections in one go
|
||||
/// </summary>
|
||||
/// <param name="dto"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("delete-multiple")]
|
||||
public async Task<ActionResult> DeleteMultipleCollections(PromoteCollectionsDto dto)
|
||||
{
|
||||
// This needs to take into account owner as I can select other users cards
|
||||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(User.GetUserId(), AppUserIncludes.Collections);
|
||||
if (user == null) return Unauthorized();
|
||||
user.Collections = user.Collections.Where(uc => !dto.CollectionIds.Contains(uc.Id)).ToList();
|
||||
_unitOfWork.UserRepository.Update(user);
|
||||
|
||||
|
||||
if (!_unitOfWork.HasChanges()) return Ok();
|
||||
await _unitOfWork.CommitAsync();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds multiple series to a collection. If tag id is 0, this will create a new tag.
|
||||
/// </summary>
|
||||
/// <param name="dto"></param>
|
||||
/// <returns></returns>
|
||||
[Authorize(Policy = "RequireAdminRole")]
|
||||
[HttpPost("update-for-series")]
|
||||
public async Task<ActionResult> AddToMultipleSeries(CollectionTagBulkAddDto dto)
|
||||
{
|
||||
// Create a new tag and save
|
||||
var tag = await _collectionService.GetTagOrCreate(dto.CollectionTagId, dto.CollectionTagTitle);
|
||||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(User.GetUserId(), AppUserIncludes.Collections);
|
||||
if (user == null) return Unauthorized();
|
||||
|
||||
if (await _collectionService.AddTagToSeries(tag, dto.SeriesIds)) return Ok();
|
||||
AppUserCollection? tag;
|
||||
if (dto.CollectionTagId == 0)
|
||||
{
|
||||
tag = new AppUserCollectionBuilder(dto.CollectionTagTitle).Build();
|
||||
user.Collections.Add(tag);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Validate tag doesn't exist
|
||||
tag = user.Collections.FirstOrDefault(t => t.Id == dto.CollectionTagId);
|
||||
}
|
||||
|
||||
if (tag == null)
|
||||
{
|
||||
return BadRequest(_localizationService.Translate(User.GetUserId(), "collection-doesnt-exists"));
|
||||
}
|
||||
|
||||
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdsAsync(dto.SeriesIds.ToList());
|
||||
foreach (var s in series)
|
||||
{
|
||||
if (tag.Items.Contains(s)) continue;
|
||||
tag.Items.Add(s);
|
||||
}
|
||||
_unitOfWork.UserRepository.Update(user);
|
||||
if (await _unitOfWork.CommitAsync()) return Ok();
|
||||
|
||||
return BadRequest(await _localizationService.Translate(User.GetUserId(), "generic-error"));
|
||||
}
|
||||
|
@ -128,13 +195,12 @@ public class CollectionController : BaseApiController
|
|||
/// </summary>
|
||||
/// <param name="updateSeriesForTagDto"></param>
|
||||
/// <returns></returns>
|
||||
[Authorize(Policy = "RequireAdminRole")]
|
||||
[HttpPost("update-series")]
|
||||
public async Task<ActionResult> RemoveTagFromMultipleSeries(UpdateSeriesForTagDto updateSeriesForTagDto)
|
||||
{
|
||||
try
|
||||
{
|
||||
var tag = await _unitOfWork.CollectionTagRepository.GetTagAsync(updateSeriesForTagDto.Tag.Id, CollectionTagIncludes.SeriesMetadata);
|
||||
var tag = await _unitOfWork.CollectionTagRepository.GetCollectionAsync(updateSeriesForTagDto.Tag.Id, CollectionIncludes.Series);
|
||||
if (tag == null) return BadRequest(await _localizationService.Translate(User.GetUserId(), "collection-doesnt-exist"));
|
||||
|
||||
if (await _collectionService.RemoveTagFromSeries(tag, updateSeriesForTagDto.SeriesIdsToRemove))
|
||||
|
@ -149,24 +215,28 @@ public class CollectionController : BaseApiController
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the collection tag from all Series it was attached to
|
||||
/// Removes the collection tag from the user
|
||||
/// </summary>
|
||||
/// <param name="tagId"></param>
|
||||
/// <returns></returns>
|
||||
[Authorize(Policy = "RequireAdminRole")]
|
||||
[HttpDelete]
|
||||
public async Task<ActionResult> DeleteTag(int tagId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var tag = await _unitOfWork.CollectionTagRepository.GetTagAsync(tagId, CollectionTagIncludes.SeriesMetadata);
|
||||
if (tag == null) return BadRequest(await _localizationService.Translate(User.GetUserId(), "collection-doesnt-exist"));
|
||||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(User.GetUserId(), AppUserIncludes.Collections);
|
||||
if (user == null) return Unauthorized();
|
||||
if (user.Collections.All(c => c.Id != tagId))
|
||||
return BadRequest(await _localizationService.Translate(user.Id, "access-denied"));
|
||||
|
||||
if (await _collectionService.DeleteTag(tag))
|
||||
if (await _collectionService.DeleteTag(tagId, user))
|
||||
{
|
||||
return Ok(await _localizationService.Translate(User.GetUserId(), "collection-deleted"));
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
await _unitOfWork.RollbackAsync();
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue